Automatically Declining Itanium Updates in WSUS using PowerShell

Working with WSUS, I sometimes find myself declining the exact same type of updates each month after Patch Tuesday. Not surprisingly, a common trend of those updates involve having the word ‘’Itanium” somewhere in the update title.  Instead of having to mess with clicking through the UI and finding all of these annoying updates, and as there are no automated declining rules available in WSUS, I think a more automated approach is necessary using the APIs and PowerShell.

Honestly, there really isn’t a whole lot of code required to make this happen. The key piece is that you need to have the WSUS Administrator console installed (for Win2K8R2 and below) or the UpdateServices module available (Win2K12 and above).

I will break down the code below and at the very end have all of it available to copy to create. My idea behind this is to just make a scheduled job that can be used to run every second Wednesday (after Patch Tuesday) to automatically decline all of these unneeded updates.

Param (
    [string]$UpdateServer = 'DC1',
    [int]$Port = 80,
    [bool]$Secure = $False
)

This is my main parameters that are configurable based on your environment. Here I decide server I will connect to, the port that is open on the server and if the connection is secure.

If (-Not (Import-Module UpdateServices -PassThru)) {
    Add-Type -Path `
"$Env:ProgramFiles\Update Services\Api\Microsoft.UpdateServices.Administration.dll" -PassThru
} 

A check is made for the UpdateServices module by actually attempting to import the module. I call –Passthru so an object is outputted which can then be used in the If statement. If no object is outputted, then it is assumed that the module doesn’t exist, the OS doesn’t support the module, etc… and then attempts to load the assembly from a known good location.

$Wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::`
GetUpdateServer($UpdateServer,$Secure,$Port)

Finally, a connection is made to the WSUS server using the parameters specified.

image

$approveState = 'Microsoft.UpdateServices.Administration.ApprovedStates' -as [type]
 
$updateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope -Property @{
    TextIncludes = 'itanium'
    ApprovedStates = $approveState::NotApproved,
    $approveState::LatestRevisionApproved,
    $approveState::HasStaleUpdateApprovals
}

image

Here I begin to piece together the filter that will be used with the query for updates. I create an object that holds the ApprovedStates enumeration which will make it easier to find what I can use with the creation of the UpdateScope object. Speaking of the UpdateScope object, this is the filtering piece that I will include with my query. I make sure that I specify itanium as for the TextIncludes propery as well as the ApprovedStates. Because I want to make sure that all non-approved and all non-declined updates have been found, I add the necessary ApprovedStates to the object.

$wsus.GetUpdates($updateScope) | ForEach {
    Write-Verbose ("Declining {0}" -f $_.Title) -Verbose
    $_.Decline()
}

image

Lastly, I can now finally begin declining all of these updates that have been found by my query. Fortunately, all I need to do is call the Decline() method associated with each update object.

You can download the source code for this below and modify it to suit your needs before deploying it in your environment as a scheduled job!

In my case, I will use PowerShell to create my scheduled job.

$triggerParam = @{
    At = "12:00 AM"
    DaysOfWeek = 'Wednesday'
    WeeksInterval = 1
    Weekly = $True
}
$trigger = New-JobTrigger @triggerParam
$JobParam = @{
    Trigger = $trigger
    FilePath = 'C:\DeclineItanium.ps1'
    Name = 'DeclineItaniumUpdates'
}
Register-ScheduledJob @JobParam

image

image

Source Code

Param (
    [string]$UpdateServer = 'DC1',
    [int]$Port = 80,
    [bool]$Secure = $False
)

If (-Not (Import-Module UpdateServices -PassThru)) {
    Add-Type -Path "$Env:ProgramFiles\Update Services\Api\Microsoft.UpdateServices.Administration.dll" -PassThru
} 

$Wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::`
GetUpdateServer($UpdateServer,$Secure,$Port)
 
$approveState = 'Microsoft.UpdateServices.Administration.ApprovedStates' -as [type]
 
$updateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope -Property @{
    TextIncludes = 'itanium'
    ApprovedStates = $approveState::NotApproved,
    $approveState::LatestRevisionApproved,
    $approveState::HasStaleUpdateApprovals
}
 
$wsus.GetUpdates($updateScope) | ForEach {
    Write-Verbose ("Declining {0}" -f $_.Title) -Verbose
    $_.Decline()
}
 
This entry was posted in powershell, WSUS and tagged , , . Bookmark the permalink.

9 Responses to Automatically Declining Itanium Updates in WSUS using PowerShell

  1. Matthew says:

    Would something of changed in Server 2012 R2 for this to not work? I have used the computer name and localhost but both fail:

    Exception calling “GetUpdateServer” with “3” argument(s): “The request failed with HTTP status 404: Not Found.”
    At C:\Scripts\Decline-WsusItaniumUpdates-2.ps1:11 char:1
    + $Wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::`
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : WebException

    You cannot call a method on a null-valued expression.
    At C:\Scripts\Decline-WsusItaniumUpdates-2.ps1:23 char:1
    + $wsus.GetUpdates($updateScope) | ForEach {
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

  2. Scott from Detroit says:

    Don’t forget that some of the Itanium updates are labeled as “IA64”, not Itanium.

  3. ishtiyaq says:

    hi,

    how do you add more text parameters?

  4. ishtiyaq says:

    how can you use the text parameter to search for more than one word?

  5. Pingback: Installing and Configuring WSUS with Powershell | smsagent

  6. Very Nice!!! Thank you so much, this is a time saver for me at the job!

  7. My WSUS server is on its last legs and keeps timing out, but this is the code I’m hoping to eventually use:

    [Cmdletbinding(SupportsShouldProcess=$True)]

    Param (
    [string]$UpdateServer = ‘jdhit-dc01’,
    [int]$Port = 80,
    [string]$Text=”itanium”,
    [bool]$Secure = $False
    )

    If (-Not (Import-Module UpdateServices -PassThru -errorAction SilentlyContinue)) {
    Write-Verbose “Adding WSUS DLL”
    Try {
    Add-Type -Path “$Env:ProgramFiles\Update Services\Api\Microsoft.UpdateServices.Administration.dll” -ErrorAction Stop
    }
    Catch {
    Write-Warning “Failed to import or load Update Services”
    #bail out
    Return
    }
    }

    Write-Verbose “Connecting to $Updateserver”
    $Wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer($UpdateServer,$Secure,$Port)

    $approveState = ‘Microsoft.UpdateServices.Administration.ApprovedStates’ -as [type]

    Write-Verbose “Filtering for updates where text includes $text”
    $updateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope -Property @{
    TextIncludes = $Text
    ApprovedStates = $approveState::NotApproved,
    $approveState::LatestRevisionApproved,
    $approveState::HasStaleUpdateApprovals
    }

    Write-Verbose “Getting updates”
    $wsus.GetUpdates($updateScope) | ForEach {
    Write-Verbose (“Declining {0}” -f $_.Title) -Verbose
    if ($pscmdlet.ShouldProcess($_.title)) {
    $_.Decline()
    }
    }

  8. This is awesome. I tweaked your code to make the text a parameter so that I can also decline things like XP updates. I also added support for -WhatIf which I think is important whenever we have a script or function that deletes things.

    • Boe Prox says:

      Thanks, Jeff!
      The only reason why I left out the -WhatIf support is because I had this as scheduled job in mind to ‘set and forget’. But you are right, it is always good practice to throw in WhatIf support for anything that performs some sort of action such as delete in a script/function.
      I’d love to see your finished script with the extra params and such!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s