This page looks best with JavaScript enabled

WSUS - Deleting Bad Drivers

 ·  ☕ 7 min read

Decline-SupersededUpdatesWithExclusionPeriod Modification

A while back I ran into an issue where the WSUS console would time out every time it was attempting to load updates. This didn’t seem to affect the operation of SCCM but it became an issue when I was on a call with Microsoft, and they wanted to blame that for a new issue that had just cropped up.

In order to get Microsoft to troubleshoot something else, I had to solve this first. They provided a script during our troubleshooting call that would still timeout, just like the console. I managed to modify that script so it pulls the updates in much smaller segments which ended up resolving our issue with the console timing out.

  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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271

  <#
    .SYNOPSIS
    This is a modification of Microsoft's "Decline-SupersededUpdatesWithExclusionPeriod.ps1" script. 
    
    .DESCRIPTION
    The problem with Microsoft's script is it tries to retrieve all updates at once. If you have a large amount of updates in your WSUS databse this may cause a timeout.
    This script allows you to decline superseded updates by date and time, down to the millisecond if necessary.

    .PARAMETER computerName
    The hostname of your WSUS Server. 

    .PARAMETER startDate
    The start datetime to start excluding updates. e.g. '2017-05-08 23:21:00'

    .PARAMETER endDate
    The end datetime to finish excluding updates. e.g. '2017-05-08 23:25:00'.
    This datetime should be after the startDate

    .PARAMETER steps
    The amount of iterations to run, used with interval.

    .PARAMETER interval
    The amount of time in seconds the script should add to the startDate and endDate

    .EXAMPLE
    .\Remove-BADWSUSUpdates.ps1 -computerName 'WSUSServer01' -startDate '2017-05-08 23:21:00' -endDate '2017-05-08 23:25:00' -steps 2 -interval 20
    This would pull all updates between 2017-05-08 23:21:00 and 2017-05-08 23:25:00 and then decline them. Because there are two steps and the interval is 20, it would then
    add 20 seconds to both dates, pull the updates again, then decline them
     e.g. Step 1. (updates between 2017-05-08 23:21:20 and 2017-05-08 23:25:20)
     e.g. Step 2. (updates between 2017-05-08 23:21:40 and 2017-05-08 23:25:40)

    
    .NOTES
    Version:        1.0
    Author:         RobotKoala - fixflare.com
    Creation Date:  02/01/2020
    Purpose/Change: Initial Script Creation
  #>


[CmdletBinding()]
param(
[Parameter(Mandatory=$true,HelpMessage='The name or IP Address of your WSUS Server')][string]$computerName,
[Parameter(Mandatory=$true,HelpMessage='The start date you want to remove updates from.')][datetime]$startDate,
[Parameter(Mandatory=$true,HelpMessage='The end time you want to remove updates from.')][datetime]$endDate,
[int]$steps=100,
[int]$interval=60
)

function Remove-BadWSUSUpdates {


Param(
	[Parameter(Mandatory=$True,Position=1)]
    [string] $UpdateServer,
	
	[Parameter(Mandatory=$False)]
    [switch] $UseSSL,
	
	[Parameter(Mandatory=$True, Position=2)]
    [int]$Port=8530,
	
    [switch] $SkipDecline,
	
    [switch] $DeclineLastLevelOnly,
	
    [Parameter(Mandatory=$False)]
    [int] $ExclusionPeriod = 90,
    [Parameter(Mandatory=$true)][datetime]$startTime,
    [Parameter(Mandatory=$true)][datetime]$endTime
)

Write-Host ''

if ($SkipDecline -and $DeclineLastLevelOnly) {
    Write-Host 'Using SkipDecline and DeclineLastLevelOnly switches together is not allowed.'
	Write-Host ''
    return
}

$outPath = [Environment]::GetFolderPath('Desktop')
$outSupersededList = Join-Path -Path $outPath -ChildPath 'SupersededUpdates.csv'
$outSupersededListBackup = Join-Path -Path $outPath -ChildPath 'SupersededUpdatesBackup.csv'
'UpdateID, RevisionNumber, Title, KBArticle, SecurityBulletin, LastLevel' | Out-File -FilePath $outSupersededList

try {
    
    if ($UseSSL) {
        Write-Host ('Connecting to WSUS server {0} on Port {1} using SSL... ' -f $UpdateServer, $Port) -NoNewLine
    } Else {
        Write-Host ('Connecting to WSUS server {0} on Port {1}... ' -f $UpdateServer, $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 {
$updatescope = New-Object -TypeName Microsoft.UpdateServices.Administration.UpdateScope

$updatescope.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::Any
$allUpdates = $wsus.GetUpdates($updatescope.ApprovedStates, $startTime,$endTime, $null, $null)
$driverUpdates = $allUpdates | Where-Object {$_.UpdateType -eq 'Driver'} 

foreach ($driverFound in $driverUpdates){
try {
$driverFound.Decline()
$wsus.DeleteUpdate($driverFound.Id.UpdateId.Guid)
write-host ('{0} was removed' -f $driverFound.Title) -ForegroundColor Green
} catch {
write-host ('{0} - {1}' -f $driverFound.Title,$_.Exception.Message) -ForegroundColor Red
}
}

}

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 -FilePath $outSupersededList -Append       
        
    }
}

Write-Host 'Done.'
Write-Host ('List of superseded updates: {0}' -f $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 ''

}
$i = 0
do {
  $i++
  write-host ('Working on {0} through {1} - Step: {2} of {3}' -f $startDate.ToShortDateString(),$endDate.ToShortDateString(),$i.ToString(),$steps) -ForegroundColor Green
  Remove-BadWSUSUpdates -UpdateServer $computerName -Port 8530 -ExclusionPeriod 90 -startTime $startDate.AddSeconds($interval) -endTime $endDate.AddSeconds($interval)
} while ($i -ile $steps - 1)
Share on

Michael
WRITTEN BY
Michael
IT Engineer