One of the challenges of on managing patches for SCCM is ensureing that the catalog of patches not declined in WSUS stays at a reasonable number. This Script will help by connecting SCCM and check WSUS for patches that are not deployed in SCCM and are older then 30 days. When a patch in WSUS meets that crieteria it will be declined. The script uses a switch prameter to allow you to run it in a reporting mode that does not decline but just reports on the patches that would be declined. The reporting mode is the default mode the script run in to protect from accidental declines. The script will output the number of patches that were\would be declined and creates a csv file with the details of patches identified to be declined. Read more about this script and what caused its creation at http://www.mrbodean.net/2017/07/01/cleaning-up-wsus-based-on-what-you-are-not-deploying-in-configuration-manager/
<# .Synopsis Decline Updates in WSUS that are not used in COnfiguration Manager .DESCRIPTION Script to decline updates in WSUS that are not deployed and not required by any clients in Configuration Manager and that are older than 30 days. .EXAMPLE Unpublish-UnUsedCMPatches -SiteCode LAB -SCCMServer LabServer -WSUSServer LabWSUS This will report on the patches that would be declined .EXAMPLE Unpublish-UnUsedCMPatches -SiteCode LAB -SCCMServer LabServer -WSUSServer LabWSUS -Decline This will report on the patches that would be declined .NOTES Author Jon Warnken @MrBoDean jon.warnken@mrbodean.net mrbodean.net #> Function Unpublish-UnUsedCMPatches{ [CmdletBinding()] Param( # WSUS Server Name [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] [string]$WsusServer, # Use SSL for the WSUS connection. Default value is $False [Parameter()] [bool]$UseSSL = $False, # Port to use for WSUS connection. Default value is 8530 [Parameter()] [int]$PortNumber = 8530, # Configuration Manager Site Server with SMS Provider role [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] [string]$SCCMServer, # Configuration Manager Site Code [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] [string]$SiteCode, # Decline Updates. Defaults to False for safety [Parameter()] [switch]$Decline ) $outPath = Split-Path $script:MyInvocation.MyCommand.Path $outDeclineList = Join-Path $outPath "DeclinedUpdates.csv" $outDeclineListBackup = Join-Path $outPath "DeclinedUpdatesBackup.csv" if(Test-Path -Path $outDeclineList){Copy-Item -Path $outDeclineList -Destination $outDeclineListBackup -Force} "UpdateID, RevisionNumber, Title, KBArticle, SecurityBulletin, LastLevel" | Out-File $outDeclineList $cmpatchlist = Get-WmiObject -Namespace "root\SMS\site_$SiteCode" -class SMS_SoftwareUpdate -ComputerName $SCCMServer|Select-Object LocalizedDisplayName, CI_UniqueID, IsDeployed, NumMissing $cmpatchlistcount = $cmpatchlist.Count If($cmpatchlistcount -eq 0){ Throw "Error Collecting patches from $SCCMServer" return } "Found $cmpatchlistcount updates on $SCCMServer" $cmpatchlist = $cmpatchlist|?{($_.IsDeployed -eq $false) -and ($_.NumMissing -eq 0)} $cmpatchlistcount = $cmpatchlist.Count "Found $cmpatchlistcount updates on $SCCMServer that are not deployed and not required. These will be evaluated to determine if they are older then 30 days and not declined. " #Connect to the WSUS 3.0 interface. [reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") | out-null If($? -eq $False) { Write-Warning "Something went wrong connecting to the WSUS interface on $WsusServer server: $($Error[0].Exception.Message)" $ErrorActionPreference = $script:CurrentErrorActionPreference Return } $WsusServerAdminProxy = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer($WsusServer,$UseSSL,$PortNumber); if($i){Remove-Variable i} If($updatesDeclined){Remove-Variable updatesDeclined} $updatesDeclined =0 Foreach($item in $cmpatchlist){ Remove-Variable patch -ErrorAction silentlycontinue $i++ $percentComplete = "{0:N2}" -f (($i/$cmpatchlistcount) * 100) Write-Progress -Activity "Decline Unused Updates" -Status "Checking if update #$i/$cmpatchlistcount - $($item.LocalizedDisplayName)" -PercentComplete $percentComplete -CurrentOperation "$($percentComplete)% complete" Try{ $patch = $WsusServerAdminProxy.getUpdate([guid]$item.CI_UniqueID) if(($patch.IsDeclined -eq $false) -and ( ($patch.CreationDate) -lt (get-date).AddDays(-30) ) ){ Write-Progress -Activity "Decline Unused Updates" -Status "Declining update #$i/$cmpatchlistcount - $($item.LocalizedDisplayName)" -PercentComplete $percentComplete -CurrentOperation "$($percentComplete)% complete" "$($patch.Id.UpdateId.Guid), $($patch.Id.RevisionNumber), $($patch.Title), $($patch.KnowledgeBaseArticles), $($patch.SecurityBulletins), $($patch.HasSupersededUpdates)" | Out-File $outDeclineList -Append If($Decline){$patch.Decline()} $updatesDeclined++ }else{ Write-Progress -Activity "Decline Unused Updates" -Status "Update #$i/$cmpatchlistcount - $($item.LocalizedDisplayName) is already declined or was recieved withing the last 30 days" -PercentComplete $percentComplete -CurrentOperation "$($percentComplete)% complete" } }catch{ #"$($item.LocalizedDisplayName) was not found in WSUS" Write-Progress -Activity "Decline Unused Updates" -Status "Update #$i/$cmpatchlistcount - $($item.LocalizedDisplayName) was not found in WSUS" -PercentComplete $percentComplete -CurrentOperation "$($percentComplete)% complete" } } If($Decline -eq $False){"$updatesDeclined updates would have been declined"}else{"$updatesDeclined updates were declined"} }
<# .Synopsis Decline Updates in WSUS that are not used in COnfiguration Manager .DESCRIPTION Script to decline updates in WSUS that are not deployed and not required by any clients in Configuration Manager and that are older than 30 days. .EXAMPLE Unpublish-UnUsedCMPatches -SiteCode LAB -SCCMServer LabServer -WSUSServer LabWSUS This will report on the patches that would be declined .EXAMPLE Unpublish-UnUsedCMPatches -SiteCode LAB -SCCMServer LabServer -WSUSServer LabWSUS -Decline This will report on the patches that would be declined .NOTES Author Jon Warnken @MrBoDean jon.warnken@mrbodean.net mrbodean.net #> Function Unpublish-UnUsedCMPatches{ [CmdletBinding()] Param( # WSUS Server Name [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] [string]$WsusServer, # Use SSL for the WSUS connection. Default value is $False [Parameter()] [bool]$UseSSL = $False, # Port to use for WSUS connection. Default value is 8530 [Parameter()] [int]$PortNumber = 8530, # Configuration Manager Site Server with SMS Provider role [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] [string]$SCCMServer, # Configuration Manager Site Code [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] [string]$SiteCode, # Decline Updates. Defaults to False for safety [Parameter()] [switch]$Decline ) $outPath = Split-Path $script:MyInvocation.MyCommand.Path $outDeclineList = Join-Path $outPath "DeclinedUpdates.csv" $outDeclineListBackup = Join-Path $outPath "DeclinedUpdatesBackup.csv" if(Test-Path -Path $outDeclineList){Copy-Item -Path $outDeclineList -Destination $outDeclineListBackup -Force} "UpdateID, RevisionNumber, Title, KBArticle, SecurityBulletin, LastLevel" | Out-File $outDeclineList $cmpatchlist = Get-WmiObject -Namespace "root\SMS\site_$SiteCode" -class SMS_SoftwareUpdate -ComputerName $SCCMServer|Select-Object LocalizedDisplayName, CI_UniqueID, IsDeployed, NumMissing $cmpatchlistcount = $cmpatchlist.Count If($cmpatchlistcount -eq 0){ Throw "Error Collecting patches from $SCCMServer" return } "Found $cmpatchlistcount updates on $SCCMServer" $cmpatchlist = $cmpatchlist|?{($_.IsDeployed -eq $false) -and ($_.NumMissing -eq 0)} $cmpatchlistcount = $cmpatchlist.Count "Found $cmpatchlistcount updates on $SCCMServer that are not deployed and not required. These will be evaluated to determine if they are older then 30 days and not declined. " #Connect to the WSUS 3.0 interface. [reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") | out-null If($? -eq $False) { Write-Warning "Something went wrong connecting to the WSUS interface on $WsusServer server: $($Error[0].Exception.Message)" $ErrorActionPreference = $script:CurrentErrorActionPreference Return } $WsusServerAdminProxy = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer($WsusServer,$UseSSL,$PortNumber); if($i){Remove-Variable i} If($updatesDeclined){Remove-Variable updatesDeclined} $updatesDeclined =0 Foreach($item in $cmpatchlist){ Remove-Variable patch -ErrorAction silentlycontinue $i++ $percentComplete = "{0:N2}" -f (($i/$cmpatchlistcount) * 100) Write-Progress -Activity "Decline Unused Updates" -Status "Checking if update #$i/$cmpatchlistcount - $($item.LocalizedDisplayName)" -PercentComplete $percentComplete -CurrentOperation "$($percentComplete)% complete" Try{ $patch = $WsusServerAdminProxy.getUpdate([guid]$item.CI_UniqueID) if(($patch.IsDeclined -eq $false) -and ( ($patch.CreationDate) -lt (get-date).AddDays(-30) ) ){ Write-Progress -Activity "Decline Unused Updates" -Status "Declining update #$i/$cmpatchlistcount - $($item.LocalizedDisplayName)" -PercentComplete $percentComplete -CurrentOperation "$($percentComplete)% complete" "$($patch.Id.UpdateId.Guid), $($patch.Id.RevisionNumber), $($patch.Title), $($patch.KnowledgeBaseArticles), $($patch.SecurityBulletins), $($patch.HasSupersededUpdates)" | Out-File $outDeclineList -Append If($Decline){$patch.Decline()} $updatesDeclined++ }else{ Write-Progress -Activity "Decline Unused Updates" -Status "Update #$i/$cmpatchlistcount - $($item.LocalizedDisplayName) is already declined or was recieved withing the last 30 days" -PercentComplete $percentComplete -CurrentOperation "$($percentComplete)% complete" } }catch{ #"$($item.LocalizedDisplayName) was not found in WSUS" Write-Progress -Activity "Decline Unused Updates" -Status "Update #$i/$cmpatchlistcount - $($item.LocalizedDisplayName) was not found in WSUS" -PercentComplete $percentComplete -CurrentOperation "$($percentComplete)% complete" } } If($Decline -eq $False){"$updatesDeclined updates would have been declined"}else{"$updatesDeclined updates were declined"} }