winget: new Windows package management

article #1504, updated 2 days ago

It’s built into most OEM installs of Windows 10 and 11, and can often be installed. On server builds it’s touch and go.

To see if you have it, try winget list from CMD or Powershell.

One good way to test it, is to install Microsoft .NET framework (SDK) 6, thus, from administrative Powershell:

winget install --id Microsoft.DotNet.Runtime.6 --silent --accept-source-agreements

I learned just now that if you add other seemingly valuable options to the one above, e.g., --scope machine, at least while running as SYSTEM, it will fail citing package not found. So you’ll want to test carefully.

Here’s one proven just now for 7zip (there’s a “search” option in winget to get the ID):

winget install --exact --id 7zip.7zip --accept-package-agreements --silent --scope machine

Here’s one for Google Chrome, needs a bit of extra:

winget.exe install --exact --id Google.Chrome --silent --accept-package-agreements --accept-source-agreements --scope machine

And here’s a way to upgrade everything Winget can upgrade. There are some systems to not use this on, e.g., anything with some Autodesk products:

winget upgrade --all --include-unknown

If you do want to use it from the SYSTEM account, in scripting, it gets interesting. You’ll want to first run the below, and then winget will run as expected.

# Function to find the path to winget.exe
function Find-WinGet-Path {
    # Get the WinGet path (for use when running in SYSTEM context).
    $WinGetPathToResolve = Join-Path -Path $ENV:ProgramFiles -ChildPath 'WindowsApps\Microsoft.DesktopAppInstaller_*_*__8wekyb3d8bbwe'
    $ResolveWinGetPath = Resolve-Path -Path $WinGetPathToResolve | Sort-Object {
        [version]($_.Path -replace '^[^\d]+_((\d+\.)*\d+)_.*', '$1')
    }
    if ($ResolveWinGetPath) {
        # If we have multiple versions - use the latest.
        $WinGetPath = $ResolveWinGetPath[-1].Path
    }

    # Get the User-Context WinGet exe location.
    $WinGetExePath = Get-Command -Name winget.exe -CommandType Application -ErrorAction SilentlyContinue

    # Select the correct WinGet exe
    if (Test-Path -Path (Join-Path $WinGetPath 'winget.exe')) {
        # Running in SYSTEM-Context.
        $WinGet = Join-Path $WinGetPath 'winget.exe'
    } elseif ($WinGetExePath) {
        # Get User-Context if SYSTEM-Context not found.
        $WinGet = $WinGetExePath.Path
    } else {
        Write-Output 'WinGet not Found!'
        Stop-Transcript
        exit 1
    }

    # Return WinGet path
    return ($WinGet -replace '\winget.exe','')
}

Function Add-PathVariable {
    param (
        [string]$addPath
    )
    if (Test-Path $addPath){
        $regexAddPath = [regex]::Escape($addPath)
        $arrPath = $env:Path -split ';' | Where-Object {$_ -notMatch 
"^$regexAddPath\\?"}
        $env:Path = ($arrPath + $addPath) -join ';'
    } else {
        Throw "'$addPath' is not a valid path."
    }
}

Add-PathVariable (Find-Winget-Path)

Installing has been even more interesting. I’ve tried a lot of things. The below worked very well recently on Server 2019, and just now on Server 2022. It is a script which installs prerequisites, and installs another script called winget-install. After the below is successful, run winget-install, and we can expect winget to be installed in working order.

#Begin installation of winget-install

Set-ExecutionPolicy -Scope Process -ExecutionPolicy Unrestricted -Force > $null

Function PrepareModule {
	param( [string]$ModuleName )

	"Preparing Powershell environment: Getting online " + $ModuleName + " info..."
	$OnlineModuleInfo = Find-Module $ModuleName -Repository PSGallery
	"Preparing Powershell environment: Getting local " + $ModuleName + " info (if exists)..."
	$LocalModuleInfo = Get-InstalledModule $ModuleName -ErrorAction SilentlyContinue > $null

	If ($OnlineModuleInfo.Version -ne $LocalModuleInfo.Version) {
		"Preparing Powershell environment: Removing old " + $ModuleName + " (if exists)..."
		Uninstall-Module -Name $ModuleName -ErrorAction SilentlyContinue > $null
		"Preparing Powershell environment: Installing new " + $ModuleName + "..."
		Install-Module -Name $ModuleName -Repository PSGallery
		"Preparing Powershell environment: Importing new " + $ModuleName + "..."
		Import-Module -Name $ModuleName
		}
}

"Setting up to use Powershell Gallery..."
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
Set-PSRepository -InstallationPolicy Trusted -Name PSGallery

PrepareModule("NuGet")	

Install-Script -Name winget-install 

#End installation of winget-install

Categories: