Running Winget from another admin account than the one actually logged in

Hi everyone!

As we are now using Ninja to serve our customers and their third-party apps list is kinda poor for now, we are trying to make Winget work, but it is proving quite difficult so far.

There is a thread about it in the Ninja Dojo: https://ninjarmm.zendesk.com/hc/fr/community/posts/4420149880973-Winget

Basically, we have a script that creates a new admin account on each workstation (this part runs fine), and then we have another script that periodically launches winget to upgrade the apps.

It only works if we use the “current logged in user” option. If we use another admin account, it says that winget command is not recognized, even if we open the powershell and type the command ourselves.

PATH is okay, and the command works fine when we log into the account directly on the computer, so we don’t know what could be missing…

Has anyone ran into this issue too?

I’ve run into a similar issue when running the Winget command from a PowerShell script run as the ‘system’ account. I can’t see the post you linked to as you have to have login I have worked around it and can explain in more detail, but I don’t have time right now. I’ll try and remember to update this post this evening.

I don’t know if this is related to your issue or not, but the issue when running it as ‘system’ is because it doesn’t actually exist, or at least not as ‘Winget’. As Winget is a modern Windows App (urgh), the executable is located under the running users local profile, but in the case of ‘system’ there is no such thing as a profile, so Winget cannot be called. However, Winget is actually a program called ‘AppInstallerCLI.exe’ which resides under ‘C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_*_x64__8wekyb3d8bbwe’ (where * is a version number) and can only be run as ‘system’. So to run as the ‘system’ account you simply need to call this in place of Winget. Below is an example script I’ve written to show you how you can work around this issue.

<#
  .SYNOPSIS
  A basic script to demonstrate Winget

  .DESCRIPTION
  A basic script to demonstrate Winget running as either a logged on user or as the 'System' user.

  Use with PSExec https://docs.microsoft.com/en-gb/sysinternals/downloads/psexec to see it running as the 'System' user

  .INPUTS
  None.

  .OUTPUTS
  When run with '-debug', a debug log file will be created under the current users 'temp' folder, as well as details being output to the console. The log file is useful if running the script as 'system' on a scheduled task etc. as obviously you can not see the console output. 

  .EXAMPLE
  PS> .\wingetExample.ps1

  .EXAMPLE
  PS> .\wingetExample.ps1 -debug
#>

[CmdletBinding()]
param()

#Editable Variables
$excludedPackages = @("Microsoft.Office", "Microsoft.dotnet", "7zip.7zip") #Skip these packages as they cause issues when upgrading
$logFile = $env:TEMP + "\Winget_log.txt"

#Variables
$Head = 0
$packageCount = 0
$runningUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name

#Function to write debug information to a log file
function debugMessage($message) {
    if ($DebugPreference -eq 'Continue') {
        add-content -Encoding ASCII $logFile ("$(get-date -f dd-MM-yyyy_hh:mm:ss) $message")
    }
    Write-Debug ("$message")
}

#Wipe previous log file if it exists
if (Test-Path -Path $logFile -PathType Leaf) {
    Remove-Item $logFile
}

debugMessage -message "Running as $runningUser"

#Get a list of packages to upgrade
if ($runningUser -eq "NT Authority\system") {
    $AppPath = Resolve-Path "C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_*_x64__8wekyb3d8bbwe"
    set-location -path $AppPath
    ((.\AppInstallerCLI.exe upgrade --accept-source-agreements | Format-Table -AutoSize) | Out-String).Split([Environment]::NewLine, [System.StringSplitOptions]::RemoveEmptyEntries) > $null #Run this to accept agreements, doesn't work as one command for 'upgrade'
    $Winget_Upgrade_Search = ((.\AppInstallerCLI.exe upgrade | Format-Table -AutoSize) | Out-String).Split([Environment]::NewLine, [System.StringSplitOptions]::RemoveEmptyEntries)
}
else {
    ((winget upgrade --accept-source-agreements | Format-Table -AutoSize) | Out-String).Split([Environment]::NewLine, [System.StringSplitOptions]::RemoveEmptyEntries) > $null
    $Winget_Upgrade_Search = ((winget upgrade | Format-Table -AutoSize) | Out-String).Split([Environment]::NewLine, [System.StringSplitOptions]::RemoveEmptyEntries)
}

#If we have packages to upgrade convert the list to a PowerShell object so we can work with items more easliy
If ($Winget_Upgrade_Search[$Winget_Upgrade_Search.Count - 1] -like "*upgrades available*") { 

    #Get the header details
    $Winget_Upgrade_Search | ForEach-Object { 

        If ($_ -like "------------------------------*") { 
            $Head = ($Winget_Upgrade_Search.IndexOf($_)) 
        }
    }
    $Winget_Header = $Winget_Upgrade_Search[($Head) - 1] -split "\s{2,}"

    $Winget_Upgrade_App_List = @()
    $Winget_Upgrade_Search[($Head + 1)..($Winget_Upgrade_Search.Count + 1)] | ForEach-Object {
        
        If ($_ -notlike "*upgrades available*") { 

            $results = $_ | Select-String "([^\s]+)" -AllMatches

            $Splits = @('', '', '', '')
            $Splits[3] = $results.Matches[$results.Matches.Length - 2]
            $Splits[2] = $results.Matches[$results.Matches.Length - 3]
            $Splits[1] = $results.Matches[$results.Matches.Length - 4]

            for ($i = 0; $i -lt ($results.Matches.Length - 4); $i++) {
                if ($i -ne ($results.Matches.Length - 5)) {
                    $Splits[0] += $results.Matches[$i].Value + " "
                }
                else {
                    $Splits[0] += $results.Matches[$i].Value
                }
            }

            $Stack = new-object psobject
            $Stack | Add-Member -membertype noteproperty -name "$($Winget_Header[0])" -Value $($Splits[0])
            $Stack | Add-Member -membertype noteproperty -name "$($Winget_Header[1])" -Value $($Splits[1])
            $Stack | Add-Member -membertype noteproperty -name "$($Winget_Header[2])" -Value $($Splits[2])
            $Stack | Add-Member -membertype noteproperty -name "$($Winget_Header[3])" -Value $($Splits[3])
            $Winget_Upgrade_App_List += $Stack
        }
    }
}

foreach ($item in $Winget_Upgrade_App_List) {
   
    if (!$excludedPackages.Contains($item.Id.Value)) {
        debugMessage -message "Upgrade available for $($item.Name), from $($item.Version.Value), to $($item.Available.Value)"
        $packageCount ++
    }
    else {
        debugMessage -message "Skipping $($item.Name) as it is on an exclusion list"
    }
}

debugMessage -message "$packageCount with upgrades available"

Thank you very much for your insights, I will investigate as soon as I can :slight_smile:

Seems like something MS could simplify a little bit though!

Yes it is a bit odd. I get the feeling Winget was a program developed on the side and someone thought ‘hey this is great let’s release it!’ Before they put any real thought into it.

1 Like

I will have a look at Chocolatey as an alternative ! It seems like their paid version is able to sync the list of programs already installed and take over their updates management. They also have a dashboard, which will be pretty handy, and prices are very affordable ! (provided they don’t have a minimum number of licenses to order)

One thing that I don’t know is if it is quite easy to add up licenses directly from their dashboard, without the need to get in touch with a sales rep first !

I looked at chocolatey and if you can get the budget (min 100 licenses) then it’s probably far far better, sadly I couldn’t.

Ouch…100 licences upfront…I don’t have that many workstations to manage for the moment.

It seems kinda hard to find affordable solutions to provide interesting packages when you’re starting small !

Hi again :slight_smile:

We finally decided to let go of Winget and give PatchMyPC a shot, and it seems like a good trade so far !

We just downloaded the EXE and put it in a folder in c:\ (which will be the same for all workstations), and we created a simple script on Ninja that launches the proper command (basically the name of the EXE with /s ).

After all this, the software runs smootly at set intervals (even as System) and does its job :slight_smile:

Ninja partners with Ivanti for the third-party side of things, but I fail to understand why they can’t have a repo as thorough as winget or chocolatey have…

PatchMyPC seems to work a little bit differently, as they download the executables directly from the official link.