Friday, January 8, 2016

Automating WSUS cleanup and maintenance with a scheduled Powershell cleanup script

Finished product first: WSUS-Cleanup.ps1

$logfile="C:\Scripts\Logs\WSUS-Cleanup.log"

$ErrorActionPreference="SilentlyContinue"
Stop-Transcript | out-null
$ErrorActionPreference = "Continue"
Start-Transcript -path $logfile
$Error.Clear()

Write-Output "$((get-date).ToLongTimeString()) $server - WSUS Cleanup starting..."
Invoke-WsusServerCleanup -CleanupObsoleteUpdates -CleanupUnneededContentFiles -CompressUpdates -DeclineExpiredUpdates -DeclineSupersededUpdates
Write-Output "$((get-date).ToLongTimeString()) $server - WSUS Cleanup complete."

Stop-Transcript
$emailbody = Get-Content $logfile | Out-String
$emailsubject = "WSUS cleanup report on $Env:ComputerName"
if ($error -ne $null){ $emailsubject = "WSUS cleanup report ERROR on $Env:ComputerName"}

Send-MailMessage `
    -From "$env:COMPUTERNAME-alert@contoso.com" `
    -To "Brian Admin <briancanfixit@gmail.com>", "Other Admin <none@contoso.com>" `
    -Subject "$emailsubject" `
    -Body "$emailbody" `
    -SmtpServer "mail.contoso.com"





Where to save this script?

I like to have my script saved to C:\Scripts\ and limit access to said folder to only authorized staff/processes. 

Windows Task Scheduler runs it every Wednesday at 2am

  1. Run Windows Task Scheduler
  2. Click "Create Basic Task..."
  3. Name the task "WSUS Cleanup" or similar, Click "Next >"
  4. Weekly, Click "Next >"
  5. Check the Wednesday checkbox, enter the time of 2:00am, Click "Next >"
  6. Use the default of "Start a Program", Click "Next >"
  7. For "Program/Script" enter:
    powershell
  8. For "Add Arguments" enter:
    -command "C:\Scripts\WSUS-Cleanup.ps1" -ExecutionPolicy Bypass
  9. Click "Next >"
  10. Click "Finish"
  11. Click on the newly created task and click "Properties"
  12. Under General, Click "Change User or Group...", enter in "SYSTEM" and click "OK"*
  13. Under Settings, "Stop the task if it runs longer than:" drop-down, select "2 Hours", Click "OK"
*=Always remember to limit file access to these scripts to prevent an authorized user from adding malicious code.

So what does it look like?

Email just after Patch Tuesday:

**********************
Windows PowerShell transcript start
Start time: 20151215074723
Username: CONTOSO\SYSTEM
RunAs User: CONTOSO\SYSTEM
Machine: WSUS (Microsoft Windows NT 6.3.9600.0)
Host Application: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.EXE -command C:\Scripts\WSUS-Cleanup.ps1 -ExecutionPolicy Bypass
Process ID: 73016
**********************
Transcript started, output file is C:\Scripts\WSUS-Cleanup.log
7:47:23 AM  - WSUS Cleanup starting...
Obsolete Updates Deleted:0
Expired Updates Declined: 0
Obsolete Updates Deleted:96
Updates Compressed:1559
Diskspace Freed:2796151652
7:51:06 AM  - WSUS Cleanup complete.
**********************
Windows PowerShell transcript end
End time: 20151215075106
**********************

Normal Email: 

**********************
Windows PowerShell transcript start
Start time: 20151014020013
Username: CONTOSO\SYSTEM
RunAs User: CONTOSO\SYSTEM
Machine: WSUS (Microsoft Windows NT 6.3.9600.0)
Host Application: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.EXE -command C:\Scripts\WSUS-Cleanup.ps1 -ExecutionPolicy Bypass
Process ID: 3124
**********************
Transcript started, output file is C:\Scripts\WSUS-Cleanup.log
2:00:14 AM  - WSUS Cleanup starting...
Obsolete Updates Deleted:0
Expired Updates Declined: 0
Obsolete Updates Deleted:0
Updates Compressed:4
Diskspace Freed:17829289
2:00:24 AM  - WSUS Cleanup complete.
**********************
Windows PowerShell transcript end
End time: 20151014020024
**********************

Script Breakdown

$logfile="C:\Scripts\Logs\WSUS-Cleanup.log"
Set the location of the log file that we will email later.

$ErrorActionPreference="SilentlyContinue"
Stop-Transcript | out-null
$ErrorActionPreference = "Continue"
Start-Transcript -path $logfile
$Error.Clear()

Here we stop the transcript and tell powershell to continue if any errors happen. We then Tell powershell to continue normally if an error happens and start the transcript. We also clear any errors that may exist.

Write-Output "$((get-date).ToLongTimeString()) $server - WSUS Cleanup starting..."

Log the Date & Time. I like to include everything as part of a larger string in double-quotes, this is why you must use the $( to escape the variable when referencing a property. If you use single quotes then it means something totally different to powershell. and you can't escape
 

Invoke-WsusServerCleanup -CleanupObsoleteUpdates -CleanupUnneededContentFiles -CompressUpdates -DeclineExpiredUpdates -DeclineSupersededUpdates
Here is the one big command that does what we came here for.
It's fairly self-explanatory and does the following 5 tasks:
-CleanupObsoleteUpdates
-CleanupUnneededContentFiles
-CompressUpdates
-DeclineExpiredUpdates
-DeclineSupersededUpdates

Write-Output "$((get-date).ToLongTimeString()) $server - WSUS Cleanup complete."
Log the Date & Time




Stop-Transcript
Stop logging everything
 

$emailbody = Get-Content $logfile | Out-String
We'll get the file contents of our log file saves as a string (it would normally use an array of strings) to use at the Email body.

$emailsubject = "WSUS cleanup report on $Env:ComputerName"

The Subject of our Email will include the computer name.
 
if ($error -ne $null){ $emailsubject = "WSUS cleanup report ERROR on $Env:ComputerName"}

If there was an error during the WSUS cleanup process, then change the subject of the email message.

Send-MailMessage `
    -From "$env:COMPUTERNAME-alert@contoso.com" `
    -To "Brian Admin <briancanfixit@gmail.com>", "Other Admin <none@contoso.com>" `
    -Subject "$emailsubject" `
    -Body "$emailbody" `
    -SmtpServer "mail.contoso.com"

Send the email message, note the back-ticks ` and the end of the lines... that basically means the powershell command spans the next line break.

And that's it. Hopefully it has helped you out. At the very least it will help me remember how I've done this before.