New client onboarding: 85 workstations needed standardized software stack. Chrome, Adobe Reader, 7-Zip, Zoom, Teams, and 12 other applications. Previous MSP manually installed via remote desktop - 45 minutes per machine. We needed silent deployment without user interaction.
Windows Package Manager (winget) is Microsoft's answer to apt-get and homebrew. Built into Windows 11, available for Windows 10.
The catch: winget doesn't work out of the box when scripts run as SYSTEM (how most RMM tools execute). We built a toolkit to solve this.
Downloads latest release from GitHub, installs for SYSTEM context execution.
[+] winget_setup.ps1GitHub
$ErrorActionPreference = 'Stop'
<#
██╗ ██╗███╗ ███╗███████╗██╗ ██╗ █████╗ ██╗ ██╗██╗ ██╗
██║ ██║████╗ ████║██╔════╝██║ ██║██╔══██╗██║ ██║██║ ██╔╝
██║ ██║██╔████╔██║█████╗ ███████║███████║██║ █╗ ██║█████╔╝
██║ ██║██║╚██╔╝██║██╔══╝ ██╔══██║██╔══██║██║███╗██║██╔═██╗
███████╗██║██║ ╚═╝ ██║███████╗██║ ██║██║ ██║╚███╔███╔╝██║ ██╗
╚══════╝╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═╝
================================================================================
SCRIPT : Winget Setup for RMM v1.0.3
AUTHOR : Limehawk.io
DATE : January 2026
USAGE : .\winget_setup.ps1
================================================================================
FILE : winget_setup.ps1
DESCRIPTION : Installs winget (Windows Package Manager) for RMM environments
--------------------------------------------------------------------------------
README
--------------------------------------------------------------------------------
PURPOSE
Installs winget (Windows Package Manager) on Windows systems using the
most reliable installation method. Optimized for RMM environments running
under SYSTEM context. Uses manual AppX installation method which is more
reliable than the Repair-WinGetPackageManager approach. Handles all OS
versions including Windows 10, 11, and Server 2019+.
DATA SOURCES & PRIORITY
1) Hardcoded script configuration (timeout, force reinstall)
2) GitHub API (microsoft/winget-cli releases)
3) System environment (OS version, architecture)
REQUIRED INPUTS
- $forceReinstall - Reinstall even if winget already exists (boolean)
- $downloadTimeout - Timeout for file downloads in seconds (integer)
- $skipServerCore - Skip installation on Server Core (boolean)
SETTINGS
- Uses alternate/manual AppX installation method (most reliable)
- Automatically detects SYSTEM context and adjusts behavior
- Installs for all users when possible
- Configures PATH environment variable automatically
- 120 second download timeout per file
BEHAVIOR
1. Validates configuration inputs
2. Checks OS compatibility and admin privileges
3. Detects if running as SYSTEM account
4. Checks if winget is already installed (skips if present unless forced)
5. Downloads winget dependencies from GitHub
6. Downloads winget AppX package from GitHub
7. Installs dependencies (VCLibs, UI.Xaml)
8. Installs winget AppX package
9. Configures PATH environment variable
10. Registers winget application
11. Verifies installation success
12. Cleans up temporary files
PREREQUISITES
- Administrator or SYSTEM privileges
- Windows 10 1809+ or Windows Server 2019+
- Internet connectivity to GitHub
- PowerShell 5.1 or higher
SECURITY NOTES
- No secrets or credentials required
- Downloads only from official Microsoft GitHub repository
- All temporary files are cleaned up after installation
- No sensitive information logged to console
ENDPOINTS
- https://api.github.com/repos/microsoft/winget-cli/releases/latest
- https://github.com/microsoft/winget-cli (release assets)
EXIT CODES
- 0 = Success - winget installed and verified
- 1 = Failure - installation failed or system incompatible
EXAMPLE RUN
[INFO] INPUT VALIDATION
==============================================================
Configuration validated successfully
Force Reinstall : No
Download Timeout: 120 seconds
[INFO] SYSTEM CHECK
==============================================================
OS Version : Windows 10 22H2
Architecture : x64
Admin Rights : Yes
Running Context : SYSTEM
Winget Status : Not installed
[RUN] DOWNLOAD DEPENDENCIES
==============================================================
Fetching latest release information...
Latest Version : v1.7.10861
Downloading dependencies package...
Downloaded : 15.2 MB
Extracting dependencies...
Extracted : VCLibs.140.00.UWPDesktop
Extracted : UI.Xaml.2.8
[RUN] DOWNLOAD WINGET
==============================================================
Downloading winget package...
Downloaded : 45.8 MB
Package verified
[RUN] INSTALL DEPENDENCIES
==============================================================
Installing VCLibs.140.00.UWPDesktop...
Installed successfully
Installing UI.Xaml.2.8...
Installed successfully
[RUN] INSTALL WINGET
==============================================================
Installing winget AppX package...
Installation successful
Configuring PATH environment variable...
PATH configured
Registering winget...
Registration complete
[INFO] VERIFICATION
==============================================================
Testing winget command...
Winget Version : v1.7.10861
Installation : Verified
[INFO] CLEANUP
==============================================================
Removing temporary files...
Cleanup complete
[OK] FINAL STATUS
==============================================================
Status : Success
Winget : Installed and working
[OK] SCRIPT COMPLETED
==============================================================
--------------------------------------------------------------------------------
CHANGELOG
--------------------------------------------------------------------------------
2026-01-19 v1.0.3 Updated to two-line ASCII console output style
2026-01-14 v1.0.2 Fixed header formatting for framework compliance
2025-12-23 v1.0.1 Updated to Limehawk Script Framework
2025-01-31 v1.0.0 Initial release - reliable winget installation for RMM
================================================================================
#>
Set-StrictMode -Version Latest
# ============================================================================
# HARDCODED INPUTS
# ============================================================================
$forceReinstall = $false # Reinstall even if winget already exists
$downloadTimeout = 120 # Timeout for file downloads in seconds
$skipServerCore = $false # Skip installation on Server Core
# ============================================================================
# INPUT VALIDATION
# ============================================================================
$errorOccurred = $false
$errorText = ""
Write-Host ""
Write-Host "[INFO] INPUT VALIDATION"
Write-Host "=============================================================="
if ($downloadTimeout -lt 30) {
$errorOccurred = $true
if ($errorText.Length -gt 0) { $errorText += "`n" }
$errorText += "- Download timeout must be at least 30 seconds"
}
if ($errorOccurred) {
Write-Host ""
Write-Host "[ERROR] VALIDATION FAILED"
Write-Host "=============================================================="
Write-Host "Input validation failed:"
Write-Host $errorText
Write-Host ""
exit 1
}
Write-Host "Configuration validated successfully"
Write-Host "Force Reinstall : $(if ($forceReinstall) { 'Yes' } else { 'No' })"
Write-Host "Download Timeout: $downloadTimeout seconds"
# ============================================================================
# SYSTEM CHECK
# ============================================================================
Write-Host ""
Write-Host "[INFO] SYSTEM CHECK"
Write-Host "=============================================================="
# Check if running as SYSTEM
$runAsSystem = $false
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent()
if ($currentUser.User.Value -eq "S-1-5-18") {
$runAsSystem = $true
}
# Check admin privileges (for non-SYSTEM accounts)
$isAdmin = ([Security.Principal.WindowsPrincipal]$currentUser).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin -and -not $runAsSystem) {
Write-Host ""
Write-Host "[ERROR] PRIVILEGES REQUIRED"
Write-Host "=============================================================="
Write-Host "Administrator or SYSTEM privileges required"
Write-Host "Please run this script as Administrator"
Write-Host ""
exit 1
}
# Get OS information
$osInfo = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion"
$osName = $osInfo.ProductName
$osBuild = $osInfo.CurrentBuild
$installationType = $osInfo.InstallationType
# Get architecture
$osArch = if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" }
# Get OS type
$osDetails = Get-CimInstance -ClassName Win32_OperatingSystem
$osType = if ($osDetails.ProductType -eq 1) { "Workstation" } else { "Server" }
Write-Host "OS Name : $osName"
Write-Host "OS Build : $osBuild"
Write-Host "Architecture : $osArch"
Write-Host "Installation : $installationType"
Write-Host "Admin Rights : $(if ($isAdmin) { 'Yes' } else { 'No' })"
Write-Host "Running Context : $(if ($runAsSystem) { 'SYSTEM' } else { 'User' })"
# Check OS compatibility
$osNumeric = ($osName -replace "[^\d]").Trim()
if ($osType -eq "Workstation" -and [int]$osBuild -lt 17763) {
Write-Host ""
Write-Host "[ERROR] INCOMPATIBLE OS VERSION"
Write-Host "=============================================================="
Write-Host "Windows 10 version 1809 or later required"
Write-Host "Your build: $osBuild"
Write-Host ""
exit 1
}
if ($osType -eq "Server" -and [int]$osBuild -lt 17763) {
Write-Host ""
Write-Host "[ERROR] INCOMPATIBLE OS VERSION"
Write-Host "=============================================================="
Write-Host "Windows Server 2019 or later required"
Write-Host "Your build: $osBuild"
Write-Host ""
exit 1
}
# Check for Server Core
if ($installationType -eq "Server Core" -and $skipServerCore) {
Write-Host ""
Write-Host "[WARN] SERVER CORE SKIPPED"
Write-Host "=============================================================="
Write-Host "Server Core installation detected and skipServerCore is enabled"
Write-Host "Installation skipped"
Write-Host ""
exit 1
}
# Check if winget already installed
Write-Host ""
Write-Host "Checking for existing winget installation..."
$wingetInstalled = $false
$wingetVersion = "Not installed"
try {
if ($runAsSystem) {
# For SYSTEM context, look for winget executable directly
$wingetPath = Resolve-Path "$env:ProgramFiles\WindowsApps\Microsoft.DesktopAppInstaller_*_x64__8wekyb3d8bbwe" -ErrorAction SilentlyContinue |
Sort-Object | Select-Object -Last 1
if ($wingetPath) {
$wingetExe = Join-Path $wingetPath.Path "winget.exe"
if (Test-Path $wingetExe) {
$wingetInstalled = $true
$versionOutput = & $wingetExe --version 2>&1
if ($versionOutput -match 'v([\d.]+)') {
$wingetVersion = $matches[1]
}
}
}
} else {
$wingetCmd = Get-Command winget -ErrorAction SilentlyContinue
if ($wingetCmd) {
$wingetInstalled = $true
$versionOutput = & winget --version 2>&1
if ($versionOutput -match 'v([\d.]+)') {
$wingetVersion = $matches[1]
}
}
}
} catch {
# Winget not found
}
Write-Host "Winget Status : $(if ($wingetInstalled) { 'Installed' } else { 'Not installed' })"
if ($wingetInstalled) {
Write-Host "Winget Version : $wingetVersion"
}
if ($wingetInstalled -and -not $forceReinstall -and -not $runAsSystem) {
Write-Host ""
Write-Host "[OK] FINAL STATUS"
Write-Host "=============================================================="
Write-Host "Status : Winget already installed"
Write-Host "Version : $wingetVersion"
Write-Host "Action : Skipped installation"
Write-Host ""
Write-Host "[OK] SCRIPT COMPLETED"
Write-Host "=============================================================="
exit 0
}
if ($wingetInstalled -and -not $forceReinstall -and $runAsSystem) {
Write-Host ""
Write-Host "Winget installed for SYSTEM, ensuring user registration..."
# Skip to registration section without reinstalling
$skipInstallation = $true
} else {
$skipInstallation = $false
}
if ($wingetInstalled -and $forceReinstall) {
Write-Host "Force reinstall enabled, proceeding with installation"
}
# ============================================================================
# DOWNLOAD AND INSTALL (skip if already installed for SYSTEM)
# ============================================================================
if (-not $skipInstallation) {
# ============================================================================
# DOWNLOAD DEPENDENCIES
# ============================================================================
Write-Host ""
Write-Host "[RUN] DOWNLOAD DEPENDENCIES"
Write-Host "=============================================================="
# Suppress progress bar for faster downloads
$ProgressPreference = 'SilentlyContinue'
Write-Host "Fetching latest release information from GitHub..."
try {
$releases = Invoke-RestMethod -Uri "https://api.github.com/repos/microsoft/winget-cli/releases" -TimeoutSec 30 -ErrorAction Stop
# Get latest non-prerelease version
$latestRelease = $releases | Where-Object { -not $_.prerelease } | Select-Object -First 1
$releaseVersion = $latestRelease.tag_name
Write-Host "Latest Version : $releaseVersion"
Write-Host "Published : $($latestRelease.published_at)"
} catch {
Write-Host ""
Write-Host "[ERROR] GITHUB API FAILED"
Write-Host "=============================================================="
Write-Host "Failed to fetch release information from GitHub"
Write-Host "Error: $($_.Exception.Message)"
Write-Host "Check internet connectivity and GitHub availability"
Write-Host ""
exit 1
}
# Create temp directory
$tempDir = Join-Path $env:TEMP "winget_install_$(Get-Random)"
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
Write-Host ""
Write-Host "Temp directory : $tempDir"
# Download dependencies package
Write-Host ""
Write-Host "Downloading dependencies package..."
$depsAsset = $latestRelease.assets | Where-Object { $_.name -match 'Dependencies\.zip' } | Select-Object -First 1
if (-not $depsAsset) {
Write-Host ""
Write-Host "[ERROR] MISSING ASSET"
Write-Host "=============================================================="
Write-Host "Dependencies package not found in release assets"
Write-Host ""
exit 1
}
$depsPath = Join-Path $tempDir "dependencies.zip"
try {
Invoke-WebRequest -Uri $depsAsset.browser_download_url -OutFile $depsPath -TimeoutSec $downloadTimeout -ErrorAction Stop
$fileSize = (Get-Item $depsPath).Length
Write-Host "Downloaded : $([math]::Round($fileSize/1MB, 1)) MB"
} catch {
Write-Host ""
Write-Host "[ERROR] DOWNLOAD FAILED"
Write-Host "=============================================================="
Write-Host "Failed to download dependencies package"
Write-Host "Error: $($_.Exception.Message)"
Write-Host ""
exit 1
}
# Extract dependencies for current architecture
Write-Host ""
Write-Host "Extracting dependencies for $osArch architecture..."
Add-Type -AssemblyName System.IO.Compression.FileSystem
$depsExtractPath = Join-Path $tempDir "deps"
New-Item -ItemType Directory -Path $depsExtractPath -Force | Out-Null
try {
$zip = [System.IO.Compression.ZipFile]::OpenRead($depsPath)
$archPattern = if ($osArch -eq "x64") { "x64" } else { "x86" }
$entries = $zip.Entries | Where-Object { $_.FullName -match "$archPattern.*\.appx$" }
foreach ($entry in $entries) {
$destPath = Join-Path $depsExtractPath $entry.Name
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $destPath, $true)
Write-Host "Extracted : $($entry.Name)"
}
$zip.Dispose()
} catch {
Write-Host ""
Write-Host "[ERROR] EXTRACTION FAILED"
Write-Host "=============================================================="
Write-Host "Failed to extract dependencies"
Write-Host "Error: $($_.Exception.Message)"
Write-Host ""
exit 1
}
# ============================================================================
# DOWNLOAD WINGET
# ============================================================================
Write-Host ""
Write-Host "[RUN] DOWNLOAD WINGET"
Write-Host "=============================================================="
Write-Host "Downloading winget package..."
$wingetAsset = $latestRelease.assets | Where-Object { $_.name -match '\.msixbundle$' } | Select-Object -First 1
if (-not $wingetAsset) {
Write-Host ""
Write-Host "[ERROR] MISSING ASSET"
Write-Host "=============================================================="
Write-Host "Winget package not found in release assets"
Write-Host ""
exit 1
}
$wingetPath = Join-Path $tempDir "winget.msixbundle"
try {
Invoke-WebRequest -Uri $wingetAsset.browser_download_url -OutFile $wingetPath -TimeoutSec $downloadTimeout -ErrorAction Stop
$fileSize = (Get-Item $wingetPath).Length
Write-Host "Downloaded : $([math]::Round($fileSize/1MB, 1)) MB"
Write-Host "Package verified"
} catch {
Write-Host ""
Write-Host "[ERROR] DOWNLOAD FAILED"
Write-Host "=============================================================="
Write-Host "Failed to download winget package"
Write-Host "Error: $($_.Exception.Message)"
Write-Host ""
exit 1
}
# Download license file
Write-Host ""
Write-Host "Downloading license file..."
$licenseAsset = $latestRelease.assets | Where-Object { $_.name -match 'License.*\.xml$' } | Select-Object -First 1
$licensePath = Join-Path $tempDir "license.xml"
if ($licenseAsset) {
try {
Invoke-WebRequest -Uri $licenseAsset.browser_download_url -OutFile $licensePath -TimeoutSec 30 -ErrorAction Stop
Write-Host "License downloaded"
} catch {
Write-Host "License download failed (non-critical): $($_.Exception.Message)"
}
}
# ============================================================================
# INSTALL DEPENDENCIES
# ============================================================================
Write-Host ""
Write-Host "[RUN] INSTALL DEPENDENCIES"
Write-Host "=============================================================="
$depFiles = Get-ChildItem -Path $depsExtractPath -Filter "*.appx"
foreach ($depFile in $depFiles) {
Write-Host "Installing $($depFile.Name)..."
try {
if ($runAsSystem) {
Add-AppxProvisionedPackage -Online -PackagePath $depFile.FullName -SkipLicense -ErrorAction Stop | Out-Null
} else {
Add-AppxPackage -Path $depFile.FullName -ErrorAction Stop | Out-Null
}
Write-Host "Installed successfully"
} catch {
# Some dependencies might already be installed or fail gracefully
$errorMsg = $_.Exception.Message
if ($errorMsg -match '0x80073D06') {
Write-Host "Already installed (higher version)"
} elseif ($errorMsg -match '0x80073CF0') {
Write-Host "Already installed (same version)"
} else {
Write-Host "Warning: $errorMsg"
}
}
}
# ============================================================================
# INSTALL WINGET
# ============================================================================
Write-Host ""
Write-Host "[RUN] INSTALL WINGET"
Write-Host "=============================================================="
Write-Host "Installing winget AppX package..."
try {
if ($runAsSystem) {
if (Test-Path $licensePath) {
Add-AppxProvisionedPackage -Online -PackagePath $wingetPath -LicensePath $licensePath -ErrorAction Stop | Out-Null
} else {
Add-AppxProvisionedPackage -Online -PackagePath $wingetPath -SkipLicense -ErrorAction Stop | Out-Null
}
} else {
Add-AppxPackage -Path $wingetPath -ErrorAction Stop | Out-Null
}
Write-Host "Installation successful"
} catch {
$errorMsg = $_.Exception.Message
if ($errorMsg -match '0x80073D06') {
Write-Host "Higher version already installed"
} elseif ($errorMsg -match '0x80073CF0') {
Write-Host "Same version already installed"
} else {
Write-Host ""
Write-Host "[ERROR] INSTALLATION FAILED"
Write-Host "=============================================================="
Write-Host "Failed to install winget package"
Write-Host "Error: $errorMsg"
Write-Host ""
exit 1
}
}
} # End of installation section
# ============================================================================
# PATH AND REGISTRATION (always run for SYSTEM to ensure user access)
# ============================================================================
# Configure PATH
Write-Host ""
Write-Host "[RUN] CONFIGURATION"
Write-Host "=============================================================="
Write-Host "Configuring PATH environment variable..."
$localAppData = if ($runAsSystem) {
"C:\Windows\System32\config\systemprofile\AppData\Local"
} else {
$env:LOCALAPPDATA
}
$wingetPaths = @(
"$env:ProgramFiles\WindowsApps\Microsoft.DesktopAppInstaller_*_x64__8wekyb3d8bbwe",
"$localAppData\Microsoft\WindowsApps"
)
foreach ($pathPattern in $wingetPaths) {
$resolvedPath = Resolve-Path $pathPattern -ErrorAction SilentlyContinue |
Sort-Object | Select-Object -Last 1
if ($resolvedPath) {
$pathToAdd = $resolvedPath.Path
$currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine")
if ($currentPath -notlike "*$pathToAdd*") {
[Environment]::SetEnvironmentVariable("Path", "$currentPath;$pathToAdd", "Machine")
$env:Path = "$env:Path;$pathToAdd"
Write-Host "Added to PATH : $pathToAdd"
}
}
}
Write-Host "PATH configured"
# Register winget
Write-Host ""
Write-Host "Registering winget..."
try {
Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe -ErrorAction SilentlyContinue | Out-Null
Write-Host "Registration complete"
} catch {
Write-Host "Registration completed with warnings (may not be needed)"
}
# For SYSTEM context, register for all users
if ($runAsSystem) {
Write-Host ""
Write-Host "Registering for all user accounts..."
# Get all user profiles
$userProfiles = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" |
ForEach-Object {
$profilePath = (Get-ItemProperty $_.PSPath).ProfileImagePath
if ($profilePath -and (Test-Path $profilePath) -and $profilePath -notmatch '(systemprofile|LocalService|NetworkService|Default)') {
$profilePath
}
}
$registeredCount = 0
foreach ($profilePath in $userProfiles) {
$username = Split-Path $profilePath -Leaf
# Load user registry hive if not already loaded
$userSid = (Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" |
Where-Object { (Get-ItemProperty $_.PSPath).ProfileImagePath -eq $profilePath }).PSChildName
# Create PowerShell script to register winget for this user
$registerScript = @"
Add-AppxPackage -DisableDevelopmentMode -Register "$env:ProgramFiles\WindowsApps\Microsoft.DesktopAppInstaller_*_x64__8wekyb3d8bbwe\AppXManifest.xml" -ErrorAction SilentlyContinue
"@
# Try to register using scheduled task
try {
$taskName = "WingetUserRegistration_$username"
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-NoProfile -WindowStyle Hidden -Command `"$registerScript`""
$principal = New-ScheduledTaskPrincipal -UserId $userSid -LogonType S4U
$task = Register-ScheduledTask -TaskName $taskName -Action $action -Principal $principal -Force -ErrorAction Stop
Start-ScheduledTask -TaskName $taskName -ErrorAction Stop
Start-Sleep -Milliseconds 500
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
$registeredCount++
} catch {
# Silently continue if registration fails for a user
}
}
if ($registeredCount -gt 0) {
Write-Host "Registered for : $registeredCount user account(s)"
} else {
Write-Host "User Registration: Users may need to log out/in or run registration manually"
}
}
# ============================================================================
# VERIFICATION
# ============================================================================
Write-Host ""
Write-Host "[INFO] VERIFICATION"
Write-Host "=============================================================="
Write-Host "Waiting for winget to initialize..."
Start-Sleep -Seconds 3
Write-Host "Testing winget command..."
$verificationSuccess = $false
try {
if ($runAsSystem) {
$wingetPath = Resolve-Path "$env:ProgramFiles\WindowsApps\Microsoft.DesktopAppInstaller_*_x64__8wekyb3d8bbwe\winget.exe" -ErrorAction Stop |
Sort-Object | Select-Object -Last 1
$versionOutput = & $wingetPath.Path --version 2>&1
} else {
$versionOutput = & winget --version 2>&1
}
if ($versionOutput -match 'v([\d.]+)') {
$installedVersion = $matches[1]
Write-Host "Winget Version : v$installedVersion"
Write-Host "Installation : Verified"
$verificationSuccess = $true
} else {
Write-Host "Version output : $versionOutput"
Write-Host "Installation : Command available but version unclear"
$verificationSuccess = $true
}
} catch {
Write-Host "Verification : Command not immediately available"
Write-Host "Note : May require restart or new session"
if ($runAsSystem) {
Write-Host "SYSTEM Context : Winget may work for user accounts after restart"
}
}
# ============================================================================
# CLEANUP
# ============================================================================
Write-Host ""
Write-Host "[INFO] CLEANUP"
Write-Host "=============================================================="
if (-not $skipInstallation -and $tempDir -and (Test-Path $tempDir)) {
Write-Host "Removing temporary files..."
try {
Remove-Item -Path $tempDir -Recurse -Force -ErrorAction Stop
Write-Host "Cleanup complete"
} catch {
Write-Host "Cleanup warning : $($_.Exception.Message)"
}
} else {
Write-Host "No temporary files to clean up"
}
# ============================================================================
# FINAL STATUS
# ============================================================================
Write-Host ""
Write-Host "[OK] FINAL STATUS"
Write-Host "=============================================================="
if ($verificationSuccess) {
Write-Host "Status : Success"
Write-Host "Winget : Installed and working"
if ($runAsSystem) {
Write-Host "Note : Running as SYSTEM - restart may be needed for user access"
}
Write-Host ""
Write-Host "[OK] SCRIPT COMPLETED"
Write-Host "=============================================================="
exit 0
} else {
Write-Host "Status : Installed with warnings"
Write-Host "Winget : Installed but verification incomplete"
Write-Host "Action : Restart PowerShell or system to use winget"
Write-Host ""
Write-Host "[WARN] SCRIPT COMPLETED"
Write-Host "=============================================================="
exit 0
}
Key insight: When running as SYSTEM, winget.exe exists but isn't in PATH. Use Resolve-Path to find it in WindowsApps and call directly.
Workhorse script with runtime text replacement. Technicians enter package ID when running - no script editing needed.
[+] winget_install_package.ps1GitHub
$ErrorActionPreference = 'Stop'
<#
██╗ ██╗███╗ ███╗███████╗██╗ ██╗ █████╗ ██╗ ██╗██╗ ██╗
██║ ██║████╗ ████║██╔════╝██║ ██║██╔══██╗██║ ██║██║ ██╔╝
██║ ██║██╔████╔██║█████╗ ███████║███████║██║ █╗ ██║█████╔╝
██║ ██║██║╚██╔╝██║██╔══╝ ██╔══██║██╔══██║██║███╗██║██╔═██╗
███████╗██║██║ ╚═╝ ██║███████╗██║ ██║██║ ██║╚███╔███╔╝██║ ██╗
╚══════╝╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═╝
================================================================================
SCRIPT : Winget Package Installer v1.0.2
AUTHOR : Limehawk.io
DATE : January 2026
USAGE : .\winget_install_package.ps1
FILE : winget_install_package.ps1
DESCRIPTION : Installs software package using winget with SuperOps integration
================================================================================
README
--------------------------------------------------------------------------------
PURPOSE
Installs a software package using winget (Windows Package Manager). Uses
SuperOps runtime text replacement for the package name. Handles silent
installation with automatic acceptance of agreements.
DATA SOURCES & PRIORITY
1) Hardcoded script configuration (package name via SuperOps replacement)
2) Winget package repository
REQUIRED INPUTS
- $PackageName - SuperOps runtime replacement variable for winget package ID
(e.g., "Google.Chrome", "Mozilla.Firefox")
SETTINGS
- Silent installation mode
- Accepts package and source agreements automatically
- Uses machine scope when available
BEHAVIOR
1. Validates software name input
2. Checks winget availability
3. Searches for package to verify it exists
4. Installs package silently
5. Reports installation result
PREREQUISITES
- Winget must be installed
- Administrator privileges recommended
- Internet connectivity
SECURITY NOTES
- No secrets in logs
- Downloads only from official winget sources
EXIT CODES
- 0 = Success - package installed
- 1 = Failure - installation failed or winget unavailable
EXAMPLE RUN
[INFO] INPUT VALIDATION
==============================================================
Software Name : Google.Chrome
[INFO] WINGET CHECK
==============================================================
Winget : Available
Version : v1.7.10861
[RUN] INSTALLATION
==============================================================
Installing Google.Chrome...
Installation complete
[OK] FINAL STATUS
==============================================================
Status : Success
Package : Google.Chrome installed
[OK] SCRIPT COMPLETED
==============================================================
CHANGELOG
--------------------------------------------------------------------------------
2026-01-19 v1.0.2 Updated to two-line ASCII console output style
2025-12-23 v1.0.1 Updated to Limehawk Script Framework
2025-12-03 v1.0.0 Initial release - winget package installer for SuperOps
================================================================================
#>
Set-StrictMode -Version Latest
# ============================================================================
# HARDCODED INPUTS (SuperOps runtime replacement)
# ============================================================================
$PackageId = "$PackageName" # Winget package ID - SuperOps replaces $PackageName
# ============================================================================
# INPUT VALIDATION
# ============================================================================
$errorOccurred = $false
$errorText = ""
Write-Host ""
Write-Host "[INFO] INPUT VALIDATION"
Write-Host "=============================================================="
if ([string]::IsNullOrWhiteSpace($PackageId)) {
$errorOccurred = $true
if ($errorText.Length -gt 0) { $errorText += "`n" }
$errorText += "- Package ID is required (set via SuperOps runtime replacement)"
}
if ($errorOccurred) {
Write-Host ""
Write-Host "[ERROR] INPUT VALIDATION FAILED"
Write-Host "=============================================================="
Write-Host $errorText
Write-Host ""
exit 1
}
Write-Host "Package ID : $PackageId"
# ============================================================================
# WINGET CHECK
# ============================================================================
Write-Host ""
Write-Host "[INFO] WINGET CHECK"
Write-Host "=============================================================="
$wingetPath = $null
$runAsSystem = ([System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value -eq "S-1-5-18")
try {
if ($runAsSystem) {
$resolvedPath = Resolve-Path "$env:ProgramFiles\WindowsApps\Microsoft.DesktopAppInstaller_*_x64__8wekyb3d8bbwe" -ErrorAction SilentlyContinue |
Sort-Object | Select-Object -Last 1
if ($resolvedPath) {
$wingetPath = Join-Path $resolvedPath.Path "winget.exe"
if (-not (Test-Path $wingetPath)) {
$wingetPath = $null
}
}
} else {
$wingetCmd = Get-Command winget -ErrorAction SilentlyContinue
if ($wingetCmd) {
$wingetPath = $wingetCmd.Source
}
}
} catch {
$wingetPath = $null
}
if (-not $wingetPath) {
Write-Host ""
Write-Host "[ERROR] WINGET NOT AVAILABLE"
Write-Host "=============================================================="
Write-Host "Winget is not installed or not available"
Write-Host "Run winget_installer.ps1 first to install winget"
Write-Host ""
exit 1
}
# Get version
try {
$versionOutput = & $wingetPath --version 2>&1
$wingetVersion = if ($versionOutput -match 'v[\d.]+') { $matches[0] } else { "Unknown" }
} catch {
$wingetVersion = "Unknown"
}
Write-Host "Winget : Available"
Write-Host "Version : $wingetVersion"
# ============================================================================
# INSTALLATION
# ============================================================================
Write-Host ""
Write-Host "[RUN] INSTALLATION"
Write-Host "=============================================================="
Write-Host "Installing $PackageId..."
try {
$installArgs = @(
"install"
"--id", $PackageId
"--silent"
"--accept-package-agreements"
"--accept-source-agreements"
)
$process = Start-Process -FilePath $wingetPath -ArgumentList $installArgs -Wait -PassThru -NoNewWindow
if ($process.ExitCode -eq 0) {
Write-Host "Installation complete"
$installSuccess = $true
} elseif ($process.ExitCode -eq -1978335189) {
# Package already installed
Write-Host "Package already installed"
$installSuccess = $true
} else {
Write-Host "Winget exit code : $($process.ExitCode)"
$installSuccess = $false
}
} catch {
Write-Host ""
Write-Host "[ERROR] INSTALLATION FAILED"
Write-Host "=============================================================="
Write-Host "Error: $($_.Exception.Message)"
Write-Host ""
exit 1
}
# ============================================================================
# FINAL STATUS
# ============================================================================
if ($installSuccess) {
Write-Host ""
Write-Host "[OK] FINAL STATUS"
Write-Host "=============================================================="
Write-Host "Status : Success"
Write-Host "Package : $PackageId installed"
Write-Host ""
Write-Host "[OK] SCRIPT COMPLETED"
Write-Host "=============================================================="
exit 0
} else {
Write-Host ""
Write-Host "[ERROR] FINAL STATUS"
Write-Host "=============================================================="
Write-Host "Status : Failed"
Write-Host "Package : $PackageId"
Write-Host "Action : Check winget logs or try manual installation"
Write-Host ""
Write-Host "[ERROR] SCRIPT COMPLETED"
Write-Host "=============================================================="
exit 1
}
The real power of winget: keeping everything updated. Run weekly via scheduled job. Updates every winget-managed application silently.
[+] winget_upgrade_all.ps1GitHub
$ErrorActionPreference = 'Stop'
<#
██╗ ██╗███╗ ███╗███████╗██╗ ██╗ █████╗ ██╗ ██╗██╗ ██╗
██║ ██║████╗ ████║██╔════╝██║ ██║██╔══██╗██║ ██║██║ ██╔╝
██║ ██║██╔████╔██║█████╗ ███████║███████║██║ █╗ ██║█████╔╝
██║ ██║██║╚██╔╝██║██╔══╝ ██╔══██║██╔══██║██║███╗██║██╔═██╗
███████╗██║██║ ╚═╝ ██║███████╗██║ ██║██║ ██║╚███╔███╔╝██║ ██╗
╚══════╝╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═╝
================================================================================
SCRIPT : Winget Upgrade All v1.0.3
AUTHOR : Limehawk.io
DATE : January 2026
USAGE : .\winget_upgrade_all.ps1
FILE : winget_upgrade_all.ps1
DESCRIPTION : Upgrades all winget-managed packages to latest versions
================================================================================
README
--------------------------------------------------------------------------------
PURPOSE:
Upgrades all winget-managed packages to their latest versions. Includes
logging and automatic cleanup of old log files.
REQUIRED INPUTS:
None
BEHAVIOR:
1. Checks Windows version requirements
2. Locates winget executable in WindowsApps
3. Runs winget upgrade --all with silent options
4. Logs output to Windows temp directory
5. Cleans up logs older than 14 days
PREREQUISITES:
- Windows 10 1809+ or Windows 11 or Server 2022
- Administrator privileges
- winget (App Installer) installed
SECURITY NOTES:
- No secrets in logs
- Accepts all package agreements automatically
EXIT CODES:
0 = Success
1 = Failure (system requirements not met)
EXAMPLE RUN:
[INFO] SYSTEM CHECK
==============================================================
Windows Version : 10.0.22631
Product Type : Workstation
Requirements met
[RUN] UPGRADE
==============================================================
Locating winget executable...
Running winget upgrade --all...
[Upgrade output...]
Upgrade completed
[RUN] LOG CLEANUP
==============================================================
Cleaning up logs older than 14 days...
Cleanup completed
[OK] FINAL STATUS
==============================================================
Result : SUCCESS
Log file : C:\Windows\temp\winget-upgrade-log_2024-12-01.txt
[OK] SCRIPT COMPLETE
==============================================================
CHANGELOG
--------------------------------------------------------------------------------
2026-01-19 v1.0.3 Fixed EXAMPLE RUN section formatting
2026-01-19 v1.0.2 Updated to two-line ASCII console output style
2025-12-23 v1.0.1 Updated to Limehawk Script Framework
2024-12-01 v1.0.0 Initial release - migrated from SuperOps
================================================================================
#>
Set-StrictMode -Version Latest
# ============================================================================
# SYSTEM CHECK
# ============================================================================
Write-Host ""
Write-Host "[INFO] SYSTEM CHECK"
Write-Host "=============================================================="
$osInfo = Get-WmiObject -Class Win32_OperatingSystem
$version = [Version]$osInfo.Version
$productType = $osInfo.ProductType
Write-Host "Windows Version : $version"
Write-Host "Product Type : $(if ($productType -eq 1) { 'Workstation' } elseif ($productType -eq 3) { 'Server' } else { 'Unknown' })"
$meetsRequirements = ($productType -eq 1 -and $version -ge [Version]"10.0.17763") -or
($productType -eq 3 -and $version -ge [Version]"10.0.20348")
if (-not $meetsRequirements) {
Write-Host ""
Write-Host "[ERROR] ERROR OCCURRED"
Write-Host "=============================================================="
Write-Host "System does not meet minimum requirements"
Write-Host "Requires: Windows 10 1809+, Windows 11, or Server 2022"
exit 1
}
Write-Host "[OK] Requirements met"
# ============================================================================
# UPGRADE
# ============================================================================
Write-Host ""
Write-Host "[INFO] UPGRADE"
Write-Host "=============================================================="
try {
Write-Host "[RUN] Locating winget executable..."
Set-Location "C:\Program Files\WindowsApps\"
$installer = "Microsoft.DesktopAppInstaller_*_x64__8wekyb3d8bbwe"
$appxDirs = Get-ChildItem $installer -ErrorAction SilentlyContinue
if (-not $appxDirs) {
throw "Could not find winget installation directory"
}
$appx = if ($appxDirs.Count -gt 1) { $appxDirs[1] } else { $appxDirs }
Set-Location $appx
$logPath = "$env:windir\temp\"
$logFile = "winget-upgrade-log_" + (Get-Date -Format "yyyy-MM-dd_HH-mm-ss") + ".txt"
$fullLogPath = Join-Path -Path $logPath -ChildPath $logFile
Write-Host "[RUN] Running winget upgrade --all..."
.\winget.exe upgrade --all --silent --include-unknown --include-pinned --accept-package-agreements --accept-source-agreements --disable-interactivity | Out-File -FilePath $fullLogPath -Append
Get-Content -Path $fullLogPath | Where-Object {
$_ -notmatch "^\s*([/\-\|\\])" -and
$_ -notmatch "\sMB" -and
$_ -notmatch "%" -and
$_.Trim() -ne ""
} | ForEach-Object { Write-Host $_ }
Write-Host "[OK] Upgrade completed"
}
catch {
Write-Host ""
Write-Host "[ERROR] ERROR OCCURRED"
Write-Host "=============================================================="
Write-Host "Failed to run winget upgrade"
Write-Host "Error : $($_.Exception.Message)"
exit 1
}
# ============================================================================
# LOG CLEANUP
# ============================================================================
Write-Host ""
Write-Host "[INFO] LOG CLEANUP"
Write-Host "=============================================================="
Write-Host "[RUN] Cleaning up logs older than 14 days..."
Get-ChildItem -Path $logPath -Filter "winget-upgrade-log_*.txt" -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-14) } |
Remove-Item -Force -ErrorAction SilentlyContinue
Write-Host "[OK] Cleanup completed"
# ============================================================================
# FINAL STATUS
# ============================================================================
Write-Host ""
Write-Host "[INFO] FINAL STATUS"
Write-Host "=============================================================="
Write-Host "Result : SUCCESS"
Write-Host "Log file : $fullLogPath"
Write-Host ""
Write-Host "[INFO] SCRIPT COMPLETE"
Write-Host "=============================================================="
exit 0
Includes automatic log cleanup - keeps 14 days of upgrade logs, purges older ones.
Want automated software deployment? We build RMM automation that works.
Contact Us