# Notes on winget

**Date:** 2026-09-30  
**Author:** Kees C. Bakker  
**Categories:** Automation, Installation Notes, Windows  
**Original:** https://keestalkstech.com/notes-on-winget/

![Notes on winget](https://keestalkstech.com/wp-content/uploads/2026/09/jeshoots-com-Ym1qmOgxoyg-unsplash.jpg)

---

With the [Windows Package Manager (winget)](https://learn.microsoft.com/en-us/windows/package-manager/winget/), it is easy to install software from the command line. This makes your installs scriptable and thus repeatable. This article is the WinGet counterpart to my previous [Notes on Chocolatey](https://keestalkstech.com/notes-on-chocolatey/): it shows how to reproduce a machine's installation instructions and update your packages regularly.

## Install WinGet

WinGet is supported on Windows 11, Windows 10 version 1809 (build 17763) and later, and Windows Server 2025. On Windows desktop, it is delivered as part of *App Installer* through the Microsoft Store. On a normal, up-to-date Windows 10 or Windows 11 installation, `winget` is usually already available. Check it from PowerShell:

```powershell
winget --version
winget source update
```

If `winget` is missing or broken, update *App Installer* from the Microsoft Store. The PowerShell repair command can also install the latest WinGet client:

```powershell
Repair-WinGetPackageManager -Latest -Force
```

## Listing installation instructions

Let's generate installation instructions from the applications on a machine. WinGet exports the packages it recognizes to JSON, which we can turn into individual install commands:

```
& {
    $exportPath = Join-Path $env:TEMP "winget-packages-$([guid]::NewGuid()).json"

    try {
      $exportOutput = & winget export --output $exportPath --disable-interactivity 2>&1
      $exitCode = $LASTEXITCODE

      if ($exitCode -ne 0) {
        throw "WinGet export failed (exit code ${exitCode}):`n$($exportOutput -join [Environment]::NewLine)"
      }

      $export = Get-Content -LiteralPath $exportPath -Raw |
        ConvertFrom-Json -ErrorAction Stop

      foreach ($source in ($export.Sources | Sort-Object { $_.SourceDetails.Name })) {
        $sourceName = $source.SourceDetails.Name.Replace("'", "''")

        foreach ($package in ($source.Packages | Sort-Object PackageIdentifier)) {
          $packageId = $package.PackageIdentifier.Replace("'", "''")
          'winget install --id ''{0}'' --exact --source ''{1}''' -f $packageId, $sourceName
        }
      }
    }
    finally {
      Remove-Item -LiteralPath $exportPath -Force -ErrorAction SilentlyContinue
    }
  }
```

This prints commands you can copy into an installation script; it does not install anything. Each command includes the package's source, so Microsoft Store packages keep `--source 'msstore'`.

The generated `packages.json` file is useful when setting up a new machine too. Instead of running the individual commands, import it with:

```powershell
winget import --import-file packages.json
```

Unlike Chocolatey, WinGet uses package IDs from its own sources. Search for an application and inspect its exact ID before putting it in a script:

```powershell
winget search git --source winget
winget show --id Git.Git --exact --source winget
```

## Upgrade Task

This creates a weekly Windows Task Scheduler task. When it runs every Monday at 9:30 AM, it requests elevation once and runs all package upgrades in an elevated PowerShell session:

```
& {
    $taskName = 'UpgradeWingetPackages'
    $scriptPath = Join-Path $env:LOCALAPPDATA 'Upgrade-WingetPackages.ps1'

    $upgradeScript = @'
winget source update
if ($LASTEXITCODE -ne 0) {
    throw "WinGet source update failed (exit code $LASTEXITCODE)."
}

winget upgrade --all `
    --silent `
    --disable-interactivity `
    --accept-source-agreements `
    --accept-package-agreements
if ($LASTEXITCODE -ne 0) {
    throw "WinGet upgrade failed (exit code $LASTEXITCODE)."
}
'@

    Set-Content -LiteralPath $scriptPath -Value $upgradeScript -Encoding UTF8

    $action = New-ScheduledTaskAction `
      -Execute 'powershell.exe' `
      -Argument "-NoProfile -ExecutionPolicy Bypass -File `"$scriptPath`""

    $trigger = New-ScheduledTaskTrigger `
      -Weekly `
      -DaysOfWeek Monday `
      -At '9:30AM'

    $settings = New-ScheduledTaskSettingsSet `
      -AllowStartIfOnBatteries `
      -DontStopIfGoingOnBatteries `
      -StartWhenAvailable

    $principal = New-ScheduledTaskPrincipal `
      -UserId ([Security.Principal.WindowsIdentity]::GetCurrent().Name) `
      -LogonType Interactive `
      -RunLevel Limited

    $existingTask = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
    if ($existingTask) {
      Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
    }

    Register-ScheduledTask `
      -TaskName $taskName `
      -Description 'Upgrade all WinGet packages' `
      -Action $action `
      -Trigger $trigger `
      -Settings $settings `
      -Principal $principal
  }
```

The task runs only while you are logged in so Windows can show the UAC prompt. It elevates the full WinGet process, so use it only with package sources and applications you trust. The task replaces an existing task with the same name. Some installers may still require input or a reboot.

## Make Upgrade Task Compatible with MakeMeAdmin

At Wehkamp we use [Make Me Admin](https://github.com/pseymour/MakeMeAdmin). You need to claim admin rights before running administrative tasks. The regular UAC prompt does not let you claim those rights first.

This script updates the task action to launch Make Me Admin, wait for you to request temporary admin rights, and then start the elevated WinGet upgrade:

```
& {
    $taskName = 'UpgradeWingetPackages'
    $makeMeAdminDir = Join-Path $env:ProgramFiles 'Make Me Admin'
    $makeMeAdminPath = Join-Path $makeMeAdminDir 'MakeMeAdminUI.exe'
    $scriptPath = Join-Path $env:LOCALAPPDATA 'Upgrade-WingetPackages.ps1'

    if (-not (Test-Path -LiteralPath $makeMeAdminPath)) {
      throw "Make Me Admin was not found at '$makeMeAdminPath'."
    }

    if (-not (Test-Path -LiteralPath $scriptPath)) {
      throw "The WinGet upgrade script was not found at '$scriptPath'. Run upgrade-task.ps1 first."
    }

    $task = Get-ScheduledTask -TaskName $taskName -ErrorAction Stop
    $quotedMakeMeAdminDir = $makeMeAdminDir.Replace("'", "''")
    $quotedScriptPath = $scriptPath.Replace("'", "''")

    $launcher = @"
Set-Location -LiteralPath '$quotedMakeMeAdminDir'
& (Join-Path '$quotedMakeMeAdminDir' 'MakeMeAdminUI.exe')
Read-Host 'Request temporary administrator access, then press Enter to continue'
`$arguments = '-NoProfile -ExecutionPolicy Bypass -File `"$quotedScriptPath`"'
`$process = Start-Process -FilePath 'powershell.exe' -ArgumentList `$arguments -Verb RunAs -Wait -PassThru
exit `$process.ExitCode
"@
    $encodedLauncher = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($launcher))

    $action = New-ScheduledTaskAction `
      -Execute 'powershell.exe' `
      -Argument "-NoProfile -EncodedCommand $encodedLauncher"

    Unregister-ScheduledTask -TaskName $taskName -Confirm:$false

    Register-ScheduledTask `
      -TaskName $taskName `
      -Description 'Upgrade all WinGet packages with Make Me Admin' `
      -Action $action `
      -Trigger $task.Triggers `
      -Settings $task.Settings `
      -Principal $task.Principal
  }
```

The next time the task runs, claim temporary admin rights in Make Me Admin before continuing.

## Skip an upgrade

WinGet supports [pins](https://learn.microsoft.com/en-us/windows/package-manager/winget/pinning) for packages that should not be upgraded automatically. For example:

```powershell
winget pin add --id Git.Git
winget pin list
winget pin remove --id Git.Git
```

A package with a non-blocking pin can still be included explicitly when upgrading everything:

```powershell
winget upgrade --all --include-pinned
```

## My arbitrary list of installed packages

I use my articles as notes to my future self. When I get a new machine, I usually install the following packages. These are the WinGet IDs corresponding to the tools from my Chocolatey article:

```
& {
    # development tools
    winget install --id Amazon.AWSCLI --exact
    winget install --id Amazon.Kiro --exact
    ## Node Version Manager for Windows
    winget install --id CoreyButler.NVMforWindows --exact
    winget install --id Git.Git --exact
    winget install --id GitHub.cli --exact
    winget install --id GnuPG.Gpg4win --exact
    winget install --id Google.ContainerTools.Skaffold --exact
    winget install --id jqlang.jq --exact
    winget install --id Microsoft.VisualStudioCode --exact
    winget install --id MikeFarah.yq --exact
    winget install --id ScooterSoftware.BeyondCompare.5 --exact
    winget install --id SST.opencode --exact
    winget install --id SUSE.RancherDesktop --exact
    winget install --id wagoodman.dive --exact
    winget install --id xhcoding.sshpass-win32 --exact

    # image and video tools
    winget install --id dotPDN.PaintDotNet --exact
    winget install --id Gyan.FFmpeg --exact
    winget install --id Inkscape.Inkscape --exact
    ## draw.io
    winget install --id JGraph.Draw --exact
    winget install --id NickeManarin.ScreenToGif --exact
    winget install --id VideoLAN.VLC --exact
    winget install --id yt-dlp.FFmpeg --exact
    winget install --id yt-dlp.yt-dlp --exact

    # tools
    winget install --id Brave.Brave --exact
    winget install --id CodeSector.TeraCopy --exact
    winget install --id JAMSoftware.TreeSize.Free --exact
    winget install --id Logitech.Options --exact
    winget install --id Microsoft.Coreutils --exact
    winget install --id Microsoft.PowerShell --exact
    winget install --id Microsoft.PowerToys --exact
    winget install --id RavensburgerAG.tiptoiManager --exact
    ## OpenSSL command-line toolkit
    winget install --id ShiningLight.OpenSSL.Light --exact
    winget install --id SparkLabs.Viscosity --exact
    winget install --id StablyAI.Orca --exact

    # Microsoft Store apps
    ## Dynamic Theme: daily Bing and Windows Spotlight wallpapers
    winget install --id 9NBLGGH1ZBKW --exact --source msstore
    ## WhatsApp
    winget install --id 9NKSQGP7F2NH --exact --source msstore
    ## ChatGPT
    winget install --id 9PLM9XGG6VKS --exact --source msstore
    ## Spotify
    winget install --id 9NCBCSZSJRSB --exact --source msstore
  }
```

Beyond Compare has separate WinGet packages for versions 4 and 5. I used version 5 above. Package availability can change; use `winget search` to check before installing.

You can install the packages without repeating every command by saving the list as a JSON export and importing it later. For a script, however, explicit IDs make the intended software clear and `--exact` avoids an ambiguous search result.

## Post installation

After installing the tools, you might want to install a version of Node.js and some global dependencies.

These are my go-to global dependencies:

```
& {
    # download and use Node.js 24 LTS
    nvm install 24
    nvm use 24
  }

  & {
    # check if your package file is still up to date
    npm install --global npm-check-updates
  }
```

Enjoy!

The code is on GitHub, so check it out: [18.winget](https://github.com/KeesCBakker/keestalkstech-code-gallery/tree/main/18.winget).
