Lately I have been seeing high CPU (90-100%) usage on servers where the Windows Server Updates Services (WSUS) is installed.
This is mainly caused by updates that is superseded, and is filling the database causing the CPU to spike.
You can clear these updates with the following PowerShell script.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
# =============================================== # Script to decline superseeded updates in WSUS. # =============================================== # It's recommended to run the script with the -SkipDecline switch to see how many superseded updates are in WSUS and to TAKE A BACKUP OF THE SUSDB before declining the updates. # Parameters: # $UpdateServer = Specify WSUS Server Name # $UseSSL = Specify whether WSUS Server is configured to use SSL # $Port = Specify WSUS Server Port # $SkipDecline = Specify this to do a test run and get a summary of how many superseded updates we have # $DeclineLastLevelOnly = Specify whether to decline all superseded updates or only last level superseded updates # $ExclusionPeriod = Specify the number of days between today and the release date for which the superseded updates must not be declined. Eg, if you want to keep superseded updates published within the last 2 months, specify a value of 60 (days) # Supersedence chain could have multiple updates. # For example, Update1 supersedes Update2. Update2 supersedes Update3. In this scenario, the Last Level in the supersedence chain is Update3. # To decline only the last level updates in the supersedence chain, specify the DeclineLastLevelOnly switch # Usage: # ======= # To do a test run against WSUS Server without SSL # Decline-SupersededUpdates.ps1 -UpdateServer SERVERNAME -Port 8530 -SkipDecline # To do a test run against WSUS Server using SSL # Decline-SupersededUpdates.ps1 -UpdateServer SERVERNAME -UseSSL -Port 8531 -SkipDecline # To decline all superseded updates on the WSUS Server using SSL # Decline-SupersededUpdates.ps1 -UpdateServer SERVERNAME -UseSSL -Port 8531 # To decline only Last Level superseded updates on the WSUS Server using SSL # Decline-SupersededUpdates.ps1 -UpdateServer SERVERNAME -UseSSL -Port 8531 -DeclineLastLevelOnly # To decline all superseded updates on the WSUS Server using SSL but keep superseded updates published within the last 2 months (60 days) # Decline-SupersededUpdates.ps1 -UpdateServer SERVERNAME -UseSSL -Port 8531 -ExclusionPeriod 60 [CmdletBinding()] Param( [Parameter(Mandatory=$false,Position=1)] [string] $UpdateServer = "NETC-WSUP-APP01", [Parameter(Mandatory=$False)] [switch] $UseSSL = $false, [Parameter(Mandatory=$false, Position=2)] $Port = "8530", [switch] $SkipDecline, [switch] $DeclineLastLevelOnly, [Parameter(Mandatory=$False)] [int] $ExclusionPeriod = 15 ) Write-Host "" if ($SkipDecline -and $DeclineLastLevelOnly) { Write-Host "Using SkipDecline and DeclineLastLevelOnly switches together is not allowed." Write-Host "" return } $outPath = Split-Path $script:MyInvocation.MyCommand.Path $outSupersededList = Join-Path $outPath "SupersededUpdates.csv" $outSupersededListBackup = Join-Path $outPath "SupersededUpdatesBackup.csv" "UpdateID, RevisionNumber, Title, KBArticle, SecurityBulletin, LastLevel" | Out-File $outSupersededList try { if ($UseSSL) { Write-Host "Connecting to WSUS server $UpdateServer on Port $Port using SSL... " -NoNewLine } Else { Write-Host "Connecting to WSUS server $UpdateServer on Port $Port... " -NoNewLine } [reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") | out-null $wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer($UpdateServer, $UseSSL, $Port); } catch [System.Exception] { Write-Host "Failed to connect." Write-Host "Error:" $_.Exception.Message Write-Host "Please make sure that WSUS Admin Console is installed on this machine" Write-Host "" $wsus = $null } if ($wsus -eq $null) { return } Write-Host "Connected." $countAllUpdates = 0 $countSupersededAll = 0 $countSupersededLastLevel = 0 $countSupersededExclusionPeriod = 0 $countSupersededLastLevelExclusionPeriod = 0 $countDeclined = 0 Write-Host "Getting a list of all updates... " -NoNewLine try { $allUpdates = $wsus.GetUpdates() } catch [System.Exception] { Write-Host "Failed to get updates." Write-Host "Error:" $_.Exception.Message Write-Host "If this operation timed out, please decline the superseded updates from the WSUS Console manually." Write-Host "" return } Write-Host "Done" Write-Host "Parsing the list of updates... " -NoNewLine foreach($update in $allUpdates) { $countAllUpdates++ if ($update.IsDeclined) { $countDeclined++ } if (!$update.IsDeclined -and $update.IsSuperseded) { $countSupersededAll++ if (!$update.HasSupersededUpdates) { $countSupersededLastLevel++ } if ($update.CreationDate -lt (get-date).AddDays(-$ExclusionPeriod)) { $countSupersededExclusionPeriod++ if (!$update.HasSupersededUpdates) { $countSupersededLastLevelExclusionPeriod++ } } "$($update.Id.UpdateId.Guid), $($update.Id.RevisionNumber), $($update.Title), $($update.KnowledgeBaseArticles), $($update.SecurityBulletins), $($update.HasSupersededUpdates)" | Out-File $outSupersededList -Append } } Write-Host "Done." Write-Host "List of superseded updates: $outSupersededList" Write-Host "" Write-Host "Summary:" Write-Host "========" Write-Host "All Updates =" $countAllUpdates Write-Host "Any except Declined =" ($countAllUpdates - $countDeclined) Write-Host "All Superseded Updates =" $countSupersededAll Write-Host " Superseded Updates (Intermediate) =" ($countSupersededAll - $countSupersededLastLevel) Write-Host " Superseded Updates (Last Level) =" $countSupersededLastLevel Write-Host " Superseded Updates (Older than $ExclusionPeriod days) =" $countSupersededExclusionPeriod Write-Host " Superseded Updates (Last Level Older than $ExclusionPeriod days) =" $countSupersededLastLevelExclusionPeriod Write-Host "" $i = 0 if (!$SkipDecline) { Write-Host "SkipDecline flag is set to $SkipDecline. Continuing with declining updates" $updatesDeclined = 0 if ($DeclineLastLevelOnly) { Write-Host " DeclineLastLevel is set to True. Only declining last level superseded updates." foreach ($update in $allUpdates) { if (!$update.IsDeclined -and $update.IsSuperseded -and !$update.HasSupersededUpdates) { if ($update.CreationDate -lt (get-date).AddDays(-$ExclusionPeriod)) { $i++ $percentComplete = "{0:N2}" -f (($updatesDeclined/$countSupersededLastLevelExclusionPeriod) * 100) Write-Progress -Activity "Declining Updates" -Status "Declining update #$i/$countSupersededLastLevelExclusionPeriod - $($update.Id.UpdateId.Guid)" -PercentComplete $percentComplete -CurrentOperation "$($percentComplete)% complete" try { $update.Decline() $updatesDeclined++ } catch [System.Exception] { Write-Host "Failed to decline update $($update.Id.UpdateId.Guid). Error:" $_.Exception.Message } } } } } else { Write-Host " DeclineLastLevel is set to False. Declining all superseded updates." foreach ($update in $allUpdates) { if (!$update.IsDeclined -and $update.IsSuperseded) { if ($update.CreationDate -lt (get-date).AddDays(-$ExclusionPeriod)) { $i++ $percentComplete = "{0:N2}" -f (($updatesDeclined/$countSupersededAll) * 100) Write-Progress -Activity "Declining Updates" -Status "Declining update #$i/$countSupersededAll - $($update.Id.UpdateId.Guid)" -PercentComplete $percentComplete -CurrentOperation "$($percentComplete)% complete" try { $update.Decline() $updatesDeclined++ } catch [System.Exception] { Write-Host "Failed to decline update $($update.Id.UpdateId.Guid). Error:" $_.Exception.Message } } } } } Write-Host " Declined $updatesDeclined updates." if ($updatesDeclined -ne 0) { Copy-Item -Path $outSupersededList -Destination $outSupersededListBackup -Force Write-Host " Backed up list of superseded updates to $outSupersededListBackup" } } else { Write-Host "SkipDecline flag is set to $SkipDecline. Skipped declining updates" } Write-Host "" Write-Host "Done" Write-Host "" |
You can set this up with a task scheduler to run once a month or so.