SCRIPT LIBRARY · POWERSHELL
Check MECM Software Update Deployment Status with PowerShell
One object per update deployment with targeted, compliant, failed and unknown counts, so you can spot the deployments that need you.
- What it does
- Pulls the summary numbers ConfigMgr keeps for every software update deployment and returns them as objects with a compliance percentage. Filter by group, collection or age, or ask for only the ones with problems.
- Requires
- Windows PowerShell 5.1
- Configuration Manager console installed, for the ConfigurationManager module
- Permissions
- Read-only access to software update deployments. Read-only Analyst is plenty.
- Runs on
- Any machine with the Configuration Manager console
- Tested
- Parse-checked and dry-run with mocked cmdlets in PowerShell 7.4
The console's Monitoring workspace will show you deployment status, one deployment at a time, with a pie chart and a lot of clicking. That's fine when you're looking at one thing. It's miserable on a Monday morning when you want to know which of last month's twenty deployments are still limping along.
ConfigMgr already keeps a summary row for every deployment: how many machines were targeted, how many are compliant, how many failed, how many haven't said anything yet. Get-CMDeployment hands you all of it. This script keeps the software update ones, works out a percentage, and gives you objects you can sort, filter, or drop into a CSV.
The old version of this post listed update groups, packages and "successful deployments" in three separate tables, and the success filter checked a property that doesn't exist, so it never actually filtered anything. This rewrite focuses on the question people really ask: how are my deployments doing?
<#
.SYNOPSIS
Shows how every software update deployment is doing: targeted, compliant, failed, unknown.
.DESCRIPTION
Reads the deployment summary data ConfigMgr (MECM) keeps for each deployment and returns one
object per software update deployment, with a compliance percentage. Filter by update group
name, collection, or age, or ask for only the deployments that need attention. Read-only.
.PARAMETER SiteCode
Your three-character site code, for example ABC.
.PARAMETER ProviderMachineName
The SMS Provider server. Defaults to this computer.
.PARAMETER GroupNameLike
Wildcard filter on the software update group name. Default: all.
.PARAMETER CollectionNameLike
Wildcard filter on the target collection name. Default: all.
.PARAMETER Days
Only deployments created in the last N days. 0 means no limit. Default: 90.
.PARAMETER Threshold
With -NeedsAttention, deployments below this compliance percentage are returned. Default: 95.
.PARAMETER NeedsAttention
Return only deployments with errors or compliance under the threshold.
.EXAMPLE
.\Get-CMUpdateDeploymentStatus.ps1 -SiteCode ABC -NeedsAttention
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)][ValidatePattern('^[A-Za-z0-9]{3}$')][string]$SiteCode,
[string]$ProviderMachineName = $env:COMPUTERNAME,
[string]$GroupNameLike = '*',
[string]$CollectionNameLike = '*',
[ValidateRange(0, 3650)][int]$Days = 90,
[ValidateRange(0, 100)][int]$Threshold = 95,
[switch]$NeedsAttention
)
$ErrorActionPreference = 'Stop'
if (-not (Get-Module ConfigurationManager)) {
if (-not $env:SMS_ADMIN_UI_PATH) { throw 'The Configuration Manager console is not installed on this machine.' }
Import-Module (Join-Path $env:SMS_ADMIN_UI_PATH '..\ConfigurationManager.psd1')
}
if (-not (Get-PSDrive -Name $SiteCode -PSProvider CMSite -ErrorAction SilentlyContinue)) {
New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $ProviderMachineName | Out-Null
}
$since = if ($Days -gt 0) { (Get-Date).AddDays(-$Days) } else { [datetime]::MinValue }
Push-Location "$($SiteCode):\"
try {
# FeatureType 5 = software updates. Everything else (apps, task sequences, baselines) is skipped.
$deployments = Get-CMDeployment | Where-Object {
$_.FeatureType -eq 5 -and
$_.SoftwareName -like $GroupNameLike -and
$_.CollectionName -like $CollectionNameLike -and
$_.DeploymentTime -ge $since
}
$results = foreach ($d in $deployments) {
$targeted = [int]$d.NumberTargeted
$percent = if ($targeted -gt 0) { [math]::Round(100 * $d.NumberSuccess / $targeted, 1) } else { $null }
[pscustomobject]@{
UpdateGroup = $d.SoftwareName
Collection = $d.CollectionName
Created = $d.DeploymentTime
Deadline = $d.EnforcementDeadline
Targeted = $targeted
Compliant = [int]$d.NumberSuccess
InProgress = [int]$d.NumberInProgress
Failed = [int]$d.NumberErrors
Unknown = [int]$d.NumberUnknown
PercentOK = $percent
LastSummary = $d.SummarizationTime
DeploymentID = $d.DeploymentID
}
}
if ($NeedsAttention) {
$results = $results | Where-Object { $_.Failed -gt 0 -or ($null -ne $_.PercentOK -and $_.PercentOK -lt $Threshold) }
}
$results | Sort-Object Created -Descending
}
finally {
Pop-Location
}
Parameters
| Parameter | Type | Default | What it's for |
|---|---|---|---|
-SiteCode | string | — | Your three-character site code, like ABC. |
-ProviderMachineName | string | $env:COMPUTERNAME | The SMS Provider server. |
-GroupNameLike | string | * | Wildcard filter on the software update group name, like '2026-09*'. |
-CollectionNameLike | string | * | Wildcard filter on the target collection. |
-Days | int | 90 | Only deployments created in the last N days. Use 0 for everything. |
-Threshold | int | 95 | The compliance percentage that counts as healthy, used with -NeedsAttention. |
-NeedsAttention | switch | — | Only return deployments with failures or compliance below the threshold. |
Run it
Everything from the last 90 days, newest first.
.\Get-CMUpdateDeploymentStatus.ps1 -SiteCode ABC | Format-Table UpdateGroup, Collection, Targeted, Compliant, Failed, PercentOKJust the deployments that need a look.
.\Get-CMUpdateDeploymentStatus.ps1 -SiteCode ABC -NeedsAttentionHow this month's groups are doing on servers, saved for the change meeting.
.\Get-CMUpdateDeploymentStatus.ps1 -SiteCode ABC -GroupNameLike '2026-09*' -CollectionNameLike '*Server*' | Export-Csv .\server-patching.csv -NoTypeInformationAnything with more than 20 machines that haven't reported at all.
.\Get-CMUpdateDeploymentStatus.ps1 -SiteCode ABC -Days 30 | Where-Object Unknown -gt 20What you'll see
UpdateGroup Collection Targeted Compliant Failed Unknown PercentOK
----------- ---------- -------- --------- ------ ------- ---------
2026-09 Workstation Updates Pilot - Workstations 50 41 2 3 82
2026-09 Server Updates Servers - Group A 64 60 0 4 93.8
2026-08 Workstation Updates All Workstations 812 797 1 14 98.2
2026-08 Server Updates Servers - Group B 71 71 0 0 100
How it works
- Connect to the site. It loads the ConfigurationManager module from the console install and maps the site drive if needed.
- Get every deployment summary.
Get-CMDeploymentreturns one summary row per deployment. The script keeps the software update ones (FeatureType5) that match your name, collection and age filters. - Do the math. For each one it builds an object with the counts ConfigMgr tracks (targeted, compliant, in progress, failed, unknown) and a
PercentOKrounded to one decimal. A deployment with nothing targeted gets$nullinstead of a divide-by-zero. - Filter if asked. With
-NeedsAttention, only deployments with at least one failure or compliance under-Thresholdcome back. - Sort newest first and return objects, not a table, so you can pipe them wherever.
Take it further
- Email the problem list. Schedule it with
-NeedsAttentionand mail the results every Monday.Send-MailMessageis deprecated, so Graph'sSend-MgUserMailis the better route now. - Drill into failures. The summary tells you which deployment is unhappy. Open it under Monitoring > Deployments in the console to see which machines failed and the error codes behind them.
- Pair it with the deployment script. New-CMTopUpdateDeployment pushes the most-needed updates to a pilot; this tells you how the pilot went.
Things that'll trip you up
- The numbers are summarized, not live. These counts come from ConfigMgr's summarization, which runs on a schedule. The LastSummary column tells you how old they are. If you need fresher numbers, run summarization from the deployment in the console first.
- Unknown isn't the same as failed. Unknown usually means the client hasn't sent state messages yet, or it's offline, or it's broken. A high Unknown count on an old deployment is worth chasing, because those machines might not be getting anything at all.
- FeatureType 5 means software updates. Get-CMDeployment returns every kind of deployment. The script keeps FeatureType 5 and ignores applications, packages, task sequences and baselines.
- Targeted is a snapshot too. If machines joined or left the collection after the deployment, the targeted count moves. A percentage that drops for no obvious reason is often just new machines arriving.