I’ve often found that managing Windows updates can be a hassle, especially when you’re trying to ensure that multiple devices are up to date without disrupting daily operations. To address this, I’ve developed a PowerShell script that automates the process of managing Windows Update settings and getting the user’s devices restarted weekly. Most MSP’s have software to do this, but if not here is a script to do it yourself. This one took me a while to get right, if you see any errors in my code let me know. I currently have this running in production and it’s working well.
I recommend scheduling this script after hours as it will restart the device if it installs a significant update.
This script will do the following:
Here’s an example of what the logs would look like:
Here is a link to the powershell script, or just copy and paste the code below.
Can set up automation in task scheduler or from software like Autotask or Syncro
# Updated 12.14.23 -JL
# Windows Update Script - Installs needed modules, Disables automatic updates, finds and installs all available updates
# Logs to C:\logs\windowsupdate with timestamps and tracks all installed updates
# Create C:\logs if it doesn't already exist
if (-not (Test-Path "C:\logs")) {
New-Item -Path "C:\logs" -ItemType Directory
}
# Ensure that PowerShellGet is imported
Import-Module PowerShellGet -Force
# Check and install NuGet Provider if not present
if (-not (Get-PackageProvider -Name NuGet -ListAvailable)) {
try {
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
Write-Output "NuGet provider has been successfully installed."
} catch {
Write-Error "Failed to install NuGet provider. Error: $_" | Out-File "C:\logs\windowsupdate.log" -Append
}
}
# Check and install PSWindowsUpdate module if not present
if (-not (Get-Module -ListAvailable -Name PSWindowsUpdate)) {
try {
Install-Module -Name PSWindowsUpdate -Force -Repository PSGallery
Write-Output "PSWindowsUpdate module has been successfully installed."
} catch {
Write-Error "Failed to install PSWindowsUpdate module. Error: $_" | Out-File "C:\logs\windowsupdate.log" -Append
}
}
# Set Windows Update service startup type to Manual
try {
Set-Service -Name wuauserv -StartupType Manual
Write-Output "Windows Update service startup type has been set to Manual."
} catch {
Write-Error "Failed to set Windows Update service startup type to Manual. Error: $_" | Out-File "C:\logs\windowsupdate.log" -Append
}
# Adding a sleep because I read in some cases you can't start a service right after the startup type has been changed
Start-Sleep -Seconds 5
# Now, start Windows Update service
if ((Get-Service -Name wuauserv).Status -ne 'Running') {
try {
Start-Service -Name wuauserv
} catch {
Write-Error "Could not start the Windows Update service. Error: $_" | Out-File "C:\logs\windowsupdate.log" -Append
}
}
# Import PSWindowsUpdate module and trigger updates
Import-Module PSWindowsUpdate -Force
# Capture the current date and time for logging
$currentDateTime = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
# Trigger the Windows Update
try {
$updateResult = Install-WindowsUpdate -MicrosoftUpdate -AcceptAll -AutoReboot
if ($updateResult) {
$installedUpdates = $updateResult | ForEach-Object { $_.Title }
"$currentDateTime - The following Windows Updates have been triggered and installed:" | Out-File "C:\logs\windowsupdate.log" -Append
$installedUpdates | ForEach-Object { "$currentDateTime - $_" | Out-File "C:\logs\windowsupdate.log" -Append }
} else {
"$currentDateTime - No updates were installed." | Out-File "C:\logs\windowsupdate.log" -Append
}
} catch {
"$currentDateTime - Failed with an error. Error: $_" | Out-File "C:\logs\windowsupdate.log" -Append
}