Cloudy: With a chance of Winget

Last Updated on November 10, 2022 by rudyooms

This blog will be about the experience I had with the Windows Package Manager AKA Desktop App Installer AKA Winget. Until now we only made use of Chocolatey but I was intrigued if Winget could be a good replacement. After reading some other blogs and information I decided to test it out myself.

Just like always, I am going to divide this blog into multiple parts.

  1. Background information
  2. Installing the App Installer
  3. Installing/removing applications
  4. Upgrading applications
  5. Winget ADMX
  6. Troubleshooting Winget
  7. Upload your own Apps
  8. Deploying the apps with the Company Portal app

1. Background Information

Just like Chocolatey, Winget is a  tool to manage packages (Package Manager) on your device. This utility gives you the possibility to automate the whole process of installing/removing/upgrading (updating)  packages (software) on the device.

Just with the use of the command line or PowerShell you could automatically download the packages and install them on the device. When you have some experience with Linux you will be familiar with it but Windows users didn’t have this built-in functionality so they needed to use some third-party software like Chocolatey.

Luckily in 2020, Microsoft released its own Windows Packaging Manager (Winget) for Windows 10. But when it was released it was a little bit buggy in my opinion.

Things have changed as Microsoft is also integrating Winget in Intune as the Microsoft Store for Business will be deprecated in the first quarter of 2023. If you are interested in the whole story behind this deprecation and what is going to happen, please read this blog

2. Installing the App Installer (Winget)

When you want to play around or just want to start migrating your Chocolatey apps to Winget, you will need to make sure Winget is installed on the devices. On all modern versions of Windows 10 and Windows 11, this Package Manager Command-line tool is already installed. Isn’t that nice? Or maybe not?

As shown below when taking a look at the WindowsApps folder you will notice that the Microsoft.DesktopAppInstaller is already installed by default!

But it will take some time before the Winget.exe tool itself gets installed and in the meantime, the Winget.exe Command is NOT yet available on Windows 10 devices!

Before the Winget executable will be available It first needs to fetch the required update from the Microsoft Store

After it downloads the latest update, you will notice that a new version is installed and inside that folder, the Winget.Exe file does exist. I am wondering what will happen when you are deploying an app with Winget during Autopilot?

If you still have some older Windows versions or you want to make sure Winget.exe Is already installed on the device after enrolling the device with Autopilot you need to install them manually. You have got multiple options to install Winget

  1. Install the App Package with Powershell (USER CONTEXT)
  2. Install the App Package with PowerShell (SYSTEM Context)
  3. Deploy the App Installer from the Microsoft Store (SYSTEM CONTEXT)
  4. Deploy the App installer a LOB App (SYSTEM CONTEXT)
  5. Deploy the App installer a Win32 App (SYSTEM CONTEXT)

2.1. Using PowerShell (USER CONTEXT)

You could call upon the invoke web request to download the app package file straight from the source. Please note: Before we could install the DesktopAppinstaller we need to make sure all the requirements are met! To do so I added the Add-AppxPackage Microsoft.VClibs to the script

$hasPackageManager = Get-AppPackage -name 'Microsoft.DesktopAppInstaller'
if (!$hasPackageManager -or [version]$hasPackageManager.Version -lt [version]"1.10.0.0") {
    "Installing winget Dependencies"
    Add-AppxPackage -Path 'https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx'
    $releases_url = 'https://api.github.com/repos/microsoft/winget-cli/releases/latest'

    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    $releases = Invoke-RestMethod -uri $releases_url
    $latestRelease = $releases.assets | Where { $_.browser_download_url.EndsWith('msixbundle') } | Select -First 1

    "Installing winget from $($latestRelease.browser_download_url)"
    Add-AppxPackage -Path $latestRelease.browser_download_url
}
else {
    "winget already installed"
}
#### Creating settings.json #####

if ([System.Security.Principal.WindowsIdentity]::GetCurrent().IsSystem) {
        $SettingsPath = "$Env:windir\system32\config\systemprofile\AppData\Local\Microsoft\WinGet\Settings\settings.json"
    }else{
        $SettingsPath = "$env:LOCALAPPDATA\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\settings.json"
    }
    if (Test-Path $SettingsPath){
        $ConfigFile = Get-Content -Path $SettingsPath | Where-Object {$_ -notmatch '//'} | ConvertFrom-Json
    }
    if (!$ConfigFile){
        $ConfigFile = @{}
    }
    if ($ConfigFile.installBehavior.preferences.scope){
        $ConfigFile.installBehavior.preferences.scope = "Machine"
    }else {
        Add-Member -InputObject $ConfigFile -MemberType NoteProperty -Name 'installBehavior' -Value $(
            New-Object PSObject -Property $(@{preferences = $(
                    New-Object PSObject -Property $(@{scope = "Machine"}))
            })
        ) -Force
    }
    $ConfigFile | ConvertTo-Json | Out-File $SettingsPath -Encoding utf8 -Force

Please Note: This PowerShell script can’t be executed in the System context as it uses the add-appxpackage!!! If you will try to do so you will end up with the 0x80073cf9 error

I’m explaining this behavior and the error code 0x80073cf9 in this blog about the Company Portal and the missing frameworks

2.2 Using PowerShell (SYSTEM Context)

After posting this blog and having some talks with James Robinson, I came to the conclusion I was missing this PowerShell option. In part 2.1 I showed you how to download the latest Winget version in an elevated PowerShell session. So why not use the same idea but combine it with Add-ProvisionedAppxPackage from part 2.5.

#WebClient
$dc = New-Object net.webclient
$dc.UseDefaultCredentials = $true
$dc.Headers.Add("user-agent", "Inter Explorer")
$dc.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f")

#temp folder
$InstallerFolder = $(Join-Path $env:ProgramData CustomScripts)
if (!(Test-Path $InstallerFolder))
{
New-Item -Path $InstallerFolder -ItemType Directory -Force -Confirm:$false
}
	#Check Winget Install
	Write-Host "Checking if Winget is installed" -ForegroundColor Yellow
	$TestWinget = Get-AppxProvisionedPackage -Online | Where-Object {$_.DisplayName -eq "Microsoft.DesktopAppInstaller"}
	If ([Version]$TestWinGet. Version -gt "2022.506.16.0") 
	{
		Write-Host "WinGet is Installed" -ForegroundColor Green
	}Else 
		{
		#Download WinGet MSIXBundle
		Write-Host "Not installed. Downloading WinGet..." 
		$WinGetURL = "https://aka.ms/getwinget"
		$dc.DownloadFile($WinGetURL, "$InstallerFolder\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle")
		
		#Install WinGet MSIXBundle 
		Try 	{
			Write-Host "Installing MSIXBundle for App Installer..." 
			Add-AppxProvisionedPackage -Online -PackagePath "$InstallerFolder\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle" -SkipLicense 
			Write-Host "Installed MSIXBundle for App Installer" -ForegroundColor Green
			}
		Catch {
			Write-Host "Failed to install MSIXBundle for App Installer..." -ForegroundColor Red
			} 
	
		#Remove WinGet MSIXBundle 
		#Remove-Item -Path "$InstallerFolder\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle" -Force -ErrorAction Continue
		}

2.3 Microsoft Store

If you don’t like to use a simple PowerShell script and/or you want to automate it you could make sure that The Windows Package Manager is installed from within the Microsoft Store. But of course, you will need to ensure you have configured/synced the Microsoft Store for Business with your Microsoft 365 tenant.

When you have made sure the Microsoft store for business is connected you can click on the “Open the business store” to start searching. Search for the “App installer” package and install/order it.

I will advise you to use the Offline version of the App Installer as shown below so you can make sure the Winget app will be delivered in the Device context

If you want to know more about why this is important please read this blog. In this blog, I am explaining the differences between the Online and Offline version

Company Portal | Intune | Offline (device) vs Online (user) (call4cloud.nl)

When you have made sure the App Installer is approved don’t forget to sync and assign the app in Intune when you have added the app to the collection.

Please Note: When assigning the app, please make sure when you are using the offline version you also configure the license type to a device license!

Please note: Even when using this option to deploy the App installer, it could take some time before the Winget.exe file arrives at the device

2.4 Deploy the App Installer a LOB app

Another possibility would be to download the required Appx files yourself and upload it to Intune as LOB App. This option will make sure, that Winget will be installed in the device/system Context!

After uploading it, please make sure you configure the Install context to “Device Context”

This is almost my most favorite option...Do you want to know why? Because it will make sure the user could open the Company Portal and start installing the apps which are deployed with Winget immediately.

After making sure you have uploaded the Winget app to Intune, don’t forget to configure this app as a required app during the Enrollment Status Page (ESP). You really want to have Winget deployed to your devices before the user logs in!

2.5 Deploy the App installer as a Win32 App

Let’s continue to my most favorite option! Of course, mixing LOB apps and Win32apps isn’t recommended so let’s make sure we are converting those appx files to a nice Win32App Package instead!

Let’s take a better look at the PowerShell script itself, shall we?

Add-ProvisionedAppxPackage -online -PackagePath:.\Microsoft.DesktopAppInstaller_2022.610.123.0_neutral___8wekyb3d8bbwe.Msixbundle -DependencyPackagePath .\Microsoft.VCLibs.140.00.UWPDesktop_14.0.30704.0_x64__8wekyb3d8bbwe.Appx,.\Microsoft.UI.Xaml.2.7_7.2203.17001.0_x64__8wekyb3d8bbwe.Appx -SkipLicense

As shown above, I am using the Add-ProvisionedAppxPackage to make sure I can install Winget/App Installer in the system context. In that same command, I am also defining the DependencyPackagePath. This Packagepath contains the App installer dependencies

When uploading the Win32app to Intune, we need to configure the detection rule. Of course we could make sure we configure the proper path as a detection rule. Because you don’t want to end up with the 0x81036502 Time-out error!

But it will fail you in the future, as it doesn’t know about newer versions. So my advise? Just use a custom detection rule to make sure it will always tries to find the up to date path

As shown below, I am using almost the same script as I used when deploying Applications with Winget. In this detection script, I am just trying to find the Winget.exe file in the latest DesktopAppInstaller folder

$ResolveWingetPath = Resolve-Path "C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_*_x64__8wekyb3d8bbwe\winget.exe"
    if ($ResolveWingetPath){
           $WingetPath = $ResolveWingetPath[-1].Path
    }
$wingetexe = $ResolveWingetPath 

if (Test-path $wingetexe)
{ Write-host "Found Winget"}

Please Note: Just as with the LOB 2.3 option, don’t forget to add the app to your list of required apps during the ESP

3. Installing and Removing applications

Installing Applications

Installing applications is normally done within a few seconds. If you want to get a list of all the applications you could install, just specify the “install” or “show” parameter and it will list all the packages out there.

Another possibility is to get a list of all apps available in the repositories by opening a browser and browse to

winget-pkgs/manifests at master · KaranKad/winget-pkgs (github.com)

Let’s proceed with installing some applications/packages. So if you want to install WebView2, you will need to specify this command.

$ResolveWingetPath = Resolve-Path "C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_*_x64__8wekyb3d8bbwe\winget.exe"
    if ($ResolveWingetPath){
           $WingetPath = $ResolveWingetPath[-1].Path
    }

$Wingetpath = Split-Path -Path $WingetPath -Parent
cd $wingetpath
.\winget.exe install --exact --id Microsoft.EdgeWebView2Runtime --silent --accept-package-agreements --accept-source-agreements

Looking at the above script, you will notice that I am trying to resolve the DesktopAppInstaller path first. Why? Let me show you

Because the Winget command is normally not recognized in the system context but that doesn’t mean you can’t use it! When you have deployed Winget in the device context, you could use the above PowerShell script to deliver apps in the system context

If you want to have multiple apps installed at the same time you could use the ; between specifying the apps

winget install 7zip.7zip  ;  winget install Mozilla.Firefox  – -force – – silent

Please Note:

Before installing applications, make sure you have taken a look at the settings.json in Winget. Maybe you want to make sure the packages are installed for the entire machine instead of the current user?

This is my settings.json file which I tested Winget with

Removing Applications

Removing existing software is also very very easy. You will only need to specify the uninstall parameter and which apps need to be removed

$ResolveWingetPath = Resolve-Path "C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_*_x64__8wekyb3d8bbwe\winget.exe"
    if ($ResolveWingetPath){
           $WingetPath = $ResolveWingetPath[-1].Path
    }

$Wingetpath = Split-Path -Path $WingetPath -Parent

cd $wingetpath
.\winget.exe uninstall --exact --id Microsoft.Teams --silent --accept-package-agreements --accept-source-agreements

When using this PowerShell script, it will uninstall the Microsoft Teams app!

It’s simple and it works just like it should and nothing more.

4. Upgrading applications

Just like with Chocolatey you will get the possibility to update/upgrade apps. It’s very easy to upgrade the apps, you only need to specify the upgrade parameter and which app you want to update.

Or use the “list option” to check if a new version is available.

I guess I am going to like this Winget update function a lot more in the feature than Chocolatey. Winget lists all applications which needed to be updated! Chocolatey only lists the applications which needed to be updated that chocolatey installed itself.

When you need to update all apps with Winget specify the upgrade  – -all parameter.

If you want to make sure your apps are upgraded each week you need to create a PowerShell script and convert it to a Win32app. This script will create an additional PowerShell script and attach it to a task schedule. Please Note: Sometimes stuff changes and I need to update my blog. I needed to add the –accept-source-agreements , otherwise, the upgrade was still waiting for approval!

$content = @'
winget source remove msstore
winget source reset --force 
winget upgrade --query --silent --force --accept-package-agreements --accept-source-agreements --all
'@

 
# create custom folder and write PS script
$path = $(Join-Path $env:ProgramData CustomScripts)
if (!(Test-Path $path))
{
New-Item -Path $path -ItemType Directory -Force -Confirm:$false
}
Out-File -FilePath $(Join-Path $env:ProgramData CustomScripts\WingetUppgradeApps.ps1) -Encoding unicode -Force -InputObject $content -Confirm:$false
 
# register script as scheduled task
$Time = New-ScheduledTaskTrigger -AtLogOn
$User = "SYSTEM"
$Action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ex bypass -file `"C:\ProgramData\CustomScripts\WingetUppgradeApps.ps1`""
Register-ScheduledTask -TaskName "UpgradeApps" -Trigger $Time -User $User -Action $Action -Force
Start-ScheduledTask -TaskName "UpgradeApps"

The only two things I am missing (for now) are:

  1. Excluding applications.

When you have teams installed it will try to update teams…. And most of the time it will fail. Luckily when other programs needed to be updated it will just proceed even with the first installation error.

2.No applicable update found

And I guess that’s why I was mentioning: “I am going to like this update function” because for now a lot of already installed programs can not be upgraded! You will be prompted with: No applicable update found.

But when I remove the zoom 5.5.2 application first and reinstall the same version again with the parameter: Winget install zoom.zoom – -version 5.5.2

I can upgrade it with no problem at all? that’s odd but I know it can be very difficult to make sure all the packages can be updated.

A way better option would be to use this wonderful tool!

GitHub – Romanitho/Winget-AutoUpdate: WAU daily updates apps as system and notify connected users. (Allowlist and Blocklist support)

5. Winget ADMX

I guess I really love this one. Winget even has its own ADMX files which you can use to limit some functions:

https://github.com/microsoft/winget-cli/releases/download/v1.0.11451/DesktopAppInstallerPolicies.zip

I guess when I am going to only use Winget, I will take some time to make sure you can ingest the ADMX in Intune. Isn’t that cool?

6. Troubleshooting Winget installation errors

If you have the same luck as me, you always will directly end up with some troubleshooting. Don’t get me wrong, but I love when it will fail on the first attempt… if all succeeds without errors, you don’t get the possibility to start troubleshooting it.

Luckily you will get a very nice log file, which I didn’t expect to be good…

Like I was telling you with the upgrade – option,  upgrading teams is difficult. It’s not Winget that is causing the problem but it’s the Teams installer.

Luckily I have some experience with Teams installers so I got you covered!

7. Building your own packages

If you want to start building your own packages you will need to install the create module first: “Winget install wingetcreate”

After the module is installed you can just create a new package when you enter this command: wingetcreate new. It will ask you for the download URL location of the installation file.

As an example, I specified the notepad + + exe download url

It will guide you through the whole process. It will ask you some default questions which need to be answered.

When you are almost done it will ask you to submit the package to your own repository and prompt you for your GitHub credentials.

As shown above, I used the NotePad + + example, it’s already in the repository. Just within a minute, I (of course) received a message that it’s already in the repository.  So I closed the pull request.

8. Deploying the Winget Apps to your devices with the Company Portal app

Just like my blog about the Win32 PowerShell express and my blog about why you need to start loving the Company Portal App. You can create all the installation packages from scratch and upload them to Intune with PowerShell

Conclusion:

When all the problems with Winget are resolved you will need to start converting your Powershell/Chocolatey Win32 apps to Winget Applications.

With the possibility to install applications in the system context with the use of Winget, you need to start moving over to Winget!

18 thoughts on “Cloudy: With a chance of Winget

  1. How do you deploy the .json configuration file to endpoints using Intune? It is per-user (%LOCALAPPDATA%) so would need to be deployed to every active user’s profile on every endpoint?

    1. Thanx for telling.. but what is not working?
      The creation of the task or executing the task. Just tested it (not with intune) but with a normal powershell system powershell and the scheduler is created. I am using this method for lots of stuff
      So how can I help you?

      1. Thanks for the quick reply! So the script does in fact create the task but when run nothing actually happens. Nothing Updates when trying to check winget list afterwards. I have tried using system as well as the logged in user to run it as.

        1. I noticed the same.. I already updated the blog… Somehow you need to add this to the script –accept-source-agreements

  2. FYI: Needed to change the winget upgrade query a bit for me to work just fine:

    winget upgrade –query –silent –force –accept-package-agreements –accept-source-agreements –all

    cheers! 🙂

  3. If you are lazy, just use the pre-built packaged Winget script to start deploying your apps within 1 min. I tested and it worked. Just too simple to believe.
    https://github.com/cliffzhu/IntuneWingetInstaller

    1. Hi..Did you also tested it after a wipe? as the script you showed, does exactly the same as I was showing. It will fetch the winget.exe and will install the required app you put in. But…. after a wipe, how are you making sure Winget.exe is on the device?

  4. Many thanks!
    In some cases the following code
    $ResolveWingetPath = Resolve-Path “C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_*_x64__8wekyb3d8bbwe”
    if ($ResolveWingetPath){
    $WingetPath = $ResolveWingetPath[-1].Path
    }
    is not working propertly because you may have e.g. such folders
    C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_1.0.32912.0_x64__8wekyb3d8bbwe
    C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_1.17.11601.0_x64__8wekyb3d8bbwe
    C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_1.4.3161.0_x64__8wekyb3d8bbwe
    And the last folder is not related to the last installed version of WinGet.
    Something like this might help
    $WinAppsPath = ‘C:\Program Files\WindowsApps\’
    $prefix = ‘Microsoft.DesktopAppInstaller_’
    $suffix = ‘_x64__8wekyb3d8bbwe’

    $SortedPath = “$WinAppsPath$prefix$(($arr | ForEach-Object {((Split-Path -Path $_ -Leaf).TrimStart($prefix).TrimEnd($suffix))} | Sort-Object {[Version]$_})[-1])$suffix”

    Best regards
    Oleksandr

    1. I am having this issue aswell. The script cant resolve the path to a valid path.
      Unfortunately you’r solution above didnt do the trick either. $SortedPath just resolves to C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller__x64__8wekyb3d8bbwe

      1. Hi, this also should work

        Set-Location -Path (‘{0}\WindowsApps\Microsoft.DesktopAppInstaller_*_x64__8wekyb3d8bbwe’ -f $env:ProgramW6432)

        In that folder there needs to be winget.exe, otherwise the installation itself isn’t preformed the way i did as it looks like

          1. Hi, are you using this on windows 10/11? Also is it wn existing device or a new enrollment

  5. I have installed the Desktop App Installer as a Win32 app but I am unable to get winget to install any app using the SYSTEM account.

    When I run the following script:

    $ResolveWingetPath = Resolve-Path “C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_*_x64__8wekyb3d8bbwe”
    if ($ResolveWingetPath){
    $WingetPath = $ResolveWingetPath[-1].Path
    }

    cd $wingetpath
    .\winget.exe install –id 9WZDNCRFJ3TJ –exact –silent –source msstore –accept-package-agreements –accept-source-agreements

    … I get the following error message (after the ‘verifying/requesting package acquisition’ step):

    An unexpected error occurred while executing the command:
    0x80070520 : A specified logon session does not exist. It may already have been terminated.

    Any ideas?

  6. I am getting below error while running any winget command from admin powershell.

    PS C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_1.18.2691.0_x64__8wekyb3d8bbwe> .\winget.exe –version
    Program ‘winget.exe’ failed to run: Access is deniedAt line:1 char:1
    + .\winget.exe –version
    + ~~~~~~~~~~~~~~~~~~~~~~.
    At line:1 char:1
    + .\winget.exe –version
    + ~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : ResourceUnavailable: (:) [], ApplicationFailedException
    + FullyQualifiedErrorId : NativeCommandFailed

Leave a Reply

Your email address will not be published.

  +  85  =  92