Tenant Manager Background

Building PowerShell Tools for MSPs: Automating Windows Updates

Written by Hornetsecurity / 28.06.2018 /
Home » Blog » Building PowerShell Tools for MSPs: Automating Windows Updates

Let’s face it, no one likes Windows Updates – least of all Managed Service Providers. However, there is a way to make the process less tedious: through automation.

For MSPs, managing Windows Updates for clients is always messy. No matter what patch management solution you use, it is inevitable that Windows Updates will still cause you headaches. Whether there is a bad patch that gets rolled out to a number of clients or the never-ending burden of having to troubleshoot devices where Windows Patches just aren’t installing properly. 

Luckily, with the magic of PowerShell and the help of the PowerShell module PSWindowsUpdate, we can manage Windows updates in an automated fashion, allowing us to develop scripts that ease some of our Windows Update pains.

How to Install PSWindowsUpdate

PSWindowsUpdate was created by Michal Gajda and is available via the PowerShell Gallery, which makes installation a breeze. To install PSWindowsUpdate, all we have to do, if we are running a Windows 10 OS, is open up a PowerShell cmd prompt and type in the following syntax:

Install-Module -Name PSWindowsUpdate

If we want to save this module and put it on a network share so that other servers can import and run this module, then we will use the save-module cmdlet:

Save-Module -Name PSWindowsUpdate -Path

Using PSWindowsUpdate

Now that we have the module installed, we can run Windows Updates via PowerShell. We can perform numerous actions with this module, but for this post, we will go over the basics. We can use the Get-WindowsUpdate cmdlet to fetch a list of available updates for our machine. In my example, I have the module installed on a workstation, and I run the following syntax to get the list of Windows updates applicable to my machine:

Get-WindowsUpdate

Building PowerShell Tools for MSPs: Automating Windows Updates

Now that I can see what updates my machine is missing, I can use the -Install parameter to install the updates. If I wanted to install all available updates and automatically reboot afterward, I would use the -autoreboot parameter. The syntax looks like this:

Remove-WindowsUpdate -KBArticleID KB890830 -NoRestart

Building PowerShell Tools for MSPs: Automating Windows Updates

If we want to check available Windows updates remotely from other computers, we can simply use the -ComputerName parameter:

Building PowerShell Tools for MSPs: Automating Windows Updates

Continuous Update Script

PSWindowsUpdate can be used in deployment scripts to ensure Windows is completely up to date before being placed in production. Below, I have created a script that will deploy all available Windows updates to a Windows Server and restart when it is complete. Right after the restart is done, the update process can be started again and repeat itself until there are no more Windows updates left. 

This is a very useful script to use for VM deployments. Unless you have the time to ensure your VM templates are always up to date every month, there will almost always be Microsoft Updates to install when deploying new Windows Virtual Machines. Also, ensuring that all available Windows Updates on a system are installed can be a very time-consuming task, as we all know Windows Updates isn’t the fastest updater in the world.

This script requires you to run it from an endpoint that has the PSWindowsUpdate module installed. It also should be run with an account with local administrator permissions to the remote server it is managing Windows updates on. Essentially, it will install PSWindowsUpdate on the remote server via PowerShell get and will use the cmdlet Invoke-WUJob, which uses a task scheduler to control Windows updates remotely. 

We have to use Task Scheduler because there are certain limitations with some of the Windows Update methods that prevent them from being called from a remote computer.

Copy this script to a notepad and save it as a .ps1. I save mine as install-windowsupdates.ps1:

<#
.SYNOPSIS
This script will automatically install all avaialable windows updates on a device and will automatically reboot if needed, after reboot, windows updates will continue to run until no more updates are available.
.PARAMETER URL
User the Computer parameter to specify the Computer to remotely install windows updates on.
#>

[CmdletBinding()]

param (
[parameter(Mandatory=$true,Position=1)]
[string[]]$computer
)

ForEach ($c in $computer){
#install pswindows updates module on remote machine
$nugetinstall = invoke-command -ComputerName $c -ScriptBlock {Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force}
invoke-command -ComputerName $c -ScriptBlock {install-module pswindowsupdate -force}
invoke-command -ComputerName $c -ScriptBlock {Import-Module PSWindowsUpdate -force}

Do{
#Reset Timeouts
$connectiontimeout = 0
$updatetimeout = 0
#starts up a remote powershell session to the computer

do{
$session = New-PSSession -ComputerName $c
"reconnecting remotely to $c"
sleep -seconds 10
$connectiontimeout++
} until ($session.state -match "Opened" -or $connectiontimeout -ge 10)

#retrieves a list of available updates
"Checking for new updates available on $c"
$updates = invoke-command -session $session -scriptblock {Get-wulist -verbose}
#counts how many updates are available
$updatenumber = ($updates.kb).count
#if there are available updates proceed with installing the updates and then reboot the remote machine
if ($updates -ne $null){

#remote command to install windows updates, creates a scheduled task on remote computer
invoke-command -ComputerName $c -ScriptBlock { Invoke-WUjob -ComputerName localhost -Script "ipmo PSWindowsUpdate; Install-WindowsUpdate -AcceptAll | Out-File C:\PSWindowsUpdate.log" -Confirm:$false -RunNow}
#Show update status until the amount of installed updates equals the same as the amount of updates available
sleep -Seconds 30
do {$updatestatus = Get-Content \$c\c$\PSWindowsUpdate.log

"Currently processing the following update:"
Get-Content \$c\c$\PSWindowsUpdate.log | select-object -last 1
sleep -Seconds 10
$ErrorActionPreference = ‘SilentlyContinue’
$installednumber = ([regex]::Matches($updatestatus, "Installed" )).count
$Failednumber = ([regex]::Matches($updatestatus, "Failed" )).count
$ErrorActionPreference = ‘Continue’
$updatetimeout++

}until ( ($installednumber + $Failednumber) -eq $updatenumber -or $updatetimeout -ge 720)
#restarts the remote computer and waits till it starts up again
"restarting remote computer"
#removes schedule task from computer
invoke-command -computername $c -ScriptBlock {Unregister-ScheduledTask -TaskName PSWindowsUpdate -Confirm:$false}
# rename update log
$date = Get-Date -Format "MM-dd-yyyy_hh-mm-ss"
Rename-Item \$c\c$\PSWindowsUpdate.log -NewName "WindowsUpdate-$date.log"
Restart-Computer -Wait -ComputerName $c -Force
}

}until($updates -eq $null)
"Windows is now up to date on $c"
}

Now that you have your .ps1 file created, remember the location where you saved it:

Building PowerShell Tools for MSPs: Automating Windows Updates

We will open up an administrative PowerShell console and type in the following command in order to deploy all available windows updates on our server “Server3”:

PowerShell "C:\users\Administrator\Documents\install-windowsupdates.ps1" -computer Server3

Building PowerShell Tools for MSPs: Automating Windows Updates

A remote connection is established with Server3 and we install the module remotely. Then we perform a get-windowsupdate to get a list of any available windows updates. Then we invoke the server to install those updates:

Building PowerShell Tools for MSPs: Automating Windows Updates

You can see in the screenshot that after the updates are installed and the server is rebooted, the script picks back up again and starts the process of verifying if there are any new updates available. If there are, it will run through the steps of installing them again. Once there are no more available updates, the script will stop:

Building PowerShell Tools for MSPs: Automating Windows Updates

Wrap-Up

As you can see, this method is quite useful in several different situations. It’s simply another tool you can add to your toolbox to help your team assist your customers. More efficient operations are a good thing for everyone!

What about you? Do you have Windows Update stories to share? Any particularly favorite workarounds? Has this post and script been useful to you?

Also, I’ve written a lot of automation for MSPs here. If you like this, check out the following blog posts:

Automation for MSPs: Getting started with Source ControlAutomation for MSPs: HTML TablesAutomation for MSPs: Advanced PowerShell Functions

Thanks for reading!