This script can interrogate a list of servers for Office and SP updates and Patches. Either a comparison can be made from a patch list file, or all patches are returned. They are returns as an array of objects, so output can be piped to csv using the | export-csv commandlet
Script: check-spupdates.ps1
# Script to check registry on multiple servers for Office patches and security updates)
# Params
# -filter : fileter on the registry lookup - normally can be ignored
# -ServerListFile : File containing a list of server names to check. One line per server
# -PatchListFile : List of KB patches - i.e. KB2553345 - one per line. Will return if the patch has been applied or not. If this param is not specified, all SP patches installed on the
# Server will be returned. This could be used to create a ServerListFile (using just the server name field) if comparison is required for other servers.
# -regpath : The root of the registry where update information is stored - can normally be ignored, as default is set for the param to the correct location
# -Servers : Can be used if a ServerListFile is not used, so we can specifity the servers in the comamnd line. If neither servers or ServerListFile specified, just check local server
# -all : retruns all patches within the S-1-5-18 registry, even if they may not be Office related.
Param($Filter = ".*",$ServerListFile, $PatchListFile, $regpath ="SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products", $servers, [switch]$all)
function Check-RegKeys {
Param($regkey,$Server)
$ServerKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine", $Server)
$SubKey = $ServerKey.OpenSubKey($regkey,$false)
If(!($SubKey)){Return}
if ($regkey -ilike "*Patch*"){
$SubKeyValues = $SubKey.GetValueNames()
if($SubKeyValues)
{
foreach($SubKeyValue in $SubKeyValues)
{
$Key = @{n="Key";e={$SubKey.Name -replace
"HKEY_LOCAL_MACHINE\\",""}}
$ValueName = @{n="ValueName";e={$SubKeyValue}}
$Value = @{n="Value";e={$_}}
$SubKey.GetValue($SubKeyValue) | ?{$_ -match $filter} | Select-Object $ValueName,$Value, @{n="path";e={$subkey.name}}
}
}
}
$SubKeyName = $SubKey.GetSubKeyNames()
foreach($subkey in $SubKeyName)
{
$SubKeyName = "$regkey\$subkey"
Check-RegKeys $SubKeyName $server
}
}
# Get server list, depending on params - if not used, just check the local server
if (!$servers){
if ($ServerListFile){
$Servers = Get-Content $ServerListFile
}
else{
$servers = $env:computername
}
}
# Get Patches
if ($PatchListFile){
try{$Patches = Get-Content $PatchListFile}catch {write-host " - Could not load Patch List File - will return all installed patches instead.."}
$patches = $null
}
# Cycle through all servers
foreach ($server in $Servers){
# Create array for results
$allresults = @()
write-host "Processing Server: $($server)"
write-host " - Getting Registry"
# Get the registry entries for the patches
$ServerPatches = Check-RegKeys $regPath $Server
write-host " - Obtained Registry"
# Format the patches so we can get the relevent information
$patchraw = $ServerPatches | Where-Object {$_.ValueName -ne "AllPatches"} | group Path
foreach ($group in $patchraw){
# Cycle through each patch
# Create object for results
$results = "" | Select Server, DisplayName, KB, InstallDate, MoreInfoURL
# Set data in object from the registry group
$results.Server = $Server
$results.DisplayName = $($group.group | Where-Object{$_.ValueName -eq "DisplayName"}).Value
$results.InstallDate = ([datetime]::ParseExact($($group.group | Where-Object{$_.ValueName -eq "Installed"}).Value,”yyyyMMdd”,$null)).ToShortDateString()
$results.MoreInfoURL = $($group.group | Where-Object{$_.ValueName -eq "MoreInfoURL"}).Value
if ($results.DisplayName -ilike "*kb*"){
$results.KB = "KB" + ([regex]::split(($results.DisplayName).ToUpper(), 'KB'))[1].split(")")[0]
}
elseif($results.MoreInfoURL -ilike "*/kb/*"){
$results.KB = "KB" + (($results.MoreInfoURL).split("/"))[-1]
}
if ($all){
$allresults += $results
}
elseif (($results.MoreInfoURL).StartsWith("http://support.microsoft.com") ){
# Filter out so only relevent patches are returned
$allresults += $results
}
}
#Remove deplicates and sort in KB order
$allresults = $allresults | Sort-Object -Unique * | Sort-Object KB
# Patches specified, so we will test for these in the registry results
if ($patches){
foreach ($patch in $Patches){
write-host " - testing patch: $patch"
$patchlookup = $allresults -match $patch
if (!$Patchlookup){
$patch | select @{Expression={$server};Name="Server"}, @{Expression={$patch};Name="KB"}, @{Expression={"False"};Name="Applied"}
}
else{
$patchlookup | select *,@{Expression={"True"};Name="Applied"}
}
}
}
else{
# No patches to test specified so retun all results
$allresults
}
}