Skip to content

Instantly share code, notes, and snippets.

@mhudasch
Last active June 12, 2018 08:27
Show Gist options
  • Save mhudasch/3d91f7f03f6a513cf19c0b18d00ecfba to your computer and use it in GitHub Desktop.
Save mhudasch/3d91f7f03f6a513cf19c0b18d00ecfba to your computer and use it in GitHub Desktop.
Find pending windows updates.
Function Get-PendingWindowsUpdate {
[CmdletBinding()]
param(
[Parameter(Position = 0, ValueFromPipeline = $true)]
[string[]]$ComputerName = $env:COMPUTERNAME)
Process {
# Use this scriptblock for both remote and local execution
$unifiedScriptBlock = [scriptblock]{
param([string]$computer)
$ErrorActionPreference = $using:ErrorActionPreference;
$VerbosePreference = $using:VerbosePreference;
$ConfirmPreference = $using:ConfirmPreference;
$start = [datetime]::now;
# Prepare output
$result = New-Object psobject -Property @{
ComputerName=$computer;
Error=$null;
Updates=@();
LastCheck=$null;
Took=0;
};
Try {
# Only do this when you are in the local administrators group
If (!([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
throw "You are not running this as an Administrator!`nPlease re-run this with an Administrator account.";
}
# Don't go further if the machine already needs a reboot because of an earlier update.
IF (Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired") {
throw "There is an update related reboot pending. Please reboot the machine an perform this command again."
}
# Before we perform a slow search for updates we check the registry flags to maybe skip the search
# When there was a search in the last 3 days and no updates need to be installed return here
If (Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\UAS") {
If ( ((Get-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\UAS").GetValue("UpdateCount") -eq 0) -and
((Get-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\Results\Detect").GetValue("LastSuccessTime") -lt
([datetime]::Now.Subtract([timespan]::FromDays(3)))) ) {
$result.LastCheck = ([datetime](Get-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\Results\Detect").GetValue("LastSuccessTime")).ToLocalTime();
$result.Took = [datetime]::Now.Subtract($start).TotalMilliseconds;
return $result;
}
}
#Create Session COM object
Write-Verbose ("Creating COM object for WSUS Session. > Computer: {0}" -f $computer);
$updateSession = New-Object -ComObject microsoft.update.session;
#Configure Session COM Object
Write-Verbose ("Creating COM object for WSUS update Search. > Computer: {0}" -f $computer);
$updateSearcher = $updateSession.CreateUpdateSearcher();
#Configure Searcher object to look for Updates awaiting installation
Write-Verbose ("Searching for WSUS updates on client. > Computer: {0}" -f $computer);
$searchResult = $updateSearcher.Search("IsInstalled=0");
$updates = $searchResult.Updates;
#Verify if Updates need installed
Write-Verbose ("Verifing that updates are available to install. > Computer: {0}" -f $computer);
If ($updates.Count -gt 0) {
#Updates are waiting to be installed
Write-Verbose ("Found {1} update\s! > Computer: {0}" -f $computer, $updates.Count);
#Cache the count to make the For loop run faster
$count = $updates.Count
#Begin iterating through Updates available for installation
Write-Verbose ("Iterating through list of updates. > Computer: {0}" -f $computer);
For ($i=0; $i -lt $count; $i++) {
#Create object holding update
$update = $updates.Item($i)
$result.Updates += New-Object psobject -Property @{
Computer = $computer;
Title = $update.Title;
KB = $($update.KBArticleIDs);
SecurityBulletin = $($update.SecurityBulletinIDs);
MsrcSeverity = $update.MsrcSeverity;
IsDownloaded = $update.IsDownloaded;
Url = $($update.MoreInfoUrls);
Categories = ($update.Categories | Select-Object -ExpandProperty Name);
RebootRequired = $update.RebootRequired;
MaxDownloadSize = $update.MaxDownloadSize;
MinDownloadSize = $update.MinDownloadSize;
ID = $update.Identity.UpdateID;
Revision = $update.Identity.RevisionNumber;
BundledUpdates = @($update.BundledUpdates) | ForEach-Object {
New-Object psobject -Property @{
Title = $_.Title;
DownloadUrl = @($_.DownloadContents).DownloadUrl;
};
};
};
}
} Else {
#Nothing to install at this time
Write-Verbose ("No updates to install. > Computer: {0}" -f $computer);
}
$result.LastCheck = $([datetime]::Now);
$result.Took = [datetime]::Now.Subtract($start).TotalMilliseconds;
Return $result;
} Catch {
$result.Error = $Error[0];
$result.LastCheck = $([datetime]::Now);
$result.Took = [datetime]::Now.Subtract($start).TotalMilliseconds;
Return $result;
}
};
$results = $ComputerName | ForEach-Object { $computer = $_;
Try {
If(!(Test-Connection -ComputerName $computer -Count 1 -Quiet)) {
throw ("Connecting to remote computer {0} failed." -f $computer);
} Else {
If($computer -eq $env:COMPUTERNAME) {
Start-Job -ScriptBlock $unifiedScriptBlock -ArgumentList @($computer);
} Else {
Invoke-Command -ComputerName $computer -ScriptBlock $unifiedScriptBlock -ArgumentList @($computer) -AsJob -HideComputerName;
}
}
} Catch {
Return @{
ComputerName=$computer;
Error=$_;
Updates=@();
LastCheck=$([datetime]::Now);
Took = 0;
};
}
};
$jobs = $results | Where-Object { $_ -is [System.Management.Automation.Job] };
$jobs | Wait-Job | Out-Null;
$u = @($results | Where-Object { $_ -is [hashtable] } | ForEach-Object { New-Object psobject -Property $_; });
$jobs | Receive-Job | ForEach-Object {
$u += $_;
};
$jobs | Remove-Job | Out-Null;
# hide the workflow meta data
Return [PsObject[]]($u | ForEach-Object { $_ | Select-Object -Property * -ExcludeProperty PSComputerName,PSSourceJobInstanceId,RunspaceId,PSShowComputerName });
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment