WSUS Windows Update Compliance PowerShell
Hello,
We previously saw how to request the local Windows Update configuration of a Windows computer or server. Today, we’ll check the installation of a specific update. On a given computer, then, on the WSUS reporting server. This will allow us to be sure that a specific KB was deployed on all our computers/server.
Get Installation Status for a Named Update
You have two options:
- Get-WmiObject/Get-CimInstance -ClassName Win32_QuickFixEngineering
- Get-Hotfix
All of those methods works, the Get-CimInstance may be a bit faster and more firewall friendly but it requires PowerShell3+.
If you want to check all of your domain joined computer, you can use this:
Get-ADForest | Select-Object -ExpandProperty Domains | ForEach-Object -Process {Get-ADComputer -Filter *} | ForEach-Object -Process {Get-CimInstance -ClassName Win32_QuickFixEngineering -ComputerName $_.Name}
As you can expect, it will be quite long. You can get the work done faster if you want to get only a subset of installed update:
Get-HotFix -Id KB4041691
Get-CimInstance -ClassName Win32_QuickFixEngineering -Filter "HotFixID='KB4041691'"
But you still need to query each computer one by one.
WSUS Windows Update Compliance PowerShell Massively
If you use a WSUS server with a WSUS reporting server, you can skip the step to query each computer one by one and directly query the WSUS report server, it will me much more efficient, but the data may not be fully up to date depending of the frequency reporting configured on your clients computers. To do this, you need to be connected on a computer or server with the WSUS dll, you can check with this:
[reflection.assembly]::LoadWithPartialName('Microsoft.UpdateServices.Administration') | Out-Null
If it works, you’re good to go on the next step, if not, you need to find a computer with the WSUS server role.
[reflection.assembly]::LoadWithPartialName('Microsoft.UpdateServices.Administration') | Out-Null
$WsusServerList = 'WSUS01','WSUS02'
#Choose one of the two kind of names, the short or full one
$KBName = 'KB4038777'
$KBName = '2017-09 Security Monthly Quality Rollup for Windows Server 2008 R2 for x64-based Systems (KB4038777)'
$UpdateScope = New-Object -TypeName Microsoft.UpdateServices.Administration.UpdateScope
$UpdateScope.TextIncludes = $KBName
$ComputerScope = New-Object -TypeName Microsoft.UpdateServices.Administration.ComputerTargetScope
$WsusAllInfo = ForEach($WsusServer in $WsusServerList){
$WsusConnection = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer($WsusServer,$false,'8530')
$WsusConnection.GetUpdates($UpdateScope) | ForEach-Object -Process {
$_.GetUpdateInstallationInfoPerComputerTarget($ComputerScope) | ForEach-Object -Process {
New-Object -TypeName PSObject -Property @{
Name = $WsusConnection.GetComputerTarget($_.ComputerTargetId).FullDomainName
UpdateInstallationState = $_.UpdateInstallationState
UpdateApprovalAction = $_.UpdateApprovalAction
Update = $WsusConnection.GetUpdate([GUID]$_.UpdateId).LegacyName
}
}
}
$WsusConnection = $null
}
#Export and display information
$WsusAllInfo | Group-Object -Property UpdateInstallationState
$WsusAllInfo | Sort-Object -Property Name -Unique | Export-Csv -NoTypeInformation -Delimiter ';' WSUS-Compliance.csv
Those few lines will allow you to query the WSUS database to get the information, it will be very much faster, only a few minutes against a few hours if you query each host one by one.