WSUS Cleanup with Powershell

If you manage a Windows Server Update Services (WSUS) server, you probably run the Server Cleanup Wizard every once and a while. It removes old and superseded updates and computers that haven’t reported their status for more than 30 days. Wouldn’t it be nice to schedule such a cleanup to run every month? Too bad there’s no command line tool I know of that can help you out with this. Powershell to the rescue!
Powershell can not only run the built-in commandlets or even those added by snapins. It can leverage the full power of the .NET Framework. Browse the MSDN Library if you want to find more cool things you can do with it. Here’s a script that uses this information to run the cleanup wizard:

#Region VARIABLES
 
# WSUS Connection Parameters:
[String]$updateServer = "myWSUSServer.domain.local"
[Boolean]$useSecureConnection = $False
[Int32]$portNumber = 80
 
# Cleanup Parameters:
# Decline updates that have not been approved for 30 days or more, are not currently needed by any clients, and are superseded by an aproved update.
[Boolean]$supersededUpdates = $True
# Decline updates that aren't approved and have been expired my Microsoft.
[Boolean]$expiredUpdates = $True
# Delete updates that are expired and have not been approved for 30 days or more.
[Boolean]$obsoleteUpdates = $True
# Delete older update revisions that have not been approved for 30 days or more.
[Boolean]$compressUpdates = $True
# Delete computers that have not contacted the server in 30 days or more.
[Boolean]$obsoleteComputers = $True
# Delete update files that aren't needed by updates or downstream servers.
[Boolean]$unneededContentFiles = $True
 
#EndRegion VARIABLES
 
#Region SCRIPT
 
# Load .NET assembly
[void][reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration")
 
# Connect to WSUS Server
$Wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer($updateServer,$useSecureConnection,$portNumber)
 
# Perform Cleanup
$CleanupManager = $Wsus.GetCleanupManager()
$CleanupScope = New-Object Microsoft.UpdateServices.Administration.CleanupScope($supersededUpdates,$expiredUpdates,$obsoleteUpdates,$compressUpdates,$obsoleteComputers,$unneededContentFiles)
$CleanupManager.PerformCleanup($CleanupScope)
 
#EndRegion SCRIPT

Download it here: Cleanup-Wsus (rename to .ps1)

Happy scheduling!

Hugo

»crosslinked«

27 thoughts on “WSUS Cleanup with Powershell”

  1. Thanks Hugo, I was looking for such script! I have a big hierarchy of WSUS replicas and it’s very messy having to do the clean manually, sever by server.

    I will give it a try!

    Cheers,
    Miguel

  2. Receiving error:

    Unable to find type [Microsoft.UpdateServices.Administration.AdminProxy]: make sure that the assembly containing this type is loaded.
    At line:1 char:61
    + $Wsus = [Microsoft.UpdateServices.Administration.AdminProxy]: <<<< :getUpdateServer($updateServer,$useSecureConnectio
    n,$portNumber)

    any ideaS?

  3. Please can you help?
    I have the port set to 443 is that problem below?
    Kevin

    Exception calling “GetUpdateServer” with “3” argument(s): “The underlying connection was closed: An unexpected error oc
    curred on a receive.”
    At C:\Scripts\Scripts\cleanup-wsus.ps1:30 char:78
    + $Wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer( <<<< $updateServer,$useSecureConnectio
    n,$portNumber)
    You cannot call a method on a null-valued expression.
    At C:\Scripts\Scripts\cleanup-wsus.ps1:33 char:42
    + $CleanupManager = $Wsus.GetCleanupManager( <<<< )
    You cannot call a method on a null-valued expression.
    At C:\Scripts\Scripts\cleanup-wsus.ps1:35 char:31
    + $CleanupManager.PerformCleanup( <<<< $CleanupScope)

  4. Should you use this script and encounter the following error:
    Exception calling “GetUpdateServer” with “3” argument(s): “The request failed with HTTP status 407: Proxy Authentication Required.”

    Try running the script locally on your WSUS server, and change line 4 to (instead of using the FQDN):
    [String]$updateServer = “localhost”

  5. I am using WSUS 3 SP2 and from powershell i get the following error.

    New-Object : Cannot find an overload for “.ctor” and
    At C:cleanup-wsus.ps1:27 char:27
    + $CleanupScope = New-Object <<<< Microsoft.UpdateSe
    tes)
    Exception calling "PerformCleanup" with "1" argument(
    Parameter name: cleanupScope"
    At C:cleanup-wsus.ps1:28 char:31
    + $CleanupManager.PerformCleanup( <<<< $CleanupScope)

    1. Anil,

      Make sure the assembly loads OK, by temporarily removing the [void] and watch for errors.
      Also, make sure there are no characters missing or added due to copying the script from my site. Retype it if you need.
      Hope that helps.
      Hugo

    1. Hi Bean,
      The cleanup method dos not return anything when successful. So there’s not much to log.
      Error handling is a field I’m not too familiar with. Try looking up some information about Powershell error handling on the web.
      Greetings,
      Hugo

  6. I do love the possibility of automating this.
    Although I have one problem. I get timeouts running the script. Is there a way to change the timeout value ?

  7. Hi Hugo,

    Your script looks great, unfortunately I get an error I’m not sure of…

    PS C:\Scripts> C:\Scripts\cleanup-wsus.ps1
    Exception calling “GetUpdateServer” with “3” argument(s): “The request failed with HTTP status 404: Not Found.”
    At C:\Scripts\cleanup-wsus.ps1:30 char:78
    + $Wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer <<<< ($updateServer,$useSecureConnection,$portNumber)
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

    You cannot call a method on a null-valued expression.
    At C:\Scripts\cleanup-wsus.ps1:33 char:42
    + $CleanupManager = $Wsus.GetCleanupManager <<<< ()
    + CategoryInfo : InvalidOperation: (GetCleanupManager:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    You cannot call a method on a null-valued expression.
    At C:\Scripts\cleanup-wsus.ps1:35 char:31
    + $CleanupManager.PerformCleanup <<<< ($CleanupScope)
    + CategoryInfo : InvalidOperation: (PerformCleanup:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    Any idea what I've done wrong??

  8. We have configured wsus server listening port to 80 (default), but still its not working.

    Exception calling “GetUpdateServer” with “3” argument(s): “The underlying connection was closed: An unexpected error oc
    curred on a receive.”
    At C:\WsusAgent 7.6\Cleanup-wsus.ps1:30 char:78
    + $Wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer <<<< ($updateServer,$useSecureConnectio
    n,$portNumber)
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

    You cannot call a method on a null-valued expression.
    At C:\WsusAgent 7.6\Cleanup-wsus.ps1:33 char:42
    + $CleanupManager = $Wsus.GetCleanupManager <<<< ()
    + CategoryInfo : InvalidOperation: (GetCleanupManager:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    You cannot call a method on a null-valued expression.
    At C:\WsusAgent 7.6\Cleanup-wsus.ps1:35 char:31
    + $CleanupManager.PerformCleanup <<<< ($CleanupScope)
    + CategoryInfo : InvalidOperation: (PerformCleanup:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

  9. Hello,
    Thanks for the nice script wich i’ve been looking and searching for. Altough I get an “error” on the last line $CleanupManager.PerformCleanup($CleanupScope)

    When i execute this line i get the following message :
    Exception calling “PerformCleanup” with “1” argument(s): “Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
    The statement has been terminated.”
    At line:1 char:31
    + $CleanupManager.PerformCleanup <<<< ($CleanupScope)
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

    I suggest that the cleanup is not executed then ? How can i avoid the timeout ?
    Any help would be appriciated.

    Regards,
    Jo

  10. Hi Hugo and internet,

    I have a modified script below.
    My issue was that I kept getting SQL query timeout from some WSUS servers that I just started to manage. I don’t think the cleanup tasks were ever been run since they were deployed years ago.
    The below modified script only does a single cleanup task, one at a time.
    This decreases the processing time of an individual sql query to avoid timeouts.

    ###cleanup-wsus-seperated-tasks.ps1 file###
    #Region VARIABLES

    # WSUS Connection Parameters:
    [String]$updateServer = “localhost”
    [Boolean]$useSecureConnection = $False
    [Int32]$portNumber = 80

    ##
    # I execute this file by:
    # %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe .\cleanup-wsus-seperated-tasks.ps1

    # Afterwords, I then run the Re-index the WSUS 3.0 Database (see more info below):
    # “C:\Program Files\Microsoft SQL Server\90\Tools\binn\SQLCMD.EXE” -S np:\\.\pipe\MSSQL$MICROSOFT##SSEE\sql\query -i .\WsusDBMaintenance.sql

    # I have both of those lines in a batch file, then create a scheduled task to run daily
    ##

    # Cleanup Parameters:
    # Decline updates that have not been approved for 30 days or more, are not currently needed by any clients, and are superseded by an aproved update.
    [Boolean]$supersededUpdates = $True
    # Decline updates that aren’t approved and have been expired my Microsoft.
    [Boolean]$expiredUpdates = $True
    # Delete updates that are expired and have not been approved for 30 days or more.
    [Boolean]$obsoleteUpdates = $True
    # Delete older update revisions that have not been approved for 30 days or more.
    [Boolean]$compressUpdates = $True
    # Delete computers that have not contacted the server in 30 days or more.
    [Boolean]$obsoleteComputers = $True
    # Delete update files that aren’t needed by updates or downstream servers.
    [Boolean]$unneededContentFiles = $True

    #EndRegion VARIABLES

    #Region SCRIPT

    # Load .NET assembly
    [void][reflection.assembly]::LoadWithPartialName(“Microsoft.UpdateServices.Administration”)

    # Connect to WSUS Server
    $Wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer($updateServer,$useSecureConnection,$portNumber)

    # This script originally had all the cleanup to be done as a single task.
    # I seperated each task to get around sql query timeout issues.
    # After seperating the tasks, I then had less timeout issues.
    # Additionally, I also gave it 4vcpus and 8GB of memory to my WSUS VM, then moved it to an ESXi host all by itself to have the best performance.
    # Since my WSUS is on SQL express, it is limited to 1vcpu and 1GB of memory.
    # If the timeouts had remained, my next step would have been to consider performing an SQL “upgrade” to a SQL standard or enterprise version, so the SQL queries would have run with multiple vcpu and more memory.

    # For reference, here’s the original lines
    # Perform Cleanup
    #$CleanupManager = $Wsus.GetCleanupManager()
    #$CleanupScope = New-Object Microsoft.UpdateServices.Administration.CleanupScope($supersededUpdates,$expiredUpdates,$obsoleteUpdates,$compressUpdates,$obsoleteComputers,$unneededContentFiles)
    #$CleanupManager.PerformCleanup($CleanupScope)

    ## Here’s the lines in which I seperated each task
    # Perform Cleanup of only supersededUpdates
    $CleanupManager = $Wsus.GetCleanupManager()
    $CleanupScope = New-Object Microsoft.UpdateServices.Administration.CleanupScope($supersededUpdates,$False,$False,$False,$False,$False)
    $CleanupManager.PerformCleanup($CleanupScope)

    # Perform Cleanup of only expiredUpdates
    $CleanupManager = $Wsus.GetCleanupManager()
    $CleanupScope = New-Object Microsoft.UpdateServices.Administration.CleanupScope($False,$expiredUpdates,$False,$False,$False,$False)
    $CleanupManager.PerformCleanup($CleanupScope)

    # Perform Cleanup of only obsoleteUpdates
    $CleanupManager = $Wsus.GetCleanupManager()
    $CleanupScope = New-Object Microsoft.UpdateServices.Administration.CleanupScope($False,$False,$obsoleteUpdates,$False,$False,$False)
    $CleanupManager.PerformCleanup($CleanupScope)

    # Perform Cleanup of only compressUpdates
    $CleanupManager = $Wsus.GetCleanupManager()
    $CleanupScope = New-Object Microsoft.UpdateServices.Administration.CleanupScope($False,$False,$False,$compressUpdates,$False,$False)
    $CleanupManager.PerformCleanup($CleanupScope)

    # Perform Cleanup of only obsoleteComputers
    $CleanupManager = $Wsus.GetCleanupManager()
    $CleanupScope = New-Object Microsoft.UpdateServices.Administration.CleanupScope($False,$False,$False,$False,$obsoleteComputers,$False)
    $CleanupManager.PerformCleanup($CleanupScope)

    # Perform Cleanup of only unneededContentFiles
    $CleanupManager = $Wsus.GetCleanupManager()
    $CleanupScope = New-Object Microsoft.UpdateServices.Administration.CleanupScope($False,$False,$False,$False,$False,$unneededContentFiles)
    $CleanupManager.PerformCleanup($CleanupScope)

    # My next step is to run WsusDBMaintenance.sql
    # Get the script from below. Don’t click the “copy code” button”, but select all, copy, paste.
    # https://technet.microsoft.com/en-us/library/dd939795%28v=ws.10%29

    # You’ll also need the “SQLCMD.EXE” tool, which you can get from here:
    # SQL 2005 feature pack:
    # http://www.microsoft.com/en-us/download/details.aspx?id=24793
    # Install both of these (I’m assuming your on 64bit windows):
    # sqlncli_x64.msi
    # SQLServer2005_SQLCMD_x64.msi
    #
    # Then you can Re-index the WSUS 3.0 Database:
    # “C:\Program Files\Microsoft SQL Server\90\Tools\binn\SQLCMD.EXE” -S np:\\.\pipe\MSSQL$MICROSOFT##SSEE\sql\query -i WsusDBMaintenance.sql

    # Note, the sql database may have a different name depending on the WSUS version
    #:: Before windows 2012, WSUS 3.0 Database
    # MSSQL$MICROSOFT##SSEE

    #:: Windows 2012 with WSUSv6 (I haven’t tested this version):
    # MSSQL$MICROSOFT##WID

    #EndRegion SCRIPT

  11. Hello,
    Thanks for the nice script. I have following error:

    Ausnahme beim Aufrufen von “GetUpdateServer” mit 3 Argument(en): “Fehler bei der Anforderung mit HTTP-Status 401: Unauthorized.”
    Bei C:\Scripts\wsus\WSUS-Serverbereinigung.ps1:30 Zeichen:78
    + $Wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer <<<< ($updateServer,$useSecureConnection,$portNum
    ber)
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

    Sie k”nnen keine Methode fr einen Ausdruck mit dem Wert NULL aufrufen.
    Bei C:\Scripts\wsus\WSUS-Serverbereinigung.ps1:33 Zeichen:42
    + $CleanupManager = $Wsus.GetCleanupManager <<<< ()
    + CategoryInfo : InvalidOperation: (GetCleanupManager:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    Sie k”nnen keine Methode fr einen Ausdruck mit dem Wert NULL aufrufen.
    Bei C:\Scripts\wsus\WSUS-Serverbereinigung.ps1:35 Zeichen:31
    + $CleanupManager.PerformCleanup <<<< ($CleanupScope)
    + CategoryInfo : InvalidOperation: (PerformCleanup:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    1. The Error is fixed, i habe a new one:

      Ausnahme beim Aufrufen von “GetUpdateServer” mit 3 Argument(en): “Der vom Client gefundene Anforderungsinhaltstyp ist ”, erwartet wurde ‘text/xml’.
      Fehler: Leere Antwort auf Anforderung.”
      Bei C:\Scripts\wsus\WSUS-Serverbereinigung.ps1:30 Zeichen:78
      + $Wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer <<<< ($updateServer,$useSecureConnection,$portNumber)
      + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
      + FullyQualifiedErrorId : DotNetMethodException

      Sie können keine Methode für einen Ausdruck mit dem Wert NULL aufrufen.
      Bei C:\Scripts\wsus\WSUS-Serverbereinigung.ps1:33 Zeichen:42
      + $CleanupManager = $Wsus.GetCleanupManager <<<< ()
      + CategoryInfo : InvalidOperation: (GetCleanupManager:String) [], RuntimeException
      + FullyQualifiedErrorId : InvokeMethodOnNull

      Sie können keine Methode für einen Ausdruck mit dem Wert NULL aufrufen.
      Bei C:\Scripts\wsus\WSUS-Serverbereinigung.ps1:35 Zeichen:31
      + $CleanupManager.PerformCleanup <<<< ($CleanupScope)
      + CategoryInfo : InvalidOperation: (PerformCleanup:String) [], RuntimeException
      + FullyQualifiedErrorId : InvokeMethodOnNull

Leave a Reply