Closed GraphicHealer closed 5 months ago
Hey there. I fixed the -DebugMode
in the bug report template. The script used to use that but as you found just uses -Debug
.
I spun up a VM with Windows 11 Pro on it and used psexec.exe
to test with the SYSTEM user account. Although winget is installed, I experienced the same issue when the "registering" step appeared.
Add-AppxPackage : Deployment failed with HRESULT: 0x80073CF9, Install failed. Please contact your software vendor.
(Exception from HRESULT: 0x80073CF9)
Deployment Register operation rejected on package Microsoft.DesktopAppInstaller_2024.423.111.0_neutral_~_8wekyb3d8bbwe
from: AppxBundleManifest.xml install request because the Local System account is not allowed to perform this operation.
Since winget is included in Windows 11 now, I was still able to use winget, even after running this installation. Running C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_1.22.11132.0_x64__8wekyb3d8bbwe\winget.exe
actually worked for me.
I then repeated the test on a Windows 10 Pro VM, and had the same error. This time, though, since winget isn't included in Windows 10, I was able to run winget, but it does not output anything. That means that even if we resolve this error, winget won't run anyway. Unfortunately this appears to be a Microsoft issue.
I then tried installing it as an administrator user, not SYSTEM, and it worked fine. This of course, just confirms that the SYSTEM user is not supported. The "registering" step is the last step of the process, and it only runs if PowerShell can't run winget already.
Unfortunately it's not something we can change in the script to fix it. It's a Microsoft issue with winget-cli itself. winget-cli does not support the SYSTEM account. SYSTEM account usage for winget is discussed in this issue. Interestingly enough, other IT admins have mentioned that they too want to use their RMM for winget deployment but ran into similar issues when installing.
Here are the instructions from Microsoft on how to install winget from the command line. If you run these using the SYSTEM account you will find the same bug is present.
https://learn.microsoft.com/en-us/windows/package-manager/winget/#install-winget
I am still glad you reported this, because I can improve the error handling and also detect SYSTEM accounts when used and prevent the script from running.
I'm actually a former sysadmin for an MSP, so definitely feel the frustration caused by this. In many of the RMMs I've used in the past, I was able to specify which user account to run the script under. That may be the best solution here.
Well shoot. I was hoping there may have been a workaround for this, but apparently not. I guess I'll just have to wait and see if Microsoft will ever even give the system account permission to use WinGet. I feel like they need to, considering the scope of what WinGet is supposed to do.
They would be leaving a lot of possible users and use cases on the table because of this.
Thanks for the quick response though! And the info! I guess I'll just have to wait and see what happens.
I have heard that the RMM my job uses, NinjaOne, is supposedly working on implementing WinGet into their systems for managing and updating programs automatically. I'm really hoping they can get it to work properly, as it would drastically speed up and simplify our current processes.
I am currently having to use chocolatey, and I keep having to tweak my scripts to fix issues that pop up here and there. Let's hope WinGet will get it working!
Again, thank you. G
Oh Shoot! @asheroto please dont completly block the script, just the registration step that fails.
The script still works when run as SYSTEM. WinGet is installed, SYSTEM just can't access it directly. I still would like to install WInGet with our RMM under the SYSTEM user, I will just have to run WinGet itself differently once installed. Possibly under the Local Admin user.
Thanks!
I haven't used NinjaOne, but a lot of RMMs will let you use another account. If not, you might be able to use runas.exe
or even create a scheduled task and specify the user account and run it.
Oddly enough, I just tested it again on Windows 10 and was able to run winget after all even under the SYSTEM account. So yeah, I'll leave as-is in regards to installation. Instead of blocking, I'll just add in a notice for that particular error that it may not work with the SYSTEM account. 😊
Here's what version 4.0.5 will look like in this case.
Take a look at this, not sure if would work
https://github.com/microsoft/winget-cli/discussions/962#discussioncomment-4440131
Take a look at this, not sure if would work
https://github.com/microsoft/winget-cli/discussions/962#discussioncomment-4440131
Lol! That is almost exactly what I'm doing!
I actually got it to work pretty much perfectly, I ended up writing my own script to install it for the moment.
Worked with a couple of other people in the NinjaOne community to figure it all out.
Thanks!
Actually, I'm going to put in another bug report for your program, cuz I did find one other issue that your system apparently doesn't account for.
Hmm, so weird. So if you use an alias and find the winget exe in WindowsApps then winget works fine with the system account? Might be able to incorporate that into the script if so.
I tried doing this, but sysget
still didn't work, at least on Windows 10...
$exeFilename = Get-ChildItem -Path "C:\Program Files\WindowsApps\" -Filter winget.exe -Recurse | ForEach-Object { $_.FullName }
New-Alias -Name sysget -Value $exeFilename
sysget upgrade --all -e --accept-package-agreements --accept-source-agreements
winget is actually installed though, just not usable under the system account, only the administrator account.
How did you get it to work?
@GraphicHealer did you only use Windows 11 or also try Windows 10?
Give me a little bit, and I can send you my script that I wrote.
Awesome, thank you! Once you send it I'll take a look and see if I can incorporate into this script. If so, I'll give you credit.
<# Winget-Install.ps1
Source for Some Functions: https://github.com/homotechsual/ninjaget/blob/dev/PS/NinjaGet.Functions.Installation.ps1
2023-11-22 - 0.0.2 - assembled and modified by David Szpunar - Initial version
DOWNLOAD: https://discord.com/channels/676451788395642880/1232432179141808228
2024-04-23 - 0.1.0 - Simplified and Modified by GraphicHealer - Initial Version
DESCRIPTION: Installs or updates WinGet.exe to the latest version. Intended to run as SYSTEM.
Use '-Force' to ignore version check.
-----
LICENSE: The MIT License (MIT)
Original Copyright 2023 Mikey O'Toole, modified by David Szpunar, Simplified and Modified by GraphicHealer.
#>
# Add -Force switch to force install
param(
[switch]$Force = $false
)
# Start Logging
Start-Transcript -Path (Ninja-Property-Get DefaultLogFile) -IncludeInvocationHeader -Append -Force
# Setup Error Codes
$Script:ExitCode = 0
# Function to get OS Information
function Get-OSInfo {
try {
# Get registry values
$registryValues = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
$releaseIdValue = $registryValues.ReleaseId
$displayVersionValue = $registryValues.DisplayVersion
$nameValue = $registryValues.ProductName
$editionIdValue = $registryValues.EditionId
# Strip out "Server" from the $editionIdValue if it exists
$editionIdValue = $editionIdValue -replace 'Server', ''
# Get OS details using Get-CimInstance because the registry key for Name is not always correct with Windows 11
$osDetails = Get-CimInstance -ClassName Win32_OperatingSystem
$nameValue = $osDetails.Caption
# Get architecture details of the OS (not the processor)
# Get only the numbers
$architecture = ($osDetails.OSArchitecture -replace '[^\d]').Trim()
# If 32-bit or 64-bit replace with x32 and x64
if ($architecture -eq '32') {
$architecture = 'x32'
} elseif ($architecture -eq '64') {
$architecture = 'x64'
}
# Get OS version details (as version object)
$versionValue = [System.Environment]::OSVersion.Version
# Determine product type
# Reference: https://learn.microsoft.com/en-us/dotnet/api/microsoft.powershell.commands.producttype?view=powershellsdk-1.1.0
if ($osDetails.ProductType -eq 1) {
$typeValue = 'Workstation'
} elseif ($osDetails.ProductType -eq 2 -or $osDetails.ProductType -eq 3) {
$typeValue = 'Server'
} else {
$typeValue = 'Unknown'
}
# Extract numerical value from Name
$numericVersion = ($nameValue -replace '[^\d]').Trim()
# Create and return custom object with the required properties
$result = [PSCustomObject]@{
ReleaseId = $releaseIdValue
DisplayVersion = $displayVersionValue
Name = $nameValue
Type = $typeValue
NumericVersion = $numericVersion
EditionId = $editionIdValue
Version = $versionValue
Architecture = $architecture
}
return $result
} catch {
Write-Error "Unable to get OS version details.`nError: $_"
exit 1
}
}
# Function to Install WinGet (modified)
function Install-WinGet {
# Install VCPPRedist Requirements
$Visual2019 = 'Microsoft Visual C++ 2015-2019 Redistributable*'
$Visual2022 = 'Microsoft Visual C++ 2015-2022 Redistributable*'
$VCPPInstalled = Get-Item @('HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*') | Where-Object {
$_.GetValue('DisplayName') -like $Visual2019 -or $_.GetValue('DisplayName') -like $Visual2022
}
if ([System.Environment]::Is64BitOperatingSystem) {
$OSArch = 'x64'
} else {
$OSArch = 'x86'
}
if (!($VCPPInstalled)) {
Write-Output 'VCPPRedist Requirements Not Installed. Installing...'
$VCPPRedistURL = ('https://aka.ms/vs/17/release/vc_redist.{0}.exe' -f $OSArch)
$VCPPRedistFileName = [Uri]$VCPPRedistURL | Select-Object -ExpandProperty Segments | Select-Object -Last 1
$WebClient = New-Object System.Net.WebClient
$VCPPRedistDownloadPath = "$ENV:SystemRoot\Temp\VCPRedist"
if (!(Test-Path -Path $VCPPRedistDownloadPath)) {
$null = New-Item -Path $VCPPRedistDownloadPath -ItemType Directory -Force
}
$VCPPRedistDownloadFile = "$VCPPRedistDownloadPath\$VCPPRedistFileName"
$WebClient.DownloadFile($VCPPRedistURL, $VCPPRedistDownloadFile)
try {
Start-Process -FilePath $VCPPRedistDownloadFile -ArgumentList '/quiet', '/norestart' -Wait -ErrorAction Stop | Out-Null
Write-Output 'VCPPRedist Requirements Installed.'
} catch {
Write-Output 'Failed to install VCPPRedist Requirements!'
$Script:ExitCode = 1
return
} finally {
Remove-Item -Path $VCPPRedistDownloadPath -Recurse -Force -ErrorAction SilentlyContinue
}
} else {
Write-Output 'VCPPRedist Requirements Installed.'
}
# Find Latest Download
$LatestRelease = (Invoke-RestMethod -Uri 'https://api.github.com/repos/microsoft/winget-cli/releases/latest' -Method Get)
$LatestAsset = ($LatestRelease).assets | Where-Object { $_.name.EndsWith('.msixbundle') }
[version]$LatestVersion = $LatestRelease.tag_name.TrimStart('v')
if ((Find-WinGet)) {
[version]$CurrentVersion = (& (Find-WinGet) '-v').TrimStart('v')
} else {
[version]$CurrentVersion = 0
}
# Check if WinGet is Updated
if (!($CurrentVersion -lt $LatestVersion) -and !$Force) {
# Exit
Write-Output 'WinGet is up to date. Exiting.'
return
}
# Installing WinGet
Write-Output 'WinGet not installed or out of date. Installing/updating...'
# Download WinGet to Temp
$WebClient = New-Object System.Net.WebClient
$WinGetDownloadPath = Join-Path -Path "$ENV:SystemRoot\Temp" -ChildPath $LatestAsset.name
Write-Output "Downloading WinGet to $WinGetDownloadPath..."
$WebClient.DownloadFile($LatestAsset.browser_download_url, $WinGetDownloadPath)
# Download WinGet Source to Temp
$WinGetSourceDownloadPath = Join-Path -Path "$ENV:SystemRoot\Temp" -ChildPath 'source.msix'
Write-Output "Downloading WinGet to $WinGetSourceDownloadPath..."
Invoke-WebRequest -Uri 'https://cdn.winget.microsoft.com/cache/source.msix' -OutFile $WinGetSourceDownloadPath
$WinArch = (Get-OSInfo).Architecture
# Download VCLibs
$VCLibs_Path = Join-Path -Path "$ENV:SystemRoot\Temp" -ChildPath "Microsoft.VCLibs.${WinArch}.14.00.Desktop.appx"
$VCLibs_Url = "https://aka.ms/Microsoft.VCLibs.${WinArch}.14.00.Desktop.appx"
Write-Output 'Downloading VCLibs...'
Write-Debug "Downloading VCLibs from $VCLibs_Url to $VCLibs_Path`n`n"
Invoke-WebRequest -Uri $VCLibs_Url -OutFile $VCLibs_Path
# Download UI.Xaml
$UIXaml_Path = Join-Path -Path "$ENV:SystemRoot\Temp" -ChildPath "Microsoft.UI.Xaml.2.8.${WinArch}.appx"
$UIXaml_Url = "https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.8.6/Microsoft.UI.Xaml.2.8.${WinArch}.appx"
Write-Output 'Downloading UI.Xaml...'
Write-Debug "Downloading UI.Xaml from $UIXaml_Url to $UIXaml_Path`n"
Invoke-WebRequest -Uri $UIXaml_Url -OutFile $UIXaml_Path
# Install Downloaded WinGet
try {
Write-Output 'Installing WinGet...'
Add-AppxProvisionedPackage -Online -PackagePath $WinGetDownloadPath -DependencyPackagePath $UIXaml_Path, $VCLibs_Path -SkipLicense -ErrorAction Stop
Add-AppxProvisionedPackage -Online -PackagePath $WinGetSourceDownloadPath -SkipLicense -ErrorAction Stop
Write-Output 'WinGet installed.'
} catch {
Write-Output 'Failed to install WinGet!'
$Script:ExitCode = 1
} finally {
Remove-Item -Path $WinGetDownloadPath -Force -ErrorAction SilentlyContinue
}
return
}
# Function to Find the WinGet exe (Modified)
function Find-WinGet {
# Get the WinGet path (for use when running in SYSTEM context).
$WinGetPathToResolve = Join-Path -Path $ENV:ProgramFiles -ChildPath 'WindowsApps\Microsoft.DesktopAppInstaller_*_*__8wekyb3d8bbwe'
$ResolveWinGetPath = Resolve-Path -Path $WinGetPathToResolve | Sort-Object {
[version]($_.Path -replace '^[^\d]+_((\d+\.)*\d+)_.*', '$1')
}
if ($ResolveWinGetPath) {
# If we have multiple versions - use the latest.
$WinGetPath = $ResolveWinGetPath[-1].Path
}
# Get the User-Context WinGet exe location.
$WinGetExePath = Get-Command -Name winget.exe -CommandType Application -ErrorAction SilentlyContinue
# Select the correct WinGet exe
if (Test-Path -Path (Join-Path $WinGetPath 'winget.exe')) {
# Running in SYSTEM-Context.
$WinGet = Join-Path $WinGetPath 'winget.exe'
} elseif ($WinGetExePath) {
# Get User-Context if SYSTEM-Context not found.
$WinGet = $WinGetExePath.Path
} else {
Write-Output 'WinGet not Found!'
Stop-Transcript
exit 1
}
# Return WinGet Location
return $WinGet
}
# Test internet connection
$MaxNet = 15
Write-Output 'Checking Internet Connection...'
for ($i = 0; ($i -lt $MaxNet) -and !$net; $i++) {
$net = Test-Connection 1.1.1.1 -Count 1 -Delay 1 -Quiet
}
if (!$net) {
Write-Output 'No Internet Detected. Cancelling Installation.'
Stop-Transcript
exit 1
}
Write-Output 'Internet Detected, Continuing Installation.'
# Test OS Compatibility
$osVersion = Get-OSInfo
# If it's a workstation, make sure it is Windows 10+
if ($osVersion.Type -eq 'Workstation' -and $osVersion.NumericVersion -lt 10) {
Write-Error 'winget requires Windows 10 or later on workstations. Your version of Windows is not supported.'
Stop-Transcript
exit 0
}
# If it's a workstation with Windows 10, make sure it's version 1809 or greater
if ($osVersion.Type -eq 'Workstation' -and $osVersion.NumericVersion -eq 10 -and $osVersion.ReleaseId -lt 1809) {
Write-Error 'winget requires Windows 10 version 1809 or later on workstations. Please update Windows to a compatible version.'
Stop-Transcript
exit 0
}
# Run the Install/Update
Install-WinGet
& (Find-WinGet) 'list' '--accept-source-agreements'
Stop-Transcript
exit $Script:ExitCode
That's my script. It has a few calls to our RMM, (Ninja-Property-Get
) but other than that, it's pretty much standalone.
Thanks, I'll check it out.
Checklist
[X] I have performed the steps in the Troubleshooting section and confirm that the issue only occurs with this script.
[X] I have restarted my computer to confirm this is not a transitory problem.
[X] I have verified that I am using the latest version of winget-install.
[X] I have verified that this issue is NOT security related (please create security advisory if so).
[X] I have run the script with the
-DebugMode
argument and included the output belowWhat You Are Seeing?
I am running
winget-install
as the system user, and the system user is not allowed to run theAdd-AppxPackage
command. Also, WinGet will not run properly as the SYSTEM user after install, even if I run it directly from theC:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_1.22.11132.0_x64__8wekyb3d8bbwe
directory.System Details
Additional Context
I am trying to implement this into the RMM software I use at my workplace, so we can utilize WinGet as our package management solution.
Full Output With
-DebugMode