<# .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 }