Skip to content

Instantly share code, notes, and snippets.

@SMSAgentSoftware
Created May 22, 2017 17:07
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SMSAgentSoftware/ddf3e45e020808914d6b642d071a0090 to your computer and use it in GitHub Desktop.
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.
# 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
}
@SrikeshMaharaj
Copy link

This is amazing work... Is there anyway to apply this to a Collection instead of Computer name?
(I have approx. 12000 computer objects)...

Thanks....

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment