Created
May 22, 2017 17:07
-
-
Save SMSAgentSoftware/ddf3e45e020808914d6b642d071a0090 to your computer and use it in GitHub Desktop.
Gets the status of software update/s on remote system/s. This is the MULTI-THREADED version.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Get-PatchStatus.ps1 | |
# Gets the status of software update/s on remote system/s | |
# | |
# The is the MULTI-THREADED version | |
# | |
# IMPORTANT: Requires my [BackgroundJob] custom class, available here: http://smsagent.wordpress.com/posh-5-custom-classes/background-job/ | |
# | |
# Author: Trevor Jones | |
# May-2017 | |
[CmdletBinding()] | |
Param | |
( | |
[Parameter(Mandatory=$false, | |
ValueFromPipelineByPropertyName=$true, | |
ValueFromPipeline=$true | |
)] | |
[string[]]$ComputerName = $env:COMPUTERNAME, | |
[Parameter(Mandatory=$true, | |
ValueFromPipelineByPropertyName=$true, | |
ValueFromPipeline=$true | |
)] | |
[array]$ArticleID, | |
[Parameter(Mandatory=$false | |
)] | |
[int]$ThrottleLimit = 32 | |
) | |
Begin | |
{ | |
# Create some arrays | |
$Jobs = @() | |
$Results = @() | |
# Start a timer | |
$Timer = New-Object System.Diagnostics.Stopwatch | |
$Timer.Start() | |
} | |
Process | |
{ | |
# Process each computer | |
Foreach ($Computer in $ComputerName) | |
{ | |
$Code = { | |
Param($Computer,$ArticleID) | |
# Create a datatable to hold the results | |
$TempTable = New-Object System.Data.DataTable | |
$TempTable.Columns.AddRange(@("ComputerName","Article ID","SCCM Installed Status","SCCM Pending Status","WMI InstalledOn Date","Comments")) | |
Try | |
{ | |
# Make a cim session | |
$CimSession = New-CimSession -ComputerName $Computer -OperationTimeoutSec 5 -ErrorAction Stop | |
} | |
Catch | |
{ | |
$Comments = $_.Exception.Message | |
} | |
# Report any error | |
If ($Comments) | |
{ | |
[void]$TempTable.Rows.Add($Computer,$null,$null,$null,$null,$Comments) | |
Remove-Variable -Name Comments -Force | |
} | |
Else | |
{ | |
# Process each KB | |
Foreach ($KB in $ArticleID) | |
{ | |
# Check SCCM WMI | |
Try | |
{ | |
$CCMUpdate = Get-CimInstance -CimSession $CimSession -Query "SELECT * FROM CCM_UpdateStatus where Article=$KB" -Namespace 'root\ccm\SoftwareUpdates\UpdatesStore' -ErrorAction Stop | Select -First 1 | Select -ExpandProperty Status | |
} | |
Catch {} | |
# Check Win32 WMI | |
Try | |
{ | |
$WMIUpdate = Get-CimInstance -CimSession $CimSession -Query "SELECT * FROM Win32_QuickFixEngineering where HotFixID='KB$KB'" -Namespace 'root\cimv2' -ErrorAction Stop | Select -ExpandProperty InstalledOn | |
} | |
Catch {} | |
# If relevant, check if the update is in a pending state | |
If ($CCMUpdate -ne "Installed" -or !$WMIUpdate) | |
{ | |
Try | |
{ | |
$CCMUpdatePending = Get-CimInstance -CimSession $CimSession -Query "SELECT * FROM CCM_SoftwareUpdate where ArticleID=$KB" -Namespace 'ROOT\ccm\ClientSDK' -ErrorAction Stop | |
} | |
Catch {} | |
If ($CCMUpdatePending) | |
{ | |
# Convert the EvaluationState value | |
Switch ($CCMUpdatePending.EvaluationState) | |
{ | |
0 {$PendingState = "None"} | |
1 {$PendingState = "Available"} | |
2 {$PendingState = "Submitted"} | |
3 {$PendingState = "Detecting"} | |
4 {$PendingState = "PreDownload"} | |
5 {$PendingState = "Downloading"} | |
6 {$PendingState = "WaitInstall"} | |
7 {$PendingState = "Installing"} | |
8 {$PendingState = "PendingSoftReboot"} | |
9 {$PendingState = "PendingHardReboot"} | |
10 {$PendingState = "WaitReboot"} | |
11 {$PendingState = "Verifying"} | |
12 {$PendingState = "InstallComplete"} | |
13 {$PendingState = "Error"} | |
14 {$PendingState = "WaitServiceWindow"} | |
15 {$PendingState = "WaitUserLogon"} | |
16 {$PendingState = "WaitUserLogoff"} | |
17 {$PendingState = "WaitJobUserLogon"} | |
18 {$PendingState = "WaitUserReconnect"} | |
19 {$PendingState = "PendingUserLogoff"} | |
20 {$PendingState = "PendingUpdate"} | |
21 {$PendingState = "WaitingRetry"} | |
22 {$PendingState = "WaitPresModeOff"} | |
23 {$PendingState = "WaitForOrchestration"} | |
} | |
} | |
} | |
# Add the results to the table | |
If ($PendingState) | |
{ | |
[void]$TempTable.Rows.Add($Computer,$KB,$CCMUpdate,$PendingState,$WMIUpdate,$Comments) | |
Remove-Variable -Name PendingState -Force | |
} | |
Else | |
{ | |
[void]$TempTable.Rows.Add($Computer,$KB,$CCMUpdate,$null,$WMIUpdate,$Comments) | |
If ($CCMUpdate) | |
{ | |
Remove-Variable -Name CCMUpdate -Force | |
} | |
If ($WMIUpdate) | |
{ | |
Remove-Variable -Name WMIUpdate -Force | |
} | |
} | |
} | |
} | |
# Close the cim session | |
If ($CimSession) | |
{ | |
Remove-CimSession -CimSession $CimSession | |
} | |
# Return the results | |
Return $TempTable | |
} | |
$Job = [BackgroundJob]::new($Code,@($Computer,$ArticleID)) | |
$Jobs += $Job | |
if (($Jobs.GetStatus() | where {$_.State -eq "Running"}).Count -ge $ThrottleLimit) | |
{ | |
Do {Start-Sleep -Seconds 1} | |
Until (($Jobs.GetStatus() | where {$_.State -eq "Running"}).Count -lt $ThrottleLimit) | |
} | |
Write-Verbose "[$Computer] Starting job" | |
$Job.Start() | |
} | |
} | |
End | |
{ | |
# Wait until all jobs have completed | |
Do {} | |
Until ($Jobs.GetStatus().State -notcontains "Running") | |
# Gather results and cleanup runspaces | |
$Jobs | foreach { | |
[void]$_.Receive() | |
$Results += $_.Result | |
$_.Remove() | |
} | |
$Timer.Stop() | |
Write-Verbose "Completed in $($Timer.Elapsed.Minutes) minutes and $($Timer.Elapsed.Seconds) seconds." | |
# Return the results | |
Return $Results | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is amazing work... Is there anyway to apply this to a Collection instead of Computer name?
(I have approx. 12000 computer objects)...
Thanks....