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:

  1. How to manually create shortcuts for the command line. Now the PowerShell module "NTObjectManager" can do that as well.
  2. How the download of dependent application components can be greatly simplified. can be. Some dependencies can be found apparently "https://aka.ms/xyz.appx". Unfortunately, as things stand, not everything can be found.

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'