Advertisement
samersultan1

WSUS Clean Up PowerShell Script

Jan 15th, 2019
1,053
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. param([switch]$Elevated)
  2.  
  3. function Test-Admin {
  4.   $currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
  5.   $currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
  6. }
  7.  
  8. if ((Test-Admin) -eq $false)  {
  9.     if ($elevated)
  10.     {
  11.         # tried to elevate, did not work, aborting
  12.     }
  13.     else {
  14.         Start-Process powershell.exe -Verb RunAs -ArgumentList ('-noprofile -noexit -file "{0}" -elevated' -f ($myinvocation.MyCommand.Definition))
  15. }
  16.  
  17. exit
  18. }
  19.  
  20. 'running with full privileges'
  21.  
  22.  
  23. <#
  24.  
  25.     WSUS-CLEANUP-UPDATES
  26.    
  27.     Runs WSUS cleanup task using stored procedures in WSUS database
  28.     thus avoiding timeout errors that may occur when running WSUS Cleanup Wizard.
  29.  
  30.     The script is intended to run as a scheduled task on WSUS server
  31.     but can also be used remotely. $SqlServer and $SqlDB variables
  32.     must be defined before running the script on a server without WSUS.
  33.  
  34.     Version 4
  35.  
  36.     Version history:
  37.  
  38.     4    Added database connection state check before deleting an
  39.          unused update: the script will now attempt to reestablish
  40.          connection if broken.
  41.  
  42.  
  43. #>
  44.  
  45.  
  46. ##########################
  47. # Configurable parameters
  48.  
  49. $SqlServer = ""    # SQL server host name; leave empty to use information from local registry
  50. $SqlDB = "SUSDB"   # WSUS database name    
  51. $SkipFileCleanup = $SqlServer -ne ""
  52.  
  53. $log_source = "WSUS cleanup Task"  # Event log source name
  54. $log_debugMode = $true  # set to false to suppress console output
  55.  
  56.  
  57. ##########################
  58.  
  59.  
  60. $ErrorActionPreference = "Stop"
  61.  
  62. # basic logging facility
  63.  
  64. function log_init{
  65.     if ( -not [System.Diagnostics.EventLog]::SourceExists($log_source) ){
  66.         [System.Diagnostics.EventLog]::CreateEventSource($log_source, "Application")
  67.     }
  68. }
  69.  
  70. function log( [string] $msg, [int32] $eventID, [System.Diagnostics.EventLogEntryType] $level ){
  71.     Write-EventLog -LogName Application -Source $log_source -EntryType $level -EventId $eventID -Message $msg
  72.     if ( $log_debugMode ){
  73.         switch ($level){
  74.             Warning {Write-Host $msg -ForegroundColor Yellow }
  75.             Error { Write-Host $msg -ForegroundColor Red }
  76.             default { Write-Host $msg -ForegroundColor Gray }
  77.         }
  78.     }
  79. }
  80.  
  81. function dbg( [string] $msg ){
  82.     if ( $log_debugMode ){
  83.         log "DBG: $msg"  300 "Information"
  84.     }
  85. }
  86.  
  87. log_init
  88.  
  89.  
  90. #########################
  91.  
  92.  
  93. function DeclineExpiredUpdates( $dbconn ){
  94.  
  95.     log "Declining expired updates" 1 "Information"
  96.  
  97.     $Command = New-Object System.Data.SQLClient.SQLCommand
  98.     $Command.Connection = $dbconn
  99.     $Command.CommandTimeout = 3600
  100.     $Command.CommandText = "EXEC spDeclineExpiredUpdates"
  101.     try{
  102.         $Command.ExecuteNonQuery() | Out-Null
  103.     }
  104.     catch{
  105.         $script:errorCount++
  106.         log "Exception declining expired updates:`n$_" 99 "Error"
  107.     }
  108. }
  109.  
  110. #########################
  111.  
  112. function DeclineSupersededUpdates( $dbconn ){
  113.  
  114.     log "Declining superseded updates" 1 "Information"
  115.    
  116.     $Command = New-Object System.Data.SQLClient.SQLCommand
  117.     $Command.Connection = $dbconn
  118.     $Command.CommandTimeout = 1800
  119.     $Command.CommandText = "EXEC spDeclineSupersededUpdates"
  120.     try{
  121.         $Command.ExecuteNonQuery() | Out-Null
  122.     }
  123.     catch{
  124.         $script:errorCount++
  125.         log "Exception declining superseded updates:`n$_" 99 "Error"
  126.     }
  127. }
  128.  
  129.  
  130. #######################
  131.  
  132. function DeleteObsoleteUpdates( $dbconn ){
  133.  
  134.         Log "Reading obsolete update list." 1 "Information"
  135.         $Command = New-Object System.Data.SQLClient.SQLCommand
  136.         $Command.Connection = $dbconn
  137.         $Command.CommandTimeout = 600
  138.         $Command.CommandText = "EXEC spGetObsoleteUpdatesToCleanup"
  139.         $reader = $Command.ExecuteReader()
  140.         $table = New-Object System.Data.DataTable
  141.         $table.Load($reader)
  142.  
  143.         $updatesTotal = $table.Rows.Count
  144.         log "Found $updatesTotal updates that can be deleted." 1 "Information"
  145.  
  146.         $updatesProcessed=0
  147.         $Command.CommandTimeout = 300
  148.         foreach( $row in $table.Rows ){
  149.             try{
  150.                 if ( $dbconn.State -ne [System.Data.ConnectionState]::Open ){
  151.                     log "Re-opening database connection" 2 "Warning"
  152.                     $dbconn.Open()
  153.                 }
  154.                 $updatesProcessed++
  155.                 log "Deleting update $($row.localUpdateID) ($updatesProcessed of $updatesTotal)" 1 "Information"
  156.                 $Command.CommandText = "exec spDeleteUpdate @LocalUpdateID=$($row.localUpdateID)"
  157.                 $Command.ExecuteNonQuery() | Out-Null
  158.             }
  159.             catch{
  160.                 $errorCount++
  161.                 log "Error deleting update $($row.localUpdateID):`n$_" 8 "Warning"
  162.             }
  163.         }
  164.  
  165. }
  166.  
  167. ###################
  168.  
  169.  
  170. function DbConnectionString{
  171.  
  172.     $WsusSetupKey = "HKLM:\SOFTWARE\Microsoft\Update Services\Server\Setup"
  173.  
  174.     if ( $script:SqlServer -eq "" ){
  175.         $server = Get-ItemProperty -path $WsusSetupKey -Name "SqlServerName" -ErrorAction SilentlyContinue
  176.         $db = Get-ItemProperty -path $WsusSetupKey -Name "SqlDatabaseName" -ErrorAction SilentlyContinue
  177.         if ( ! $server  ){
  178.             throw "Cannot determine SQL server name"
  179.         }
  180.         $script:SqlServer = $server.SqlServerName
  181.         $script:SqlDB = $db.SqlDatabaseName
  182.     }
  183.  
  184.     if ( $script:SqlServer -match "microsoft##" ){
  185.         return "data source=\\.\pipe\$script:SqlServer\tsql\query;Integrated Security=True;database='$script:SqlDB';Network Library=dbnmpntw"
  186.     }
  187.     else{
  188.         return "server='$script:SqlServer';database='$script:SqlDB';trusted_connection=true;"
  189.     }
  190.  
  191. }
  192.  
  193.  
  194. ##############
  195.  
  196. function DeleteUnusedContent{
  197.  
  198.     log "Deleting unneeded content files" 1 "Information"
  199.    
  200.     try{
  201.         Import-Module UpdateServices
  202.         $status = Invoke-WsusServerCleanup -CleanupUnneededContentFiles
  203.         log "Done deleting unneeded content files: $status" 1 "Information"
  204.     }
  205.     catch{
  206.         $script:errorCount++
  207.         log "Exception deleting unneeded content files:`n$_" 99 "Error"
  208.     }
  209.  
  210. }
  211.  
  212.  
  213. ###################
  214.  
  215. function DeleteInactiveComputers( $DbConn ){
  216.  
  217.     log "Removing obsolete computers" 1 "Information"
  218.    
  219.     $Command = New-Object System.Data.SQLClient.SQLCommand
  220.     $Command.Connection = $dbconn
  221.     $Command.CommandTimeout = 1800
  222.     $Command.CommandText = "EXEC spCleanupObsoleteComputers"
  223.     try{
  224.         $Command.ExecuteNonQuery() | Out-Null
  225.     }
  226.     catch{
  227.         $script:errorCount++
  228.         log "Exception removing obsolete computers:`n$_" 99 "Error"
  229.     }
  230.  
  231. }
  232.  
  233. function RestartWsusService{
  234.     log "Stopping IIS.." 1 "Information"
  235.     try{
  236.         Stop-Service W3SVC -Force
  237.         try{
  238.             log "Restarting WSUS service.." 1 "Information"
  239.             Restart-Service WsusService -Force
  240.         }
  241.         finally{
  242.             log "Starting IIS..." 1 "Information"
  243.             Start-Service W3SVC
  244.         }
  245.     }
  246.     catch{
  247.         $script:errorCount++
  248.         log "Error restarting WSUS services:`n$_" 99 "Error"        
  249.     }
  250.     Start-Sleep -Seconds 30
  251. }
  252.  
  253. <#------------------------------------------------
  254.                      MAIN                        
  255. -------------------------------------------------#>
  256.  
  257.  
  258. $timeExecStart = Get-Date
  259. $errorCount = 0
  260.  
  261. try{
  262.    
  263.     $Conn = New-Object System.Data.SQLClient.SQLConnection
  264.     $Conn.ConnectionString = DbConnectionString
  265.     log "Connecting to database $SqlDB on $SqlServer" 1 "Information"
  266.     $Conn.Open()
  267.     try{
  268.         DeclineExpiredUpdates $Conn
  269.         DeclineSupersededUpdates $Conn
  270.         DeleteObsoleteUpdates $Conn
  271.         DeleteInactiveComputers $Conn  
  272.         RestartWsusService  
  273.         if ( ! $SkipFileCleanup ) {  
  274.             DeleteUnusedContent
  275.         }
  276.     }
  277.     finally{
  278.         $Conn.Close()
  279.     }
  280.  
  281. }
  282. catch{
  283.     $errorCount++
  284.     log "Unhandled exception:`n$_" 100 "Error"
  285. }
  286.  
  287. $time_exec = ( Get-Date ) - $timeExecStart
  288. log "Completed script execution with $errorCount error(s)`nExecution time $([math]::Round($time_exec.TotalHours)) hours and $([math]::Round($time_exec.totalMinutes)) minutes." 1 "Information"
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement