
Automate Windows Update Installation and Reporting with PowerShell
Before you run this
This guide uses the community PSWindowsUpdate module to check for, install, and log Windows updates on the machine it runs on. The end result is a scheduled job that patches a box unattended and drops a CSV report so you can see what happened.
It needs elevation. Installing updates is a system-level operation. You must run PowerShell as Administrator, and later the scheduled task runs as SYSTEM with highest privileges. Installing the module for all users also requires an elevated session.
Test it first. Read the script before you run it. Run it on a test VM or a single non-production server before you point it at anything that matters. Patching can require a reboot, can occasionally break an application, and — critically — installing an update is not something you casually roll back. Get-WindowsUpdate (the listing step) changes nothing and is safe to run anywhere; Install-WindowsUpdate changes the system. Treat the reboot behaviour with respect: the example below suppresses automatic reboots so you stay in control of when the machine restarts.
Nothing here deletes user data, but installed updates and any reboot they trigger are real changes to a live system. Undo is possible for most updates (see the last section) but is not guaranteed for all of them.
Assumptions
- Windows Server 2022 (the steps are identical on Server 2019 and on Windows 10/11).
- Windows PowerShell 5.1, the version shipped in-box. PowerShell 7 works too, but 5.1 is what a scheduled task will find by default.
- The machine reaches either Windows Update or your internal WSUS. PSWindowsUpdate honours the update source the machine is already configured to use.
- You have local Administrator rights.
Install the module
PSWindowsUpdate lives on the PowerShell Gallery. Install it once, for all users, from an elevated prompt:
# One-time setup, elevated session
Install-Module -Name PSWindowsUpdate -Scope AllUsers -Force
Import-Module PSWindowsUpdate
# Confirm it loaded and see what it offers
Get-Command -Module PSWindowsUpdate
If this is the first time you have used the Gallery, PowerShell may prompt you to trust the NuGet provider and the PSGallery repository. That is expected. If your environment blocks the public Gallery, download the module on a connected machine and copy it into a module path — but the Gallery is the standard route.
Step 1 — List updates without installing anything
Always start with the read-only view. This queries for applicable updates and installs nothing:
# Read-only: lists available updates, changes nothing
Get-WindowsUpdate -MicrosoftUpdate
-MicrosoftUpdate widens the scope to include Microsoft Update (drivers and other Microsoft products), not just core Windows Update. Drop that switch if you only want Windows OS updates. Look at the KB numbers and titles before you go further — this is your dry run.
Step 2 — Install updates, with reboot under your control
Here is the install command. Read every switch before running it:
# Installs all applicable updates. -AcceptAll accepts prompts/EULAs.
# -IgnoreReboot installs but does NOT reboot automatically.
Install-WindowsUpdate -MicrosoftUpdate -AcceptAll -IgnoreReboot -Verbose
-AcceptAllaccepts each update so the run is unattended.-IgnoreRebootis the safe default: it installs everything but leaves the machine running so you choose when to restart. The alternative is-AutoReboot, which restarts as soon as an update requires it — only use that on a machine where an unplanned reboot is acceptable.-Verbosegives you a live account of what is happening.
After installing, check whether a reboot is now pending:
# Returns True/False for a pending reboot
Get-WURebootStatus -Silent
Step 3 — A script that installs and writes a report
This is the piece you schedule. It installs updates, then writes a CSV of what the machine has recently applied using the module's history cmdlet. Replace C:\path\to\WULogs with a real directory you control.
#Requires -RunAsAdministrator
$logDir = 'C:\path\to\WULogs' # <-- replace with your own log folder
if (-not (Test-Path $logDir)) {
New-Item -Path $logDir -ItemType Directory | Out-Null
}
Import-Module PSWindowsUpdate
# Install updates but leave the reboot decision to you
Install-WindowsUpdate -MicrosoftUpdate -AcceptAll -IgnoreReboot -Verbose
# Write a report from update history
$stamp = Get-Date -Format 'yyyy-MM-dd_HHmmss'
$report = Join-Path $logDir "WU_$stamp.csv"
Get-WUHistory |
Select-Object Date, Title, KB, Result |
Export-Csv -Path $report -NoTypeInformation -Encoding UTF8
# Flag whether a restart is now required
if (Get-WURebootStatus -Silent) {
"Reboot required after run at $stamp" | Out-File (Join-Path $logDir 'reboot-needed.txt')
}
A note on honesty: the exact property names emitted by these cmdlets can vary by module version. Before you rely on the CSV columns, run Get-WUHistory | Get-Member once on your box and confirm Date, Title, KB, and Result exist — adjust the Select-Object list to match what you see.
If you want the report emailed rather than filed, do it as a separate step. Note that the old Send-MailMessage cmdlet still works in 5.1 but Microsoft has deprecated it; check current Microsoft Learn guidance before building an email pipeline on it.
Step 4 — Schedule it
Save the script as C:\path\to\Install-Updates.ps1, then register a weekly task that runs as SYSTEM. These are the standard built-in ScheduledTasks cmdlets:
$action = New-ScheduledTaskAction -Execute 'powershell.exe' `
-Argument '-NoProfile -ExecutionPolicy Bypass -File "C:\path\to\Install-Updates.ps1"'
# Runs every Sunday at 03:00 — change to suit your maintenance window
$trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Sunday -At 3am
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' `
-LogonType ServiceAccount -RunLevel Highest
Register-ScheduledTask -TaskName 'Windows Update - Automated' `
-Action $action -Trigger $trigger -Principal $principal `
-Description 'Installs Windows updates and writes a CSV report'
-ExecutionPolicy Bypass applies only to this one invocation and does not change the machine-wide policy.
Verify it worked
The task exists and last ran cleanly:
Get-ScheduledTask -TaskName 'Windows Update - Automated' | Get-ScheduledTaskInfoCheck
LastRunTimeandLastTaskResult(0 means success).The updates actually installed: look at your CSV in the log folder, or query history directly:
Get-WUHistory | Select-Object -First 20 Date, Title, KB, ResultCross-check against Windows' own record in Settings → Windows Update → Update history, or with
Get-HotFixfor updates that register as hotfixes.
Undo and rollback
Most updates can be removed by KB number. PSWindowsUpdate provides:
# Removes an update by KB. Test on a non-production machine first.
Remove-WindowsUpdate -KBArticleID KB5030000 -Verbose
Substitute the real KB you want gone. The built-in equivalent is wusa /uninstall /kb:5030000. Not every update is removable — some servicing-stack and cumulative updates cannot be cleanly uninstalled — so do not treat removal as guaranteed. For a fuller reversal, a system restore point or a VM snapshot taken before the run is your reliable safety net.
To stop the automation entirely, remove the scheduled task:
Unregister-ScheduledTask -TaskName 'Windows Update - Automated' -Confirm:$false
For exact parameters and current behaviour of the module, see the PSWindowsUpdate page on the PowerShell Gallery, and consult Microsoft Learn for the built-in Register-ScheduledTask and Get-HotFix cmdlets.
Runs enterprise networks and security for a living, and writes Shore Up to turn two decades of hands-on Linux, Windows and mail-server work into guides you can actually use.
More about the author →Was this article helpful?
Tap a star — no sign-in needed.
Be the first to rate this article.
