Before diving into the code, let me share exactly why I wrote this script and why you might find it helpful. For a long time, I tested commercial utilities like IObit Software Updater and CCleaner Software Updater. While they technically handle updates, they come with a high cost to system performance. Their underlying executables introduce unnecessary background telemetry, aggressive pop-ups for premium upgrades, and persistent processes that consume system resources.
Moving from a consumer attitude towards the computer system to a developer's perspective, I felt I needed the workstations to be lean and optimized. I realized that installing a bloated, closed-source program to manage software updates is counterproductive. By writing a custom PowerShell script that leverages native WinGet commands, we eliminate the bloat. Here is my transparent, open-source automation tool that runs silently in the background, executing exactly what we need and nothing more.
Keeping Windows applications up to date is a standard requirement for any developer's workstation. While Microsoft provides the excellent Windows Package Manager (WinGet), it currently lacks a native, silent background auto-updater.
If you search for solutions, you will likely find tools like Winget-AutoUpdate (WAU). While incredibly powerful for enterprise IT departments, it is heavily bloated for a single developer's laptop. I wanted a lightweight, "set-it-and-forget-it" solution.
Working alongside Google Gemini as an AI pair-programming partner, I iteratively built a robust, zero-bloat PowerShell automation script. Here is a breakdown of the development journey, the technical hurdles we solved, and the final code.
The Technical Hurdles
We initially wrote a script that triggered at system startup using the hidden NT AUTHORITY\SYSTEM account. However, we quickly ran into several Windows architecture quirks that required refactoring.
1. The User Context Bug
Because WinGet is installed on a per-user basis (living in AppData), the SYSTEM account literally could not find the winget executable, throwing a CommandNotFoundException.
-
The Fix: We refactored the Scheduled Task principal to dynamically grab the interactive user's profile (
[System.Security.Principal.WindowsIdentity]::GetCurrent().Name) and run with highest administrative privileges.
2. The Startup Timing Issue
Once we switched to the user profile, triggering the task at "System Startup" caused it to fail because the user had not logged in yet.
-
The Fix: We changed the trigger to
AtLogOnand added an ISO 8601 delay (PT15M) so the update sequence silently waits 15 minutes after logging in, ensuring it never slows down the boot process.
3. Preventing Log Bloat
Since this script runs daily and logs its output silently, the text file would eventually grow massive.
-
The Fix: We implemented an automatic log-trimming function. Before running the update, PowerShell checks if the log exceeds 2 MB. If it does, it uses the highly efficient
-Tail 500parameter to keep only the most recent history, preventing indefinite file growth.
The Bulletproof Deployment
The final hurdle was distribution. Windows strictly limits executing downloaded .ps1 files via its ExecutionPolicy. Instead of forcing users to lower their system security or mess with digital signatures, the deployment is designed as a direct terminal command. By pasting the raw code into an administrative PowerShell window, it acts as a series of manual commands, bypassing the script execution blocks securely and cleanly.
The Final Code
Here is the complete, production-ready script. Paste this into an Administrator PowerShell window, and it will automatically generate the payload and register the Scheduled Task.
# ==============================================================================
# SCRIPT: Setup-WinGetAutomation.ps1
# PURPOSE: Automatically creates a background WinGet updater script and
# registers it to run 15 minutes after you log in.
# ==============================================================================
$Folder = "C:\Automation"
$ScriptPath = "$Folder\BackgroundUpdater.ps1"
$TaskName = "Automated_WinGet_Updater"
# Ensure the target directory actually exists
if (!(Test-Path $Folder)) {
New-Item -ItemType Directory -Path $Folder | Out-Null
}
# 1. Write the updating payload with Auto-Trimming Logic
$UpdaterCode = @"
# Check if the log file exists and is larger than 2MB (2097152 bytes)
if ((Test-Path "$Folder\updater_log.txt") -and ((Get-Item "$Folder\updater_log.txt").Length -gt 2097152)) {
(Get-Content "$Folder\updater_log.txt" -Tail 500) | Set-Content "$Folder\updater_log.txt"
}
Start-Transcript -Path "$Folder\updater_log.txt" -Append
Write-Host "Starting background WinGet update asset sequence..."
winget upgrade --all --include-unknown --accept-package-agreements --accept-source-agreements
Write-Host "Sequence completed successfully."
Stop-Transcript
"@
Set-Content -Path $ScriptPath -Value $UpdaterCode
# 2. Define the new trigger: At User Logon
$Trigger = New-ScheduledTaskTrigger -AtLogOn
$Trigger.Delay = "PT15M"
$Action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-WindowStyle Hidden -NoProfile -ExecutionPolicy Bypass -File `"$ScriptPath`""
$CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$Principal = New-ScheduledTaskPrincipal -UserId $CurrentUser -LogonType Interactive -RunLevel Highest
$Settings = New-ScheduledTaskSettingsSet -Compatibility Win8
# 3. Register the task
Register-ScheduledTask -TaskName $TaskName -Trigger $Trigger -Action $Action -Principal $Principal -Settings $Settings -Force | Out-Null
Write-Host "Success! The code generated '$ScriptPath' with auto-trimming logs, triggered 15 mins after Login." -ForegroundColor Green
Get the Code & Documentation
You can check out the full repository, including the README.md documentation, on GitHub here:
👉 MediaExpres/windows-automation
What's your approach?
How do you currently handle keeping your development environment up to date? Do you rely on third-party tools, or have you built your own custom scripts? Let me know in the comments below!
(🤖 Acknowledgment: The code and documentation in this project were developed iteratively with Google Gemini as an AI pair-programming partner).
Top comments (0)