WinGet Guides

How to Pin App Versions with WinGet for Stable Environments

If you run Windows at scale or you simply want a rock‑solid workstation, keeping apps on a specific version is critical. The Windows Package Manager — better known as winget — makes this possible through versioned installs and package pinning. In this guide you’ll learn, step by step, how to pin app versions with winget to create stable, repeatable environments. You’ll get practical examples, ready‑to‑use commands, and complete scripts you can drop into automation pipelines or Intune.

You’ll learn how to:

  • Install a specific package version with winget
  • Pin that version so routine upgrades won’t break your baseline
  • Export/import your environment and use YAML WinGet Configuration for declarative version locks
  • Troubleshoot common pinning and installation errors
  • Automate the entire flow for fresh installs, bulk updates, CI/CD, and Intune deployments
Contents show

Overview of the Use Case

What does “pin app versions with winget” mean?

Pinning tells winget to hold a package at a chosen version and ignore upgrades unless you explicitly allow them. That way, routine winget upgrade commands won’t unexpectedly bump versions and break dependencies, scripts, or compliance requirements.

In plain language: pinning is a version lock. Once you pin an app, winget won’t upgrade it during bulk updates unless you override the pin.

When do you need version pinning?

  • Fresh OS or device enrollments: Install a known‑good baseline of apps at fixed versions.
  • Staged rollouts: QA pins a version; production devices follow after testing.
  • CI/CD runners and build agents: Lock toolchains (e.g., Git, Node.js, Python) to prevent “works yesterday, breaks today.”
  • Highly regulated or validated environments: Maintain documented versions for audits and change control.
  • Labs, VDI, or classroom machines: Keep classroom software consistent across devices and semesters.
  • Air‑gapped/offline or bandwidth‑constrained sites: Precache installers and keep versions stable to reduce churn.

Quick Reference Table

Command Purpose Example Output
winget –info Show winget version and environment details Windows Package Manager v1.6.x; Logs: C:\Users…\DiagOutputDir
winget search Find a package ID and source Name Id Version Source; Git Git.Git 2.44.0 winget
winget show –id Show details, including available versions Versions: 2.44.0; 2.43.0; 2.42.0
winget install –id –version Install a specific version Successfully installed
winget pin add –id –version Pin a package to a version Added pin: Git.Git = 2.44.0
winget pin list List all pinned packages Name Id Pinned Version; Git Git.Git 2.44.0
winget pin remove –id Remove a pin Removed pin: Git.Git
winget upgrade List available upgrades (excludes pinned) Name Id Version Available Source
winget upgrade –all Upgrade everything (excludes pinned) Upgraded 5 of 5 packages (0 pinned skipped)
winget upgrade –include-pinned –id Override pin for one upgrade Upgraded Git.Git from 2.44.0 to 2.45.1
winget source reset –force Fix source index issues Successfully reset all sources
winget export -o packages.json –include-versions Export installed packages with versions Wrote packages.json
See also  Export/Import Your App List with WinGet (and Keep It Clean)

Note: Output varies by version and package; the samples are indicative.

Key Concepts and Prerequisites

What you need

  • A recent version of the Windows Package Manager (winget). Package pinning requires a modern release (1.6+ recommended).
  • PowerShell 5.1+ or PowerShell 7+ for scripting and automation.
  • Administrator rights for system‑level installs (some packages require elevation).
  • Network access to your sources (e.g., winget community repo, Microsoft Store, or your internal source).

How to check your winget version

Run:

winget –info

Look for:

  • Windows Package Manager vX.Y.Z
  • Logs path (helpful for troubleshooting)
  • Sources (e.g., winget, msstore)

If winget says the pin command is unknown, update the App Installer from the Microsoft Store or install the latest winget package from Microsoft and retry.

Sources and scope considerations

  • winget source “winget” (Community): Most EXE/MSI packages, supports versioned installs and pinning.
  • winget source “msstore” (Microsoft Store): Apps often auto‑update via Store; version pinning is limited or not applicable.
  • Scope matters: Use –scope machine for all‑users installs and –scope user for per‑user installs, depending on the package.

Where pins live

Pins are stored per user on the local machine by winget. They affect winget’s behavior for that user unless you run winget as another user or in a system context. In shared or multi‑user systems, set pins in the context that will perform upgrades.

Step-by-Step Guide

1) Identify the correct package ID

Use search to find the canonical package ID:

winget search git

If there are many results or name collisions, filter and use exact matches:

winget search –source winget –name Git
winget show –id Git.Git

From winget show, confirm architecture, installer type, and available versions.

Tips:

  • Prefer –id with -e/–exact to avoid ambiguity.
  • Check architecture: add –architecture x64 if needed.
  • If multiple sources exist, specify –source winget.

2) Install a specific version (versioned install)

To install Git 2.44.0 silently for all users:

winget install –id Git.Git –version 2.44.0 –scope machine –silent –source winget –accept-package-agreements –accept-source-agreements

Notes:

  • –silent uses the manifest’s silent switches (recommended for automation).

  • Some packages accept –override to pass custom installer switches, e.g.:

    winget install –id Some.App –version 1.2.3 –override “/quiet /norestart”

  • If the version is not found, verify it is listed in winget show and that you’re targeting the correct source and architecture.

3) Pin the installed version

Once the app is installed, pin it to the version you want:

winget pin add –id Git.Git –version 2.44.0

Verify:

winget pin list

You should see the package and the pinned version.

Notes:

  • Pinning without specifying a version typically pins the currently installed version; specifying the version is explicit and safer.

  • Use -e/–exact to avoid pinning the wrong package when names are similar:

    winget pin add –id Git.Git -e –version 2.44.0

4) Run upgrades safely (pinned packages are skipped)

Routine upgrades will now skip pinned packages:

winget upgrade

Or bulk upgrade:

winget upgrade –all

Pinned packages will be listed as skipped or simply not shown in the upgrade list, depending on your winget version.

5) Temporarily override the pin for a one‑time upgrade

You have two main options:

  • Override pin in a one‑off command:

    winget upgrade –id Git.Git –include-pinned

  • Remove pin, upgrade, re‑pin:

    winget pin remove –id Git.Git
    winget upgrade –id Git.Git
    winget pin add –id Git.Git –version

6) Bulk pinning for a baseline

Pin multiple packages at once with a simple PowerShell loop:

$baseline = @{
“Git.Git” = “2.44.0”
“Microsoft.VisualStudioCode” = “1.91.0”
“Python.Python.3.11” = “3.11.9”
“OpenJS.NodeJS.LTS” = “18.20.3”
}

foreach ($id in $baseline.Keys) {
$ver = $baseline[$id]

Install exact version if missing

if (-not (winget list --id $id | Select-String $ver)) {
    winget install --id $id --version $ver --source winget --silent --accept-package-agreements --accept-source-agreements
}
# Pin the version
winget pin add --id $id --version $ver

}
winget pin list

7) Export your installed packages (with versions)

Create a versioned inventory:

winget export -o .\packages.json –include-versions

Sample packages.json (truncated):

{
“CreationDate”: “2025-10-10T10:05:12.345Z”,
“Sources”: [
{
“Packages”: [
{ “Id”: “Git.Git”, “Version”: “2.44.0” },
{ “Id”: “Microsoft.VisualStudioCode”, “Version”: “1.91.0” }
],
“SourceDetails”: { “Argument”: “https://cdn.winget.microsoft.com/cache“, “Identifier”: “Microsoft.Winget.Source_8wekyb3d8bbwe” }
}
]
}

See also  winget configure Explained: Rebuild Any PC from a Single YAML

Import on another machine:

winget import -i .\packages.json –accept-source-agreements –accept-package-agreements

Note: Import installs versions but does not itself create pins. After import, run pin commands or use Configuration (next step).

8) Use YAML WinGet Configuration to declaratively lock versions

WinGet Configuration lets you define a desired state in a YAML file and apply it repeatably. It’s a powerful complement to pinning.

Sample config.yaml:

WinGet Configuration: lock specific app versions

Apply with: winget configure -f .\config.yaml –accept-configuration-agreements

properties:
configurationVersion: 0.2.0

resources:

  • resource: Microsoft.WinGet.DSC/WinGetPackage
    id: Git
    directives:
    description: Install Git 2.44.0 silently (machine scope)
    settings:
    id: Git.Git
    version: 2.44.0
    source: winget
    scope: machine
    installMode: silent

  • resource: Microsoft.WinGet.DSC/WinGetPackage
    id: VSCode
    settings:
    id: Microsoft.VisualStudioCode
    version: 1.91.0
    source: winget
    installMode: silent

  • resource: Microsoft.WinGet.DSC/WinGetPackage
    id: Python311
    settings:
    id: Python.Python.3.11
    version: 3.11.9
    source: winget
    scope: machine
    installMode: silent

Apply it:

winget configure -f .\config.yaml –accept-configuration-agreements

Notes:

  • Configuration enforces the specified versions when you apply it.
  • It does not automatically create pins, but it’s a declarative way to maintain version locks; rerun the configuration to reconcile drift.
  • Keep your YAML under version control and code review changes before rolling to production.

9) Optional: Prefetch installers for offline or slow networks

If you need offline installs, pre‑download installers to a cache share:

winget download –id Git.Git –version 2.44.0 -o \fileserver\cache\git-2.44.0

Repeat for each package/version. You can then install directly from the cached installers using their native silent switches, or set up an internal winget source. For fully offline winget usage, consider hosting a private REST source and pushing manifests with wingetcreate.

10) Housekeeping: list, remove, and reset pins

  • List pins:

    winget pin list

  • Remove a specific pin:

    winget pin remove –id Git.Git

  • Reset all pins (use cautiously):

    winget pin reset

Troubleshooting

Common issues and exact fixes

  • Problem: “Installer hash mismatch”

    • Cause: Manifest updated, cached installer changed, or corrupted download.

    • Fix:

      winget source update
      winget upgrade –id –version –source winget

      Clear cache if needed (path from winget –info logs) or re‑download:

      winget download –id –version -o C:\Temp\WingetCache\\

  • Problem: “Multiple installers found” or ambiguous package/name

    • Fix: Use exact ID, architecture, and source:

      winget install –id -e –architecture x64 –source winget
      winget show –id -e

  • Problem: “No applicable installer found” for the requested version

    • Fix: Confirm the version exists for your architecture/scope:

      winget show –id | more

      Try a different version or architecture:

      winget install –id –version –architecture x64

  • Problem: “Source index is corrupt” or update failures

    • Fix:

      winget source reset –force
      winget source update

  • Problem: Pinned package still upgrades

    • Fix: Ensure you didn’t pass –include-pinned. Verify pin with:

      winget pin list

      If the ID differs (Name vs Id mismatch), re‑pin with exact ID:

      winget pin add –id <Exact.Id> -e –version

  • Problem: Admin/UAC prompts block automation

    • Fix: Run the shell elevated or launch an elevated session:

      Start-Process PowerShell -Verb RunAs

      In automation, run as SYSTEM or use a scheduled task set to “Run with highest privileges.”

  • Problem: Microsoft Store apps ignore pins or versions

    • Fix: Store apps typically auto‑update via Store. Prefer winget “winget” source packages for pinning, or use policy to control Store updates.

Automation Tips

Automate installs and pins with PowerShell

This script ensures specific versions are installed and pinned, and logs everything:

Baseline versions

$baseline = @{
“Git.Git” = “2.44.0”
“Microsoft.VisualStudioCode” = “1.91.0”
“Python.Python.3.11” = “3.11.9”
}

$log = “C:\ProgramData\WingetBaseline\baseline-$(Get-Date -Format yyyyMMdd-HHmmss).log”
New-Item -ItemType Directory -Force -Path (Split-Path $log) | Out-Null

“Starting baseline at $(Get-Date)” | Tee-Object -FilePath $log -Append

foreach ($id in $baseline.Keys) {
$ver = $baseline[$id]
“Processing $id $ver” | Tee-Object -FilePath $log -Append

$installed = winget list --id $id | Select-String $ver
if (-not $installed) {
    winget install --id $id --version $ver --source winget --silent --accept-package-agreements --accept-source-agreements `
      | Tee-Object -FilePath $log -Append
}

winget pin add --id $id --version $ver `
  | Tee-Object -FilePath $log -Append

}

“Pins:” | Tee-Object -FilePath $log -Append
winget pin list | Tee-Object -FilePath $log -Append

“Completed baseline at $(Get-Date)” | Tee-Object -FilePath $log -Append

Schedule it with Task Scheduler (system context, at startup)

Create a scheduled task that runs as SYSTEM with highest privileges:

$action = New-ScheduledTaskAction -Execute “powershell.exe” -Argument “-NoProfile -ExecutionPolicy Bypass -File C:\Scripts\Apply-Baseline.ps1”
$trigger = New-ScheduledTaskTrigger -AtStartup
$principal = New-ScheduledTaskPrincipal -UserId “SYSTEM” -RunLevel Highest
Register-ScheduledTask -TaskName “Winget Baseline” -Action $action -Trigger $trigger -Principal $principal

See also  Private WinGet Source: How to Host Your Own Repository

Use in Intune

  • Option 1: Use “Microsoft Store app (new)” and/or WinGet integration for app deployment, then run a device or user PowerShell script to pin versions:

    • Deploy your apps (winget source packages where possible).
    • Assign a PowerShell script with the pin commands to target groups.
  • Option 2: Use WinGet Configuration:

    • Store config.yaml in a secure repo.

    • Deploy a script that downloads config.yaml locally and runs:

      winget configure -f C:\Path\config.yaml –accept-configuration-agreements

CI/CD (build agents, self‑hosted runners)

  • Pre‑stage your toolchain via a versioned install script and pin it.
  • Run with –silent, –accept-package-agreements, –accept-source-agreements to prevent prompts.
  • Cache installers with winget download in the pipeline cache to speed up builds.
  • If the agent image is ephemeral, run the script at provision time.

Offline or bandwidth‑constrained sites

  • Pre‑download installers with winget download to a central share.
  • For full offline repeatability, host an internal winget REST source and publish vetted manifests/versions.
  • Pin installed versions to avoid unexpected updates when the network link is restored.

Best Practices

  • Prefer IDs over names

    • Use –id with -e/–exact to target the correct package every time.
  • Separate install and pin steps

    • Install the exact version you want, then pin it. This makes your intent explicit.
  • Keep a versioned baseline manifest

    • Maintain a baseline (JSON via winget export or a YAML WinGet Configuration). Store in version control, review changes, and tag releases.
  • Use silent installs in automation

    • Favor –silent and provide –accept-package-agreements and –accept-source-agreements to avoid prompts.
  • Verify architecture and scope

    • Always specify –architecture and –scope when repeatability matters across machines.
  • Don’t pin everything forever

    • Pin only what you must keep stable. Regularly update and re‑pin to pick up security fixes.
  • Test pins and upgrades in a staging ring

    • Trial upgrades, then promote versions to production by updating your pins/baseline.
  • Document exceptions

    • Microsoft Store apps often don’t honor pinning. Use policies or replace them with winget community packages where feasible.
  • Monitor logs

    • Check the log path from winget –info for install/pinning failures, especially when running at scale.

Conclusion

Pinning app versions with winget is a simple, robust way to build and maintain stable Windows environments. Install the exact version you need, pin it, and run routine winget upgrades with confidence knowing your baselines won’t drift. For larger fleets, pair pins with a YAML WinGet Configuration file and automation scripts to make your environment reproducible, testable, and auditable. Try the commands in this guide on a test machine and you’ll see how quickly you can lock down versions without giving up the power and convenience of the Windows Package Manager.

FAQ Section

What’s the difference between installing a version and pinning it?

A versioned install sets a specific version now, but future winget upgrade commands may move it forward. Pinning tells winget to hold that package at the pinned version and skip it during upgrades unless you explicitly override with –include-pinned.

Can I pin Microsoft Store apps?

Generally, no. Store apps update through the Microsoft Store service and often don’t support versioned installs or winget pin behavior. Prefer packages from the “winget” community source if you need pinning, or control Store updates via policy.

How do I find which versions are available to install?

Use:

winget show –id

Look for the “Versions” section. Not all packages maintain historical versions; winget can only install versions that exist in the source’s manifests for your architecture.

Where are pins stored, and are they per user?

Pins are stored locally by winget and are applied per user context. If you run winget as another user or as SYSTEM, that context has its own pins. For shared devices, apply pins in the context that performs upgrades (e.g., SYSTEM via scheduled task).

How do I update a pinned package to a newer version?

Either temporarily override the pin:

winget upgrade –id –include-pinned

Or remove and recreate the pin:

winget pin remove –id
winget upgrade –id
winget pin add –id –version

About the author

Jonathan Dudamel

Jonathan Dudamel

I'm Jonathan Dudamel, an experienced IT specialist and network engineer passionate about all things Windows. I have deep expertise in Microsoft project management, virtualization (VMware ESXi and Hyper-V), and Microsoft’s hybrid platform. I'm also skilled with Microsoft O365, Azure ADDS, and Windows Server environments from 2003 through 2022.

My strengths include Microsoft network infrastructure, VMware platforms, CMMS, ERP systems, and server administration (2016/2022).