Powershell: Pending Windows Updates

Every now and then you need to check if your servers or client computers have pending updates. You can generate a simple list of this with Powershell. I have created a script for this on my Github named ListPendingWindowsUpdates.ps1. Here is a quick breakdown of the script, feel free to use and modify it anyway you like. Please comment below what you ended up doing with it.
We always need to declare the functions of the Powershell script first but I will dig into the only function of this script below and start with the locally executed code. This script have one locally executed part and then a function that is executed on each and every server/client it lists. For this to work you need to run the script with domain admin rights. Both to access the Active Directory and to remote execute the code on each server/client.
Local base script
First we import the modules needed to access the Active Directory.
[ps]import-module ActiveDirectory[/ps]
Then we get a list of computers we want to execute this one from the Active Directory.
[ps]Get-ADComputer -Filter "*" | Sort-Object Name | Set-Variable servers[/ps]
You can use any filter you like here, more information: https://technet.microsoft.com/en-us/library/ee617192.aspx. You can also replace this line with getting the content of a text file with all the server names listed. Then we create an object list to store the result in. I prefer to use custom objects to store and pass information back and forth between the local script and remote functions since it’s really simple and easy to display the information or dump it out to a text file when done.
[ps]$result = @()[/ps]
Then we will start to loop the servers. For each server we do a try/catch to make sure that we continue to execute even if one server is down or spits an error. In the catch I haven’t implemented any error handling in this example, it depends on how you use it if you want it stored or displayed. The way the script sits now it will spit out the error in the console while executing. If you plan to save the result to a text file it would be good to include this.
[ps]
foreach($server in $servers)
{
try
{
# Invoke remote function
Invoke-Command -ScriptBlock ${function:GetPendingUpdatesRemote} -ComputerName $server -ErrorAction Stop | Set-Variable remoteresult
$result += $remoteresult
}
catch
{
# Catch error and store it
$_.Exception
}
}
[/ps]
Then we spit out the result into a gridview. From here you can copy past into an excel spreadsheet. You can also change this line to save the result in a text file or other output.
Remote function
When the remote Invoke-Command is executed it sends the function GetPendingUpdatesRemote over to the target system and executes the code there. I found that this approach was much quicker then executing all code locally and running remote COM sessions. This is a breakdown of the functions code.
First we establish COM Object session for the Microsoft Update service. If an error is encountered we return that to the requesting script.
[ps]
try
{
#Create Session COM object
$updatesession = [activator]::CreateInstance([type]::GetTypeFromProgID("Microsoft.Update.Session",’localhost’))
}
catch
{
# Catch error and return it
return $_.Exception
}
[/ps]
We create a return value to store all updates. We will be creating a single custom object for each pending update. Then we create the update searcher from the COM object session and configure it to search for updates that haven’t been installed yet, obviously.
[ps]
$retval = @()
# Configure Session COM Object
$updatesearcher = $updatesession.CreateUpdateSearcher()
# Configure Searcher object to look for Updates awaiting installation
$searchresult = $updatesearcher.Search("IsInstalled=0")
[/ps]
With a simple if statement we check if there is any updates to be installed, by checking if the count is greater then zero.
[ps]
if ($searchresult.Updates.Count -gt 0)
{
[/ps]
Then we loop the pending updates.
[ps]
for ($i=0; $i -lt $count; $i++)
{
[/ps]
Now we store the information about the update in a new object and then create another temp object storing just the information we are interested in. In this case title, kb reference, priority, reboot behavior and if it’s already downloaded or not.
[ps]
# Create object holding update
$update = $searchresult.Updates.Item($i)
# Create temp object
$temp = "" | Select Title,KB,Priority,RebootBehavior,IsDownloaded
$temp.Title = $update.Title
$temp.KB = (‘KB’ + $update.KBArticleIDs)
# Get priority
$temp.Priority = switch ($update.DownloadPriority)
{
1 {‘Low’}
2 {‘Normal’}
3 {‘High’}
}
# Get reboot behaivor
$temp.RebootBehavior = switch ($update.InstallationBehavior.RebootBehavior)
{
0 {‘NeverReboots’}
1 {‘AlwaysRequiresReboot’}
2 {‘CanRequestReboot’}
}
# Verify that update has been downloaded
if ($update.IsDownLoaded -eq "True")
{
$temp.IsDownloaded = $true
}
else
{
$temp.IsDownloaded = $false
}
[/ps]
Then we return the information to the requesting system so we can collect all the information in one list. By using the PSComputerName added to the object by the remote execution we know which server the information is from.