Winget Package Management with PowerShell
I've been sharing my thoughts and code for managing my work environment with PowerShell. It's not that I am paranoid about security, but I like having everything up-to-date. The last area I need to tackle is package management. These are packages for tools like git, web browsers, and email clients. I've been using winget
for several years. It is not without its problems and is not the only package manager available. I'm betting more than a few readers use Chocolatey. Even though my solution is based on winget
, I'm hoping that you can adapt it to your package manager of choice.
Microsoft.Winget.Client
The first step is to make sure I can use PowerShell. Without going into its sordid and tortured history, winget
was born as a command-line tool. I can use the native winget
command in PowerShell, but it writes text to the pipeline. Parsing text to manage packages is not ideal. Fortunately, Microsoft has released a PowerShell module for winget
. The module is called Microsoft.Winget.Client
. You can install it from the PowerShell Gallery.
Install-PSResource -Name Microsoft.Winget.Client
The module provides PowerShell commands around a lot of winget
functionality. This article isn't about the module, so I won't go into detail, and I think the module needs work. But for my management needs, it is sufficient.
Don't forget that you can use Get-Command
to view all the module commands.
Get-Command -Module Microsoft.Winget.Client
My management solution is based onGet-WingetPackage
which will retrieve installed package information.
PS C:\> Get-WingetPackage Discord
Name Id Version Available Source
---- -- ------- --------- ------
Discord Discord.Discord 1.0.9005 1.0.9181 winget
You can see that the Available
property indicates an update is available. When working with something new, don't assume the default output is all there is. When I run this in PowerShell 7, the Version
and Available
properties are displayed italicized. This is a hint that these are custom table headers and not actual property name. There may be more to the object than what is displayed.
PS C:\> Get-WingetPackage Discord | Select-Object *
InstalledVersion : 1.0.9005
Name : Discord
Id : Discord.Discord
IsUpdateAvailable : True
Source : winget
AvailableVersions : {1.0.9181, 1.0.9180, 1.0.9179, 1.0.9178…}
I tell that the IsUpdateAvailable
property will be very useful.
PS C:\> Get-WingetPackage | Where IsUpdateAvailable |
Select Name,InstalledVersion,@{Name="Update";Expression={$_.availableVersions[0]}},
source
Name InstalledVersion Update Source
---- ---------------- ------ ------
Microsoft Visual C++ 2005 Redistributable (x64) 8.0.59192 8.0.61000 winget
ESET Endpoint Security 11.1.2053.2 12.0.2045.0 winget
Microsoft Edge 132.0.2957.140 133.0.3065.51 winget
MozBackup Unknown 1.5.1 winget
SpeedFan Unknown 4.52 winget
Microsoft Visual C++ 2012 Redistributable (x86) 11.0.50727.1 11.0.61030.0 winget
DYMO Connect 1.4.6.37 1.4.7.48 winget
Python Launcher < 3.12.0 3.12.0 winget
Microsoft .NET SDK 8.0 8.0.206 8.0.405 winget
Camtasia 22.5.2.44147 24.1.5.6542 winget
Discord 1.0.9005 1.0.9181 winget
Spotify 1.2.12.902.g192… 1.2.56.502.g… winget
Obsidian 1.6.7 1.8.4 winget
VSCodium 1.96.4.25026 1.97.0.25037 winget
Microsoft Visual Studio Code 1.96.2 1.97.0 winget
Python 3.10 3.10.3 3.10.11 winget
This means I could run a command like this to update everything:
Get-WingetPackage | Where IsUpdateAvailable | Update-WingetPackage
But I rarely want to take such a wide approach. I prefer more granular control to my updates.
There is also a major bug with some of these commands. The commands to install, update, and uninstall, are supposed to support -WhatIf
, but they do not. If I run:
Get-WingetPackage obsidian | Update-WinGetPackage -WhatIf
The package will be downloaded and updated. I can work around this, but you need to be aware of it. This bug has been reported.
Exclusions
Even though winget
shows many potential updates, I know from experience that I don't necessarily want to update everything. Some packages, like MozBackup
are legacy packages that are no longer being maintained. I can install the package, but there is unlikely to ever be an update. I have a few of these packages installed.
PS C:\> Get-WingetPackage | Where InstalledVersion -eq 'unknown' |
Select Name,InstalledVersion,@{Name="Update";Expression={$_.availableVersions[0]}},
IsUpdateAvailable,Source | Format-Table
Name InstalledVersion Update IsUpdateAvailable Source
---- ---------------- ------ ----------------- ------
Microsoft SQL Server 2019 (64-bit) Unknown False
KDiff3 (remove only) Unknown False
MozBackup Unknown 1.5.1 True winget
SpeedFan Unknown 4.52 True winget
I also have packages like Discord that can update themselves. Finally, there are packages that I know from experience never properly update like Microsoft SDKs or runtimes. This is another ongoing winget
problem that I simply live with.
What I've done is create an exclusion text file.
#wingetexclude.txt
#Winget packages to exclude from UpdateWingetPackages.ps1
#This file must be in the same directory as the update script
#Package names will be used in a regex pattern
MuseScore
Discord
Camtasia
Spotify
PowerShell
SDK
Redistributable
Slack
Code
The entries are part of package names that I want to exclude from my update automation. As you can see from the note, the names are using in a wildcard regular expression pattern.