As I wrote in the blog "Winget on Server 2022", there is a reason why we can't use Winget.exe with Server 2019. Winget is started from the command line and support for it is missing in Server 2019. The situation has changed and we have a solution.
Winget, similar to "aptget" on Linux, allows a significant number of Applications can be installed with simple commands from the command line. A description of Winget can be found here:
https://docs.microsoft.com/en-us/windows/package-manager/winget/.
The Winget installation package Winget is a part of the AppInstaller package with which AppX and MSIX packages can be installed at the click of a button. In the Microsoft Store you can find the package can be found here (pay attention to the release here, possibly use Insider):
https://www.microsoft.com/de-de/p/app-installer/9nblggh4nns1#activetab=pivot:overviewtab.
Furthermore you can find the package Windows Package Manager in the GitHub repository. Manager, which can be installed manually and also includes Winget:
https://github.com/microsoft/winget-cli/releases/tag/v1.1.12653
This is all a bit confusing from a purely linguistic point of view. The key thing is that we need an easy command line tool that we can use to automatically install applications from many manufacturers in an automated way. It would now of course be nice to use this Tool, also for Server 2019 to be able to use.
My friend Thorsten Butz (you can also find a session of PSUGH there as a recording) figured out two things:
Thorsten Butz has shown, among other things, in his presentation, how to get the link (Den Repse Point) can be displayed:
Fsutil.exe reparsepoint Query "$home\AppData\Local\Microsoft\WindowsApps\winget.exe"
Some PowerShell 7 versions before what was also possible to display the this data directly in the PowerShell to let indicate. However, there were apparently problems with the Performance. Therefore, Microsoft has removed this feature. In this view you could you could see that in reality with the, rebase point to a directory under c:\Program Files\WindowsApps.... is referenced.
With the information obtained, an installation script can be created that will Winget can deploy to Server 2019 as well.
About the script
Installation can be done offline if the package files are available offline on the target system. on the target system. The path to the directory containing the installation files is defined in the "RepoPath" variable and can be customized in the source code.
$RepoPath = "$env:TEMP\WinGetTemp"
The download can be done on a system with internet access, for example.
Now the line "$Offline = $false must also be set to $true. The Files must be recognized and present in the repo directory. Offline it only works because Microsoft provides a license file on Github. This with a parameter to the function Add-AppxProvisionedPackage and the license file must License file must be present in the repo directory.
The script absolutely requires the PowerShell module 'NTObjectManager'. If this module is not installed, the script will try to install this module itself. itself. Needed is the module for the rebase point on Winget.exe.
The execution of the script must be as administrator. Otherwise the rebase point on Winget.exe cannot be created.
# WinGet Version Installation Scrip for Server 2019 # Work with Version 1.2.10271 or internal Version="1.17.10271.0" # Or Bundle Version="2022.127.2322.0" # Andreas Nick 2022 Write-Host "Download and install winget" -ForegroundColor Yellow if(!(Get-Module -ListAvailable -Name 'NTObjectManager')){ Install-Module 'NTObjectManager' -scope CurrentUser -Force -Confirm:$false } Import-Module 'NTObjectManager' $Offline = $false $RepoPath = "$env:TEMP\WinGetTemp" $BaseURL = 'https://www.microsoft.com/store/productId/9NBLGGH4NNS1' $DownloadFiles = @( 'Microsoft.DesktopAppInstaller_2022.127.2322.0_neutral_~_8wekyb3d8bbwe.msixbundle', 'Microsoft.VCLibs.140.00.UWPDesktop_14.0.30704.0_x64__8wekyb3d8bbwe.appx', 'Microsoft.UI.Xaml.2.7_7.2203.17001.0_x64__8wekyb3d8bbwe.appx') # Version 1.2.10271 !! $LicenseFileURL = 'https://github.com/microsoft/winget-cli/releases/download/v1.2.10271/b0a0692da1034339b76dce1c298a1e42_License1.xml' if(Test-Path $RepoPath ) { #Remove-Item $RepoPath -Recurse } else { New-Item $RepoPath -ItemType Directory } function Get-AppXPackageURL { [CmdletBinding()] param ( [string]$Uri, [string]$Filter = '.*' #Regex ) process { #$Uri=$StoreLink $WebResponse = Invoke-WebRequest -UseBasicParsing -Method 'POST' -Uri 'https://store.rg-adguard.net/api/GetFiles' -Body "type=url&url=$Uri&ring=Retail" -ContentType 'application/x-www-form-urlencoded' $result =$WebResponse.Links.outerHtml | Where-Object {($_ -like '*.appx*') -or ($_ -like '*.msix*')} | Where-Object {$_ -like '*_neutral_*' -or $_ -like "*_"+$env:PROCESSOR_ARCHITECTURE.Replace("AMD","X").Replace("IA","X")+"_*"} | ForEach-Object { $result = "" | Select-Object -Property filename, downloadurl if( $_ -match '(?<=rel="noreferrer">).+(?=</a>)' ) { $result.filename = $matches.Values[0] } if( $_ -match '(?<=a href=").+(?=" r)' ) { $result.downloadurl = $matches.Values[0] } $result } $result | Where-Object -Property filename -Match $filter } } #Download Winget 2022.127.2322.0 and Dependencies $Packlist = @() if(-not $Offline){ $Packlist = @(Get-AppXPackageURL -Uri $BaseURL) #Download License file Invoke-WebRequest -Uri $LicenseFileURL -OutFile (Join-Path $RepoPath -ChildPath 'license.xml' ) #Download package files foreach($item in $DownloadFiles) { if(-not (Test-Path (Join-Path $RepoPath -ChildPath $item ))) { $dlurl = [string]($Packlist | Where-Object -Property filename -match $item)[0].downloadurl Invoke-WebRequest -Uri $dlurl -OutFile (Join-Path $RepoPath -ChildPath $item ) } else { Write-Information "The file $($item) already exist in the repo. Skip download" } } } #Install Winget without license #Add-AppxPackage -Path $(Join-Path $RepoPath -ChildPath $DownloadFiles[0]) -DependencyPath $(Join-Path $RepoPath -ChildPath $DownloadFiles[1]), $(Join-Path $RepoPath -ChildPath $DownloadFiles[2]) #Get-AppxPackage Microsoft.DesktopAppInstaller | Remove-AppxPackage #Install Winget with license Add-AppxProvisionedPackage -PackagePath $(Join-Path $RepoPath -ChildPath $DownloadFiles[0]) -LicensePath $(Join-Path $RepoPath -ChildPath 'license.xml') -online ` -DependencyPackagePath $(Join-Path $RepoPath -ChildPath $DownloadFiles[1]), $(Join-Path $RepoPath -ChildPath $DownloadFiles[2]) # Here is the trick of Thorsten Butz. We create a rebase point ourselves # The alias can be defined somewhere in the $Temp:Path. For example, in C:\Windows\System32 Set-ExecutionAlias -Path "$env:windir\winget.exe" -Target $((Get-AppxPackage *DesktopAppInstaller*).InstallLocation + '\winget.exe') -PackageName ` 'Microsoft.DesktopAppInstaller_8wekyb3d8bbwe' -AppType Desktop -EntryPoint 'Microsoft.DesktopAppInstaller_8wekyb3d8bbwe!winget' -Version 3 #Get-ExecutionAlias 'c:\windows\winget.exe' #Remove-Item 'c:\windows\winget.exe' #'Microsoft.DesktopAppInstaller_8wekyb3d8bbwe'
Comments 6
Danke für das Skript! mittlerweile gibt es zu den Downloadfiles neuere Versionen:
$DownloadFiles = @( 'Microsoft.DesktopAppInstaller_2023.118.406.0_neutral_~_8wekyb3d8bbwe.msixbundle',
'Microsoft.VCLibs.140.00.UWPDesktop_14.0.30704.0_x64__8wekyb3d8bbwe.appx',
'Microsoft.UI.Xaml.2.7_7.2208.15002.0_x64__8wekyb3d8bbwe.appx'
)
Danke
The script doesnt work for me? Can you explain why its not working for me?
Cannot index into a null array.
At C:\Users\sa_vda\Desktop\winget script.ps1:71 char:8
+ $dlurl = [string]($Packlist | Where-Object -Property filename ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: ( [], RuntimeException
+ FullyQualifiedErrorId : NullArray
Invoke-WebRequest : Cannot validate argument on parameter 'Uri'. The argument is null or empty. Provide an argument tha
t is not null or empty, and then try the command again.
At C:\Users\sa_vda\Desktop\winget script.ps1:72 char:32
+ Invoke-WebRequest -Uri $dlurl -OutFile (Join-Path $RepoPath - ...
+ ~~~~~~
+ CategoryInfo : InvalidData: ( [Invoke-WebRequest], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
Cannot index into a null array.
At C:\Users\sa_vda\Desktop\winget script.ps1:71 char:8
+ $dlurl = [string]($Packlist | Where-Object -Property filename ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: ( [], RuntimeException
+ FullyQualifiedErrorId : NullArray
Add-AppxProvisionedPackage : De parameter is onjuist.
At C:\Users\sa_vda\Desktop\winget script.ps1:86 char:1
+ Add-AppxProvisionedPackage -PackagePath $(Join-Path $RepoPath -ChildP ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: ( [Add-AppxProvisionedPackage], PSArgumentException
+ FullyQualifiedErrorId : Microsoft.Dism.Commands.AddAppxProvisionedPackageCommand
The element "$Packlist" appears to be empty. Perhaps no data came over the Internet or something has changed at Microsoft.
Hi Andreas,
could yoe be so kind to append a list to your posting which privides the exact files and download locations, which I habe to dump to $RepoPath before starting the script above. I cannot figure which of the download links are informational or essential for the script to succeed.
Thank you in advance, Thomas
Sorry for the late reply. The notification system was defective. Nothing has to be deposited but the following block is probably no longer up to date (see above)
$DownloadFiles = @( 'Microsoft.DesktopAppInstaller_2022.127.2322.0_neutral_~_8wekyb3d8bbwe.msixbundle',
'Microsoft.VCLibs.140.00.UWPDesktop_14.0.30704.0_x64__8wekyb3d8bbwe.appx',
'Microsoft.UI.Xaml.2.7_7.2203.17001.0_x64__8wekyb3d8bbwe.appx')