Manage Windows Update installations using Windows PowerShell

In most domains Windows Update are controlled by Group Policy and Windows Server Update Services (WSUS). For client computers, its common to download and install updates automatically. For servers, we want to control the installation of Windows Updates inside a scheduled maintenance window, and hence the Windows Update settings are configured not to automatically install updates.

If there are a few servers to manage, it wont be that time consuming to log on to each server with Remote Desktop and do the Windows Update installations manually. In larger environments however, this wont be an option, it must be automated in some way. While enterprise environments typically invest in a commercial product like BigFix for patch management, this might be overkill or too expensive for environments smaller than an enterprise.

To manage Windows Update in an automated way we can access the Windows Update Agent API using a COM-object called Microsoft.Update.Session. Using the New-Object cmdlet in Windows PowerShell, its easy to work with this COM-object. Based on this COM-object and portions of James ONeills functions for managing Windows Update, Ive written a PowerShell-script called Invoke-WindowsUpdate.

This script is intended to be used to download and install Windows Updates on servers. It runs like expected when invoked from a local computer, however, invoking the script using PowerShell Remoting like I was planning turned out to be problematic: Invoke-Command -ComputerName ServerA -FilePath 'C:ps-scriptsWindows UpdateInvoke-WindowsUpdate.ps1'

This will return the following error message:
Exception calling "CreateUpdateDownloader" with "0" argument(s): "Access is denied. (Exception from HRESULT: 0x80070005 E_ACCESSDENIED))"

This issue doesnt seem to be related to PowerShell, as there are several others reporting the same problem in other languages like VBScript.

The common workaround is to schedule the script to run as a scheduled task running as SYSTEM. Ive chosen to use this approach and to use PowerShell Remoting to invoke the scheduled task to run the script. An example:

$servers = Get-Content 'C:ps-scriptsWindows UpdateBulkA.txt'
foreach ($server in $servers) {
Invoke-Command -Computer $server -ScriptBlock {schtasks /run /tn "PowerShell - download and install Windows Updates"}
}

To create the scheduled task I would recommend you to use Group Policy Preferences.
A few sample screenshots from my lab setup:

image

image

image

image

Although its possible to invoke the script on all servers in the domain at once using i.e. the Active Directory-module for PowerShell to get the server names, I would recommend to break down the installations in several bulks. This way you can control that all domain controllers doesnt go offline at the same time and so on.

As you can see in the script its also possible to enable reporting to e-mail or file (HTML) to a central location, in addition to control whether the servers should reboot if required.
Planned improvements include nicer reports, Windows Update settings in the reports and if possible make the script work without having to use scheduled tasks. Suggestions for other improvements are always welcome.