Windows Package Manager (WinGet) is Microsoft’s fast, safe, and scriptable package manager for Windows 10/11. It lets you install, upgrade, and manage apps from trusted sources with a single command. For IT pros and power users, WinGet becomes especially valuable when you schedule it to automatically update all applications on a regular cadence. That’s exactly what this guide helps you achieve.
This is a practical, comprehensive tutorial packed with ready-to-use commands, copy/paste PowerShell, optional YAML configuration, and multiple ways to automate updates via Task Scheduler. By the end, you’ll have reliable, silent, fully unattended updates for your Windows apps using WinGet.
Overview of the Use Case
In plain language, this guide shows you how to configure your PC (or a fleet of PCs) to automatically run WinGet to upgrade all installed apps on a schedule. That means Windows will regularly check for newer versions of your software and install them quietly—without user interaction—so your system stays secure and up-to-date.
Common scenarios where this is useful:
- Fresh installs and rebuilds: After imaging a PC, schedule automatic updates to keep the baseline hardened and current.
- Bulk updates across a lab or small fleet: Use one script and a Scheduled Task to keep every station in sync.
- CI/CD automation and dev workstations: Keep developer tools current without manual overhead.
- Remote/hybrid workforce: Reduce helpdesk tickets by keeping apps consistently updated.
- Kiosk/shared devices: Maintain security posture without exposing manual update prompts.
Quick Reference Table
| Command | Purpose | Example Output |
|---|---|---|
| winget –info | Show WinGet version, sources, and system info | Windows Package Manager v1.6.x…; Sources: winget, msstore |
| winget list –upgrade-available | List apps with available updates | Name Version Available Source Id… |
| winget upgrade –all –silent –accept-source-agreements –accept-package-agreements | Silently upgrade all updatable apps | Found 7 upgrades. Upgrading: 1/7 PowerToys… Done |
| winget upgrade –id |
Upgrade a specific app silently | Upgrading Notepad++.Notepad++… Successful |
| winget source list | Show configured sources | Name: winget, Type: Microsoft.Rest… |
| winget source update | Refresh source indexes | Done |
| winget source reset –force | Rebuild sources (fixes index errors) | Resetting sources… Done |
| winget export -o packages.json | Export installed apps | Exported 85 packages to packages.json |
| winget import -i packages.json –silent –accept-* | Import and install from list | Installing 85 packages… Done |
| schtasks /Create … | Create a Scheduled Task from cmd | SUCCESS: The scheduled task “WinGet-AutoUpdate” has successfully been created. |
| Register-ScheduledTask … | Create a Scheduled Task from PowerShell | TaskPath TaskName State |
Key Concepts and Prerequisites
-
Tools you need:
- Windows 10 (1909+) or Windows 11
- WinGet (Windows Package Manager), included with the Microsoft App Installer
- PowerShell 5.1 (built-in) or PowerShell 7.x (optional)
- Task Scheduler (built-in)
- Administrative rights are recommended for machine-wide updates
-
How to check if WinGet is installed and its version:
- Command: winget –info
- If WinGet is missing, install/update “App Installer” from Microsoft Store (Publisher: Microsoft Corporation).
-
Understanding sources:
- The default source is winget (community manifests).
- Optional sources include msstore (Microsoft Store).
- Use winget source list and winget source update to inspect and refresh sources.
-
Silent and unattended operation:
- Use these flags for automation: –silent, –disable-interactivity, –accept-source-agreements, –accept-package-agreements.
- Some packages may still require elevation. Use Scheduled Task with “Run with highest privileges.”
-
Logging:
- WinGet logs: %LOCALAPPDATA%\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\DiagOutputDir
- Best practice: also create your own log file for the scheduled run.
Step-by-Step Guide
1) Verify and prepare WinGet
-
Open Windows Terminal or PowerShell as Administrator.
-
Check version and sources:
winget –info
winget source list
winget source update -
Optional: Export your current app list (handy for rebuilds):
winget export -o C:\Temp\packages.json
2) Test a manual upgrade run
Run a one-time upgrade to confirm WinGet is healthy and apps update silently:
winget upgrade –all –include-unknown –silent –disable-interactivity –accept-source-agreements –accept-package-agreements
- Add –include-unknown to attempt upgrades even when versions aren’t fully known to WinGet.
- If you see UAC prompts, you’ll handle that by running your task “with highest privileges.”
3) Create a robust PowerShell updater script
Save the following script as C:\ProgramData\WinGet\Update-AllApps.ps1. It updates sources, upgrades all apps silently, writes logs, and returns an appropriate exit code for monitoring.
C:\ProgramData\WinGet\Update-AllApps.ps1
Purpose: Silently update all WinGet apps and log results.
param(
[switch]$IncludeUnknown = $true,
[string]$LogFolder = “C:\ProgramData\WinGet\Logs”
)
Ensure log folder
if (-not (Test-Path $LogFolder)) {
New-Item -ItemType Directory -Path $LogFolder -Force | Out-Null
}
$timeStamp = Get-Date -Format “yyyyMMdd-HHmmss”
$logFile = Join-Path $LogFolder “WinGet-Upgrade-$timeStamp.log”
Helper to run a command and tee to log
function Invoke-Logged {
param([string]$CommandLine)
“n=== $CommandLine ===n” | Tee-Object -FilePath $logFile -Append
cmd.exe /c $CommandLine 2>&1 | Tee-Object -FilePath $logFile -Append
return $LASTEXITCODE
}
Update sources first
$code1 = Invoke-Logged “winget source update”
Build upgrade command
$upgradeArgs = @(“upgrade”,”–all”,”–silent”,”–disable-interactivity”,”–accept-source-agreements”,”–accept-package-agreements”)
if ($IncludeUnknown) { $upgradeArgs += “–include-unknown” }
$cmd = “winget ” + ($upgradeArgs -join ” “)
$code2 = Invoke-Logged $cmd
Show a short summary in the log
“nCompleted at $(Get-Date). Exit codes: source=$code1 upgrade=$code2n” | Tee-Object -FilePath $logFile -Append
Exit with the most significant non-zero code (if any)
if ($code2 -ne 0) { exit $code2 }
exit $code1
Notes:
- The script purposely uses cmd.exe /c inside PowerShell to capture Winget console output reliably into the log.
- Logs are written to C:\ProgramData\WinGet\Logs. Consider centralizing with a SIEM or log shipping if needed.
Test it once interactively:
PowerShell -ExecutionPolicy Bypass -File “C:\ProgramData\WinGet\Update-AllApps.ps1”
4) Create a Scheduled Task (GUI method)
- Open Task Scheduler
- Create Task…
- General:
- Name: WinGet Auto-Update
- Description: Silently upgrades all apps via WinGet.
- Select: Run whether user is logged on or not
- Check: Run with highest privileges
- Configure for: Your Windows version
- Security options: Choose the user that will run the task (prefer the logged-in admin or service account that has WinGet available). For reliability, run as the target user context rather than SYSTEM.
- Triggers:
- New… Daily at a time outside active work hours
- Optional: Delay task for up to: 30 minutes (randomizes start)
- Optional: Repeat task every: 1 day (or weekly)
- Actions:
- New… Start a program
- Program/script: powershell.exe
- Add arguments:
-NoProfile -ExecutionPolicy Bypass -File “C:\ProgramData\WinGet\Update-AllApps.ps1”
- Conditions:
- Start the task only if the computer is on AC power (recommended for laptops)
- Wake the computer to run this task (optional)
- Settings:
- Allow task to be run on demand
- Stop the task if it runs longer than: 2 hours (adjust as needed)
- If the task fails, restart every 30 minutes, attempt 3 times
Click OK and provide credentials when prompted. Test the task with “Run” to ensure it works headless.
5) Create a Scheduled Task (command line: schtasks)
Run from an elevated cmd:
schtasks /Create /TN “WinGet-AutoUpdate” /RU “<DOMAIN\Username>” /RP “
/TR “powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\ProgramData\WinGet\Update-AllApps.ps1” ^
/SC DAILY /ST 03:00 /RL HIGHEST /F
- Replace <DOMAIN\Username> and
as appropriate. - For local accounts use MACHINE\Username or just Username.
- Add /AC if you want to restrict to AC power via Conditions (GUI-only option); otherwise, handle via policy.
6) Create a Scheduled Task (PowerShell: Register-ScheduledTask)
$Action = New-ScheduledTaskAction -Execute “powershell.exe” -Argument ‘-NoProfile -ExecutionPolicy Bypass -File “C:\ProgramData\WinGet\Update-AllApps.ps1″‘
$Trigger = New-ScheduledTaskTrigger -Daily -At 3:00AM
$Principal = New-ScheduledTaskPrincipal -UserId “$env:USERDOMAIN\$env:USERNAME” -LogonType Password -RunLevel Highest
$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries:$false -DontStopIfGoingOnBatteries:$false -StartWhenAvailable
Prompts for password; supply a secure string if automating
Register-ScheduledTask -TaskName “WinGet-AutoUpdate” -Action $Action -Trigger $Trigger -Principal $Principal -Settings $Settings
Optional tweaks:
-
Add a random delay:
$Trigger.RandomDelay = [TimeSpan]”0:30:00″
-
Run Weekly:
$Trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Sunday -At 3:00AM
7) Optional: Custom YAML config to control exclusions, pins, and logging
If you want extra control (exclude certain apps, pin versions, adjust logging), you can drive the script with a simple YAML file and extend the script to read it. Save as C:\ProgramData\WinGet\AutoUpdate.yaml:
C:\ProgramData\WinGet\AutoUpdate.yaml
Purpose: Optional configuration consumed by your PowerShell script.
NOTE: This is a custom YAML for the script below, not a WinGet manifest.
logging:
folder: “C:\ProgramData\WinGet\Logs”
upgrade:
includeUnknown: true # include –include-unknown
excludeIds: # do not update these packages
- “Nvidia.GeForceExperience”
- “Oracle.JavaRuntimeEnvironment”
pins: # pseudo-pins for the script (for real ‘winget pin’, see Best Practices) - id: “Git.Git”
version: “2.46.0” # keep at this version; do not auto-update - id: “Python.Python.3.11”
version: “3.11.9”
schedule:
time: “03:00” # not used by Task Scheduler directly; for documentation only
Extend the script to read YAML (requires PowerShell’s YAML module):
Install-Yaml support once on the system (requires internet)
Install-Module -Name powershell-yaml -Scope AllUsers -Force
Import-Module powershell-yaml
$configPath = “C:\ProgramData\WinGet\AutoUpdate.yaml”
if (Test-Path $configPath) {
$conf = (Get-Content $configPath -Raw) | ConvertFrom-Yaml
if ($conf.logging.folder) { $LogFolder = $conf.logging.folder }
$IncludeUnknown = [bool]$conf.upgrade.includeUnknown
$ExcludeIds = @($conf.upgrade.excludeIds)
$Pins = @($conf.upgrade.pins)
# Example: Convert pins to real 'winget pin' at runtime
foreach ($p in $Pins) {
if ($p.id -and $p.version) {
cmd.exe /c "winget pin add --id `"$($p.id)`" --version `"$($p.version)`"" | Out-Null
}
}
}
This is optional. If you prefer simplicity, stick to the core script shown earlier.
8) Optional: WinGet Configuration (YAML) to declare desired packages
WinGet supports a separate feature called “WinGet Configuration” to declare the desired state of software (ensure Present/Latest). You can store and apply that YAML in your environment builds, then keep updates scheduled as shown above. Minimal example:
Example WinGet Configuration YAML (apply with: winget configure –apply .\config.yaml)
This ensures certain apps are installed; your scheduled task then keeps them updated.
For schema and the latest syntax, consult official docs: aka.ms/winget-config
$schema: https://aka.ms/winget-config.schema.json
properties:
resources:
- resource: Microsoft.WinGet.DSC/WinGetPackage
id: NotepadPP
directives:
description: Ensure Notepad++ is installed
settings:
id: Notepad++.Notepad++
source: winget - resource: Microsoft.WinGet.DSC/WinGetPackage
id: VisualStudioCode
settings:
id: Microsoft.VisualStudioCode
source: winget
Then schedule either your update script or directly:
winget configure –apply C:\Config\config.yaml
Troubleshooting
Common issues and exact fixes:
-
Problem: Hash mismatch or security hash error
-
Explanation: The downloaded installer no longer matches the manifest hash (publisher updated the binary).
-
Fixes:
-
Refresh sources:
winget source update
-
Retry upgrade:
winget upgrade –all –silent –accept-source-agreements –accept-package-agreements
-
If urgent and you trust the source, you can bypass hash verification for a specific install/upgrade:
winget install –id
–silent –ignore-security-hash –accept-package-agreements –accept-source-agreements Use sparingly; restore normal verification on the next cycle.
-
-
-
Problem: Multiple installers found or ambiguous installer selection
-
Explanation: Package has multiple architectures/scopes/installer types.
-
Fix: Specify filters explicitly:
winget upgrade –id
–exact –silent –architecture x64 –scope machine Other useful filters: –locale en-US, –source winget
-
-
Problem: No applicable update found even when a newer version exists
-
Explanation: Version detection may be unknown for that package.
-
Fix: Include unknown versions and force retry:
winget upgrade –all –include-unknown –silent –disable-interactivity –accept-source-agreements –accept-package-agreements
-
-
Problem: Source index or database corruption
-
Symptoms: Errors like “Failed when opening source” or “Aggregate source failure”
-
Fix:
winget source reset –force
winget source updateIf issues persist, sign out/in or reinstall “App Installer” from the Microsoft Store.
-
-
Problem: Winget not found in Scheduled Task when running as SYSTEM
- Explanation: WinGet (App Installer) is deployed per-user; SYSTEM may not see it.
- Fix:
- Run the task under a user context that has WinGet installed (recommended).
- Or deploy App Installer MSIX for all users and ensure PATH resolution, then test thoroughly.
-
Problem: UAC prompts halt the scheduled run
-
Fix: In the task’s General tab, check “Run with highest privileges.” Also use:
–silent –disable-interactivity –accept-source-agreements –accept-package-agreements
-
-
Problem: App-specific installers require manual interaction
-
Fix: Exclude or pin those apps, or research app-specific installer switches. You can exclude with a custom script (see YAML section above) or use:
winget pin add –id
-
Automation Tips
-
Combine WinGet with PowerShell for control:
- Add pre-checks (disk space, battery/AC).
- Kill known blocking processes before upgrade (optional).
- Retry failed packages individually.
-
Task Scheduler best practices:
- Run daily or weekly during off-hours.
- Randomize start within 15–30 minutes to reduce contention on shared networks.
- Enable “Run with highest privileges.”
- Configure stop-after duration and restart-on-failure.
-
Intune/MEM:
- Wrap the script (Update-AllApps.ps1) into a Win32 app or use the Intune Management Extension as a scheduled script.
- For devices joined to Azure AD, the user context still matters; test under the same user that has App Installer/WinGet.
-
CI/CD scenarios:
-
In build agents or self-hosted runners on Windows, add a step to:
winget source update
winget upgrade –all –silent –accept-source-agreements –accept-package-agreements -
Cache or mirror installers if you need hermetic builds.
-
-
Offline or restricted networks:
-
Consider adding a private WinGet REST source:
winget source add -n Contoso -a https://packages.contoso.local/api
-
Mirror required installers internally and curate manifests for your environment.
-
Export/import lists for rebuilds:
winget export -o packages.json
winget import -i packages.json –silent –accept-source-agreements –accept-package-agreements
-
-
Logging and monitoring:
- Centralize logs to a file share or SIEM.
- Use the script’s exit codes to alert on failures.
- Periodically review the WinGet logs under the App Installer package folder.
Best Practices
-
Version pinning for stability:
-
Use winget pin to hold critical apps at a known stable version:
winget pin add –id
–version <x.y.z>
winget pin list
winget pin remove –id -
Pin components that are sensitive (drivers, database clients, GPU tools) and update them intentionally.
-
-
Silent and non-interactive runs:
- Always include –silent, –disable-interactivity, –accept-source-agreements, –accept-package-agreements in scheduled commands.
-
Keep sources healthy:
-
Periodically run:
winget source update
-
If errors arise, repair with:
winget source reset –force
winget source update
-
-
Exclude or control problematic apps:
- Some installers don’t support silent mode well. Exclude them or handle with app-specific logic.
- Consider a separate maintenance window for those apps.
-
Reusable scripts and configuration:
- Store Update-AllApps.ps1 under C:\ProgramData or a git repo.
- Use consistent logging paths and rotate logs.
- Optional: maintain a simple YAML/JSON with exclusions and pins.
-
Security and trust:
- Only use trusted sources (winget, msstore, your internal source).
- Avoid –ignore-security-hash unless you fully trust the binary and need a temporary workaround.
-
Validate after updates:
- For critical machines, run smoke tests post-update (e.g., verify specific binaries or services are healthy).
Conclusion
Scheduling auto-updates for all apps with WinGet and Task Scheduler is a powerful, low-maintenance way to keep Windows systems secure and current. You learned how to:
- Verify and prepare WinGet
- Run a silent, unattended upgrade for all packages
- Create Scheduled Tasks (GUI, schtasks, and PowerShell)
- Add logging, exclusions, and optional YAML-driven configuration
- Troubleshoot common issues
- Apply best practices like version pinning and internal sources
Try the sample script and task setup today. WinGet is a safe, native solution that scales from a single workstation to a small fleet with minimal overhead.
FAQ
How often should I schedule WinGet auto-updates?
Daily during off-hours is a good default for most environments. Weekly might be sufficient for stable endpoints. Add a random delay to avoid network spikes in shared environments.
Can I run the Scheduled Task as SYSTEM?
It’s not recommended unless you have confirmed WinGet is available to the SYSTEM context. Because WinGet is distributed via the user-scoped App Installer, running as the same user who has WinGet installed is more reliable. If you must run as SYSTEM, test thoroughly.
How do I exclude a specific app from auto-updates?
Two common approaches:
-
Pin it:
winget pin add –id
-
Extend the PowerShell script to skip particular PackageIds (see the optional YAML section).
Is it safe to use –include-unknown?
Yes, in many environments it helps catch upgrades where version detection is incomplete. However, it may attempt upgrades more aggressively. If you prefer strict version checks, omit –include-unknown.
What if some installers still pop up UI or require input?
Those packages may not fully support silent mode. Exclude them from the general auto-update task, handle with app-specific installer switches, or update them manually during a maintenance window.
