Update your software using WinGet and PowerShell

Not all software comes with built-in update support or notifications. And even if it does, it takes time, and it’s always a good idea to automate things, of course 🙂 In this blog post, I will show you a short PowerShell function that will update your software if it’s WinGet compatible. (More and more software is added to its database every week)

What is WinGet?

“The winget command line tool enables users to discover, install, upgrade, remove and configure applications on Windows 10 and Windows 11 computers. This tool is the client interface to the Windows Package Manager service.” You can find a list of software that you can install or update using WinGet here

How the script works

Instead of typing a lot of command-line parameters for WinGet.exe, this PowerShell function uses the parameters that will update the software without any intervention on your Windows machine. Personally, I use the 1.4 version, and it will update it to that version if needed. (Version 1.3 is standard, but 1.4 has more features, but it’s still in preview.)

Running the script

You can save the script in c:\scripts as Start-WinGetUpdate.ps1, for example, and add it to your PowerShell profile for easy access by:

- notepad $profile
- Add ". c:\scripts\Start-WinGetUpdate.ps1"
- Quit/Save and start a new PowerShell session

It will always check for the installed version of WinGet, but you can skip that by using the -SkipVersionCheck parameter. The example below will check for the installed version (And download and install the 1.4 version if needed) and update NotePad++ and WinSCP to the latest version.

The script

Below is the script. Download/save it to your local system. Shoutout to Robin Stolpe, who did a big update/refinement on this! (https://github.com/rstolpe)

function Start-WinGetUpdate {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $false, HelpMessage = "Decide if you want to skip the WinGet version check, default it set to false")]
        [switch]$SkipVersionCheck = $false
    )

    #Check if script was started as Administrator
    if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]"Administrator")) {
        Write-Error ("{0} needs admin privileges, exiting now...." -f $MyInvocation.MyCommand)
        break
    }

    # =================================
    #         Static Variables
    # =================================
    #
    # GitHub url for the latest release
    [string]$GitHubUrl = "https://api.github.com/repos/microsoft/winget-cli/releases/latest"

    # The headers and API version for the GitHub API
    [hashtable]$GithubHeaders = @{
        "Accept"               = "application/vnd.github.v3+json"
        "X-GitHub-Api-Version" = "2022-11-28"
    }
    #

    # =================================
    #     Collecting some data
    # =================================
    #
    # Checks if WinGet is installed and if it's installed it will collect the current installed version of WinGet
    [version]$CheckWinGet = $(try { (Get-AppxPackage -Name Microsoft.DesktopAppInstaller).version } catch { $Null })

    <## Checking what architecture your running
    # To Install visualcredist use vc_redist.x64.exe /install /quiet /norestart
    # Now we also need to verify that's the latest version and then download and install it if the latest version is not installed
    # When this is added no need to install Microsoft.VCLibs as it's included in the VisualCRedist
    # Don't have the time for it now but this will be added later#>

    $Architecture = $(Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -ExpandProperty SystemType)
    switch ($Architecture) {
        "x64-based PC" {
            [string]$VisualCRedistUrl = "https://aka.ms/vs/17/release/vc_redist.x64.exe"
            [string]$VCLibsUrl = "https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx"
            [string]$Arch = "x64"
        }
        "ARM64-based PC" {
            [string]$VisualCRedistUrl = "https://aka.ms/vs/17/release/vc_redist.arm64.exe"
            [string]$VCLibsUrl = "https://aka.ms/Microsoft.VCLibs.arm64.14.00.Desktop.appx"
            [string]$Arch = "arm64"
        }
        "x86-based PC" {
            [string]$VisualCRedistUrl = "https://aka.ms/vs/17/release/vc_redist.x86.exe"
            [string]$VCLibsUrl = "https://aka.ms/Microsoft.VCLibs.x86.14.00.Desktop.appx"
            [string]$Arch = "arm64"
        }
        default {
            Write-Error "Your running a unsupported architecture, exiting now..."
            break
        }
    }

    # Checking if Microsoft.VCLibs is installed
    $CheckVCLibs = $(Get-AppxPackage -Name "Microsoft.VCLibs.140.00" -AllUsers | Where-Object { $_.Architecture -eq $Arch })
    #
    $VCLibsOutFile = "$env:TEMP\Microsoft.VCLibs.140.00.$($Arch).appx"

    # Checking if it's a newer version of WinGet to download and install if the user has used the -SkipVersionCheck switch.
    # If WinGet is not installed this section will still run to install WinGet.
    if ($SkipVersionCheck -eq $false -or $null -eq $CheckWinGet) {
        if ($null -eq $CheckWinGet) {
            Write-Output = "WinGet is not installed, downloading and installing WinGet..."
        }
        else {
            Write-Output = "Checking if it's any newer version of WinGet to download and install..."
        }

        # Collecting information from GitHub regarding latest version of WinGet
        try {
            if ($PSVersionTable.PSVersion.Major -ge 7) {
                [System.Object]$GithubInfoRestData = Invoke-RestMethod -Uri $GitHubUrl -Method Get -Headers $GithubHeaders -TimeoutSec 10 -HttpVersion 3.0 | Select-Object -Property assets, tag_name
            }
            else {
                [System.Object]$GithubInfoRestData = Invoke-RestMethod -Uri $GitHubUrl -Method Get -Headers $GithubHeaders -TimeoutSec 10 | Select-Object -Property assets, tag_name
            }
            [string]$latestVersion = $GithubInfoRestData.tag_name.Substring(1)

            [System.Object]$GitHubInfo = [PSCustomObject]@{
                Tag         = $latestVersion
                DownloadUrl = $GithubInfoRestData.assets | where-object { $_.name -like "*.msixbundle" } | Select-Object -ExpandProperty browser_download_url
                OutFile     = "$env:TEMP\WinGet_$($latestVersion).msixbundle"
            }
        }
        catch {
            Write-Error @"
   "Message: "$($_.Exception.Message)`n
   "Error Line: "$($_.InvocationInfo.Line)`n
"@
            break
        }

        # Checking if the installed version of WinGet are the same as the latest version of WinGet
        if ($CheckWinGet -le $GitHubInfo.Tag) {
            Write-Output "WinGet has a newer version $($GitHubInfo.Tag), downloading and installing it..."
            Invoke-WebRequest -UseBasicParsing -Uri $GitHubInfo.DownloadUrl -OutFile $GitHubInfo.OutFile

            Write-Output "Installing version $($GitHubInfo.Tag) of WinGet..."
            Add-AppxPackage $($GitHubInfo.OutFile)
        }
        else {
            Write-OutPut "Your already on the latest version of WinGet $($CheckWinGet), no need to update."
        }
    }

    # If Microsoft.VCLibs is not installed it will download and install it
    if ($null -eq $CheckVCLibs) {
        try {
            Write-Output "Microsoft.VCLibs is not installed, downloading and installing it now..."
            Invoke-WebRequest -UseBasicParsing -Uri $VCLibsUrl -OutFile $VCLibsOutFile

            Add-AppxPackage $VCLibsOutFile
        }
        catch {
            Write-Error "Something went wrong when trying to install Microsoft.VCLibs..."
            Write-Error @"
   "Message: "$($_.Exception.Message)`n
   "Error Line: "$($_.InvocationInfo.Line)`n
"@
            break
        }
    }

    # Starts to check if you have any softwares that needs to be updated
    Write-OutPut "Checks if any software needs to be updated"
    try {
        WinGet.exe upgrade --all --silent --force --accept-source-agreements --disable-interactivity --include-unknown
        Write-Output "Everything is now completed, you can close this window"
    }
    catch {
        Write-Error @"
   "Message: "$($_.Exception.Message)`n
   "Error Line: "$($_.InvocationInfo.Line)`n
"@
    }

}

Download the script(s) from GitHub here

11 thoughts on “Update your software using WinGet and PowerShell

  1. Thanks for the share. Be aware that if Notepad++ is open at the time of update, it appears to have updated, but the .exe is still the old version. That’s at least using the win32 Intune packaging method.

    So I would recommend including a way to obtain ensure that notepad++ is closed at the point of the update.

    • Not sure if killing the programs is a good idea, you will start this manually and I could put a message on-screen to close open applications in the future… I’ll think about it 🤔

      • I agree, that’s a good point. That’s where PowerShell App Deployment Kit still surpasses winget in my opinion. With PSADT and serviceui.exe, you can still interact with end user to close the open application, but still achieve an installation as the system account.

        However, using PSADT can be a bit too much to install something like Notepad ++. Are you aware of a way to prompt the user to gracefully close the application using the new winget method?

    • I’ve been working on a similar way to automate third party software using winget, and it’s quite effective, I’ve used a global exclusions list to exclude thing like Java, because it breaks things (you know what I mean 😉 ).

      My next “todo” is look into using custom repor’s for bespoke stuff like agents etc

      Regards

      RichE

  2. Yes, didn’t play around with that enough yet but it is a better option. Winget doesn’t do that for you (yet, who knows) but most installers do prompt you to close applications. Winget does have a –force option that I use, but I guess it depends on the application if it will just close or ask you to Save… Notepad always does that, it can stop reboots 😛

    • It’s the year that tools remain utterly useful if they’re well written and simple to use for certain situations where they’re all you need, regardless of how long ago they were created.

  3. Pingback: Intune Newsletter - 13th January 2023 - Andrew Taylor

  4. Pingback: PowerShell is fun :)PowerShell Profile

Leave a Reply to Harm VeenstraCancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.