Winget: Notes from the Field, Avoid Mistakes

Making Winget Work for You

As an administrator, I’m certain you would like to ensure a silent installation of the software so there isn’t any prompt for the end users. You can obtain the switches for each individual command of Winget by including “-?” after the command you are trying to evaluate. Here are some worthy examples:

  • Winget List -?
  • Winget Install -?
  • Winget Search -?
  • Winget Upgrade -?
  • Winget Uninstall -?

From a System’s Perspective 🤖

First thing’s first. One common issue I see online is that admins are having trouble kicking off Winget remotely with their MDM platforms such as Configuration Manager & Intune. Usually, they are able to get everything working in a terminal, VScode, or in PowerShell ISE, but can’t get it work in a deployment. That would be because Winget will create an environment variable for user accounts, but the built in NT System account cannot locate “Winget.exe” to call the CLI tool without instruction. Simply put, you need to navigate to the location of where the Winget.exe is before calling the commands. I would recommend always using this as a prefix to your Winget commands:

$WinGetResolve = Resolve-Path "C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_*_x64__8wekyb3d8bbwe\winget.exe"
$WinGetPathExe = $WinGetResolve[-1].Path
$WinGetPath = Split-Path -Path $WinGetPathExe -Parent
set-location $WinGetPath
.\winget.exe install -e --id Zoom.Zoom --accept-source-agreements --accept-package-agreements --scope machine

This will navigate to where Winget resides and call the install command as you would in a terminal. This Example is a good snippet for a bare test situation.

If you are needing your script to return from where it was initiated, I would suggest using a “Push/Pop Directory” method instead of a set-location.

Set-Location Results: (containing the variable from the previous snippet)

PS C:\> set-location $WinGetPath
PS C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_1.22.10861.0_x64__8wekyb3d8bbwe>

Push/Pop Location Method Result:

PS C:\SomeWhere\Else> Push-location $WinGetPath
PS C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_1.22.10861.0_x64__8wekyb3d8bbwe> $DoSomething
PS C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_1.22.10861.0_x64__8wekyb3d8bbwe> Pop-Location
PS C:\SomeWhere\Else>

Can’t I Just Use “Start-Process” Instead?

You can, and it will actually work for the most part, however You will likely run into some issues along the way because “Start-Process” with an “-ArgumentList” will typically call the .Exe and force a new terminal to open to perform the process. Usually pushing the command to Session ID #1, making the process now visible to any users logged on. You could also use “-NoNewWindow” as a parameter, but then you can pass the output to a variable for version comparison or obtain extra transcript information. Long term, I would plan to use the location of Winget.exe and performing the actions within the script instead of the “Start-Process” for more options down the line. 😁

Silent Installations, Quick and Simple

To make the installations use their silent install methods, you simply just need to add one of the two switches:

  • -h
  • –silent

This will allow the program to perform the publisher’s intended method they specify in their manifests for silent installation. Here are two quick examples:

Winget install Zoom.Zoom -h
Winget install Zoom.Zoom --silent

Be Exclusive, No Room for Error

My particular habit is to always include the “-e” & “–id” switches. This will only apply the exact Winget ID that you are working with. This also includes case sensitivity.This is to ensure you only working with the application/ID you specified with no room for ambiguity.

Winget install -e --id Zoom.Zoom

Avoid Additional Prompts, Get Ahead of Hiccups

Very Seldom will you actually need to perform any agreements for installations. One method to make sure everything you need is in place before using a script to perform the action will be to include the following switches:

  • –accept-package-agreements
  • –accept-source-agreements

Here is an example to ensure no prompts will cause any pauses from an unattended deployment:

Winget install Zoom.Zoom --accept-package-agreements --accept-source-agreements

User VS System Installation, Details Matter!

This is a fun topic in my opinion because each application can vary drastically in behavior here. I will make a post to cover “Handling Profile-Based Software Updates”, as it can be a whole post in and of itself. One detail to share here is to take note of the “–scope” switch this will allow you to see details for software within user profiles and software that’s installed for the entire system. There are cases where you will even see different types of installers. Here is an example of a Zoom installation for the user scope and machine scope:

C:\>Winget install Zoom.Zoom --scope user

Found Zoom [Zoom.Zoom] Version 5.17.11.34827
This application is licensed to you by its owner.
Microsoft is not responsible for, nor does it grant any licenses to, third-party packages.
Downloading https://zoom.us/client/5.17.11.34827/ZoomInstallerFull.exe?archType=x64
  ██████████████████████████████  86.7 MB / 86.7 MB
Successfully verified installer hash
Starting package install...
Successfully installed

Take note of the size of the download, along with the URL. This type of installation performs as an .EXE installation.

C:\>Winget install Zoom.Zoom --scope machine

Found Zoom [Zoom.Zoom] Version 5.17.11.34827
This application is licensed to you by its owner.
Microsoft is not responsible for, nor does it grant any licenses to, third-party packages.
Downloading https://zoom.us/client/5.17.11.34827/ZoomInstallerFull.msi?archType=x64
  ██████████████████████████████   107 MB /  107 MB
Successfully verified installer hash
Starting package install...
Successfully installed

See the change? Installing the software for the whole system will actually change the installer type. This one will perform as an .MSI installation.

You can also combine these switches with the “Show” command to get ahead of what to expect. Feel free to use the show command to get information about packages, there is no harm with this one, and it will not perform any installations etc. 😁

C:\>Winget Show Zoom.Zoom --scope machine
....(skipping a bunch of other data)...
Installer:
  Installer Type: msi
  Installer Url: https://zoom.us/client/5.17.11.34827/ZoomInstallerFull.msi?archType=x64
  Installer SHA256: ba6bb5a6d7b8faa6e9c28c3dc14b66b9165328ce0648e31a6de748f594760f90

Putting It All Together, A Template to Run With!

I will make it quick and simple. You can use this template that ties all the conditions mentioned in this post together. Just replace the Winget ID with any application you are trying to install and let it rip!

$WingetID = "Zoom.Zoom"
$WinGetResolve = Resolve-Path "C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_*_x64__8wekyb3d8bbwe\winget.exe"
$WinGetPathExe = $WinGetResolve[-1].Path
$WinGetPath = Split-Path -Path $WinGetPathExe -Parent
set-location $WinGetPath
.\winget.exe install -e --id $WingetID -h --accept-package-agreements --accept-source-agreements --scope machine

I Hope This Has Helped!

I do plan to cover a few more scenarios in other post. Especially for profile-based installations and how to update the software for users with a System Privilege Deployment. I personally recommend using the machine scope where you can. With this information put together, I hope this can serve as a start to automating Winget in your environment. 😁 – Hope to see you again!

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top