Automatic Windows Update
Powershell Script

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:

  1. Import NuGet module 
  2. Import PSWindowsUpdate module
  3. Disable automatic updates (you can remove lines 33-39 if you don’t want to do this)
  4. Fetch and install all available windows updates
  5. Log all completed updates to C:\logs\
  6. Restart if necessary
 
 

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
}