Happy Reboot Wednesday! At least that was what I thought. When over half of our boxes were still alive and humming along this morning without a reboot, I was a bit concerned because I knew that KB2809289 was marked as a ‘Requires Restart’. So, this begged the question, did the boxes actually get KB2809289 installed?

Well, I could go around to each individual computer and check. Yeah, that’s not going to happen. I could use the wmic qfe get command using the /node:pcname parameter. But I didn’t want to have to do this hundreds of times. So, I decided to write my very first PowerShell. I could have easily have burned this out in .NET but I decided today was a good day to try this PowerShell thing.

What this script does is query your Active Directory domain and gets a list of pc names. It then iterates through each pc and leverages the QuickFixEngineering (qfe) object from PowerShell to see if the KBs you specified are installed or not. Originally I was shelling out to wmic and handling the results through PowerShell. But the amazing @mwjcomputing has learned me more good! (I hear the screams of thousands of grammar pedants and then it suddenly goes silent.) Apparently scripting in PowerShell, while similar to C#, is not the same as writing in C#. After @mwjcomputing stopped laughing at my first script, he graciously pointed out PowerShell with WMI.

There is a great WMI Object called Win32_QuickFixEngineering where you can specify the ‘-Computer $name’. You can also use the -Filter “KBHotFix=’KB12345′” if you wanted, but if you entered 10 KBs it would be calling the same object 10 times. I’m a minimalist with objects so a single call is enough and I’ll search the results instead. Less calls means less of a footprint.

The script has the following parameters that you can use:
$kbs [Required] = List the KBs separated by a comma in quotes. (i.e.: -kbs “809289,2814124”)
$outputFile [Optional] = Will save the comma separated values output to this file. (i.e.: -outputFile wumaster.csv)
$omitInstalled [Optional] = Will exclude the results if the KB is installed. 1 = True, 0 = False (i.e.: -omitInstalled 1)
$computer [Optional] = Specifies a single computer to check if you don’t want to do your entire Active Directory. (i.e.: -computer APT0xA)

A sample command would be:

PS C:\>waucheck.ps1 -kbs "2809289,2814124" -omitInstalled 1 -outputFile wumaster.csv

This will then query your Active Directory (if you have one) and scan each PC and see if KB2809289 and KB2814124 are installed. If they are not, they will save to wumaster.csv. If they are installed, it won’t output anything.

Just to note, there were some pcs that gave me a RPC error. When this happens, they will output in the file with the name of the computer and RPC_Error in the “Installed” column. These you’ll have to check manually.

Without further delay, here is waucheck.ps1. Download the file directly here –> waucheck.ps1 You can also copy and paste the code below and put it in a file saved as waucheck.ps1. Then run this from a PowerShell command line.

<#  -------------------------------------------------
Name: 		WAUCheck
Description: 	Windows Automatic Update Checker
		Checks for installed KBs

Author:		Ben0xA
Shout Outs:	mwjcomputing
Params:
	$kbs		Comma separated values of KB numbers
	$outputFile	The output file to save results
	$omitInstalled	Omits output if the KB is installed
	$computer	Specifies a single computer to scan
-----------------------------------------------------
#>

Param(
	[Parameter(Mandatory=$true,Position=1)]
	[string]$kbs,
	
	[Parameter(Mandatory=$false,Position=2)]
	[string]$outputFile,
	
	[Parameter(Mandatory=$false,Position=3)]
	[boolean]$omitInstalled,
	
	[Parameter(Mandatory=$false,Position=4)]
	[string]$computer
)

Function Get-Pcs{
	$domain = New-Object System.DirectoryServices.DirectoryEntry
	
	$ds = New-Object System.DirectoryServices.DirectorySearcher
	$ds.SearchRoot = $domain
	$ds.Filter = ("(objectCategory=computer)")
	$ds.PropertiesToLoad.Add("name")
	
	$rslts = $ds.FindAll()
	return $rslts
}

Function Get-KBs($pcname){
	$rslt = ""
	$qfe = Get-WmiObject -Class Win32_QuickFixEngineering -Computer $pcname -ErrorVariable myerror -ErrorAction SilentlyContinue
	if($myerror.count -eq 0) {
		foreach($kb in $kbItems){
			$installed = $false
			$kbentry = $qfe | Select-String $kb
			if($kbentry){
				$installed = $true
			}
			if($omitInstalled){
				if(-not $installed){
					$rslt += "$pcname,$kb,$installed`r`n"
				}
			}
			else {
				$rslt += "$pcname,$kb,$installed`r`n"
			}		
		}
	}
	else{
		$rslt += "$pcname,$kb,RPC_Error`r`n"
	}
	return $rslt
}

# Begin Program Flow

Clear-Host
Write-Host "WAUCheck"
Write-Host "Written By: @Ben0xA"
Write-Host "Huge thanks to @mwjcomputing!`r`n"
Write-Host "Looking for KBs $kbs"
if($omitInstalled){
	Write-Host "Omitting entries where the KB is installed."
}

if(-not $outputFile){
	Write-Host "Sending output to the screen. Use -outputFile name to save to a file.`r`n"
}
else {
	Write-Host "Will save csv results to $outputFile. Query messages will only appear on the screen.`r`n"
}

$wumaster = "PC Name,KB,Installed`r`n"
$kbItems = $kbs.Split(",")
if(-not $computer){
	$pcs = Get-PCs
	
	foreach($pc in $pcs){
		$pcname = $pc.Properties.name
		
		if($pcname){
			Write-Host "Querying $pcname, please wait..."
			$wumaster += Get-KBs($pcname)
		}	
	}
}
else{
	Write-Host "Querying $computer, please wait..."
	$wumaster += Get-KBs($computer)
}

if(-not $outputFile){
	Clear-Host
	$wumaster
}
else {
	$wumaster| Out-File $outputFile
	Write-Host "Output saved to $outputFile"
}

#End Program
Share →