This works for recent builds of 10 and 11. Server 2019 sometimes has issues.
<# Winget-Install.ps1 Source for Some Functions: https://github.com/homotechsual/ninjaget/blob/dev/PS/NinjaGet.Functions.Installation.ps1 2023-11-22 - 0.0.2 - assembled and modified by David Szpunar - Initial version DOWNLOAD: https://discord.com/channels/676451788395642880/1232432179141808228 2024-04-23 - 0.1.0 - Simplified and Modified by GraphicHealer - Initial Version 2024-10-11 - 0.1.1 - took out Ninja RMM item - JEB DESCRIPTION: Installs or updates WinGet.exe to the latest version. Intended to run as SYSTEM. Use '-Force' to ignore version check. ----- LICENSE: The MIT License (MIT) Original Copyright 2023 Mikey O'Toole, modified by David Szpunar, Simplified and Modified by GraphicHealer. #> # Add -Force switch to force install param( [switch]$Force = $false ) # Start Logging Start-Transcript -Path ".\winget-install.log" -IncludeInvocationHeader -Append -Force # Setup Error Codes $Script:ExitCode = 0 # Function to get OS Information function Get-OSInfo { try { # Get registry values $registryValues = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' $releaseIdValue = $registryValues.ReleaseId $displayVersionValue = $registryValues.DisplayVersion $nameValue = $registryValues.ProductName $editionIdValue = $registryValues.EditionId # Strip out "Server" from the $editionIdValue if it exists $editionIdValue = $editionIdValue -replace 'Server', '' # Get OS details using Get-CimInstance because the registry key for Name is not always correct with Windows 11 $osDetails = Get-CimInstance -ClassName Win32_OperatingSystem $nameValue = $osDetails.Caption # Get architecture details of the OS (not the processor) # Get only the numbers $architecture = ($osDetails.OSArchitecture -replace '[^\d]').Trim() # If 32-bit or 64-bit replace with x32 and x64 if ($architecture -eq '32') { $architecture = 'x32' } elseif ($architecture -eq '64') { $architecture = 'x64' } # Get OS version details (as version object) $versionValue = [System.Environment]::OSVersion.Version # Determine product type # Reference: https://learn.microsoft.com/en-us/dotnet/api/microsoft.powershell.commands.producttype?view=powershellsdk-1.1.0 if ($osDetails.ProductType -eq 1) { $typeValue = 'Workstation' } elseif ($osDetails.ProductType -eq 2 -or $osDetails.ProductType -eq 3) { $typeValue = 'Server' } else { $typeValue = 'Unknown' } # Extract numerical value from Name $numericVersion = ($nameValue -replace '[^\d]').Trim() # Create and return custom object with the required properties $result = [PSCustomObject]@{ ReleaseId = $releaseIdValue DisplayVersion = $displayVersionValue Name = $nameValue Type = $typeValue NumericVersion = $numericVersion EditionId = $editionIdValue Version = $versionValue Architecture = $architecture } return $result } catch { Write-Error "Unable to get OS version details.`nError: $_" exit 1 } } # Function to Install WinGet (modified) function Install-WinGet { # Install VCPPRedist Requirements $Visual2019 = 'Microsoft Visual C++ 2015-2019 Redistributable*' $Visual2022 = 'Microsoft Visual C++ 2015-2022 Redistributable*' $VCPPInstalled = Get-Item @('HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*') | Where-Object { $_.GetValue('DisplayName') -like $Visual2019 -or $_.GetValue('DisplayName') -like $Visual2022 } if ([System.Environment]::Is64BitOperatingSystem) { $OSArch = 'x64' } else { $OSArch = 'x86' } if (!($VCPPInstalled)) { Write-Output 'VCPPRedist Requirements Not Installed. Installing...' $VCPPRedistURL = ('https://aka.ms/vs/17/release/vc_redist.{0}.exe' -f $OSArch) $VCPPRedistFileName = [Uri]$VCPPRedistURL | Select-Object -ExpandProperty Segments | Select-Object -Last 1 $WebClient = New-Object System.Net.WebClient $VCPPRedistDownloadPath = "$ENV:SystemRoot\Temp\VCPRedist" if (!(Test-Path -Path $VCPPRedistDownloadPath)) { $null = New-Item -Path $VCPPRedistDownloadPath -ItemType Directory -Force } $VCPPRedistDownloadFile = "$VCPPRedistDownloadPath\$VCPPRedistFileName" $WebClient.DownloadFile($VCPPRedistURL, $VCPPRedistDownloadFile) try { Start-Process -FilePath $VCPPRedistDownloadFile -ArgumentList '/quiet', '/norestart' -Wait -ErrorAction Stop | Out-Null Write-Output 'VCPPRedist Requirements Installed.' } catch { Write-Output 'Failed to install VCPPRedist Requirements!' $Script:ExitCode = 1 return } finally { Remove-Item -Path $VCPPRedistDownloadPath -Recurse -Force -ErrorAction SilentlyContinue } } else { Write-Output 'VCPPRedist Requirements Installed.' } # Find Latest Download $LatestRelease = (Invoke-RestMethod -Uri 'https://api.github.com/repos/microsoft/winget-cli/releases/latest' -Method Get) $LatestAsset = ($LatestRelease).assets | Where-Object { $_.name.EndsWith('.msixbundle') } [version]$LatestVersion = $LatestRelease.tag_name.TrimStart('v') if ((Find-WinGet)) { [version]$CurrentVersion = (& (Find-WinGet) '-v').TrimStart('v') } else { [version]$CurrentVersion = 0 } # Check if WinGet is Updated if (!($CurrentVersion -lt $LatestVersion) -and !$Force) { # Exit Write-Output 'WinGet is up to date. Exiting.' return } # Installing WinGet Write-Output 'WinGet not installed or out of date. Installing/updating...' # Download WinGet to Temp $WebClient = New-Object System.Net.WebClient $WinGetDownloadPath = Join-Path -Path "$ENV:SystemRoot\Temp" -ChildPath $LatestAsset.name Write-Output "Downloading WinGet to $WinGetDownloadPath..." $WebClient.DownloadFile($LatestAsset.browser_download_url, $WinGetDownloadPath) # Download WinGet Source to Temp $WinGetSourceDownloadPath = Join-Path -Path "$ENV:SystemRoot\Temp" -ChildPath 'source.msix' Write-Output "Downloading WinGet to $WinGetSourceDownloadPath..." Invoke-WebRequest -Uri 'https://cdn.winget.microsoft.com/cache/source.msix' -OutFile $WinGetSourceDownloadPath $WinArch = (Get-OSInfo).Architecture # Download VCLibs $VCLibs_Path = Join-Path -Path "$ENV:SystemRoot\Temp" -ChildPath "Microsoft.VCLibs.${WinArch}.14.00.Desktop.appx" $VCLibs_Url = "https://aka.ms/Microsoft.VCLibs.${WinArch}.14.00.Desktop.appx" Write-Output 'Downloading VCLibs...' Write-Debug "Downloading VCLibs from $VCLibs_Url to $VCLibs_Path`n`n" Invoke-WebRequest -Uri $VCLibs_Url -OutFile $VCLibs_Path # Download UI.Xaml $UIXaml_Path = Join-Path -Path "$ENV:SystemRoot\Temp" -ChildPath "Microsoft.UI.Xaml.2.8.${WinArch}.appx" $UIXaml_Url = "https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.8.6/Microsoft.UI.Xaml.2.8.${WinArch}.appx" Write-Output 'Downloading UI.Xaml...' Write-Debug "Downloading UI.Xaml from $UIXaml_Url to $UIXaml_Path`n" Invoke-WebRequest -Uri $UIXaml_Url -OutFile $UIXaml_Path # Install Downloaded WinGet try { Write-Output 'Installing WinGet...' Add-AppxProvisionedPackage -Online -PackagePath $WinGetDownloadPath -DependencyPackagePath $UIXaml_Path, $VCLibs_Path -SkipLicense -ErrorAction Stop Add-AppxProvisionedPackage -Online -PackagePath $WinGetSourceDownloadPath -SkipLicense -ErrorAction Stop Write-Output 'WinGet installed.' } catch { Write-Output 'Failed to install WinGet!' $Script:ExitCode = 1 } finally { Remove-Item -Path $WinGetDownloadPath -Force -ErrorAction SilentlyContinue } return } # Function to Find the WinGet exe (Modified) function Find-WinGet { # 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 Location return $WinGet } # Test internet connection $MaxNet = 15 Write-Output 'Checking Internet Connection...' for ($i = 0; ($i -lt $MaxNet) -and !$net; $i++) { $net = Test-Connection 1.1.1.1 -Count 1 -Delay 1 -Quiet } if (!$net) { Write-Output 'No Internet Detected. Cancelling Installation.' Stop-Transcript exit 1 } Write-Output 'Internet Detected, Continuing Installation.' # Test OS Compatibility $osVersion = Get-OSInfo # If it's a workstation, make sure it is Windows 10+ if ($osVersion.Type -eq 'Workstation' -and $osVersion.NumericVersion -lt 10) { Write-Error 'winget requires Windows 10 or later on workstations. Your version of Windows is not supported.' Stop-Transcript exit 0 } # If it's a workstation with Windows 10, make sure it's version 1809 or greater if ($osVersion.Type -eq 'Workstation' -and $osVersion.NumericVersion -eq 10 -and $osVersion.ReleaseId -lt 1809) { Write-Error 'winget requires Windows 10 version 1809 or later on workstations. Please update Windows to a compatible version.' Stop-Transcript exit 0 } # Run the Install/Update Install-WinGet & (Find-WinGet) 'list' '--accept-source-agreements' Stop-Transcript exit $Script:ExitCode