Automating WSUS with PowerShell
The scripts explained in this post allow you to automate several Windows Server Update Services (WSUS) tasks such as synchronization, approvals, cleanups, and scheduled update installations.
Syncing WSUS with PowerShell and Task Scheduler ^
In this article, I assume you are familiar with WSUS administration. Right after installing WSUS, you have to configure periodic synchronization. Unfortunately, as you can see in the screenshot below, the synchronization options are somewhat limited.
Since I don’t need to sync every day, I select Synchronize manually and use the script below along with Task Scheduler to synchronize WSUS at the times I prefer.
1
2
3
4
|
$wsusserver = “wsus”
[reflection.assembly]::LoadWithPartialName(“Microsoft.UpdateServices.Administration”) | out–null
$wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer($wsusserver, $False,8530);
$wsus.GetSubscription().StartSynchronization();
|
I load the .NET Update Services object into the $wsusserver variable and use the StartSynchronization() method to start manual synchronization. The screenshot below shows the Task Scheduler task I’m using to launch the PowerShell script.
You will see the synchronization results in the WSUS console as if you synced manually:
Automating WSUS update approval ^
The next task I’m going to automate is the approval of updates. WSUS offers automatic approval. However, it is quite inflexible, so I wrote the PowerShell script below:
I added comments, so I’ll just explain briefly how the script works. First I load the Windows Update Assembly, so I can use the WSUS .NET object. Then I’m preparing the variables that I need to work with the WSUS object:
- $wsus: is the WSUS object.
- $UpdateScope: Defines the time interval for the $wsus.GetUpdates() method.
- $groups: Defines all WSUS groups I’d like to approve updates for.
- $Classification: Defines updates classifications for the $wsus.GetUpdates() method. I’m filtering out service packs, drivers, and upgrades.
- $Categories: Defines updates categories or products for the $wsus.GetUpdates() method. I’m filtering out SQL and Skype updates. SQL gets updated manually, and I don’t have Skype installations in my environment.
Then I’m setting up the Update Scope interval to get only updates created within the last month. I know I’m approving my updates every month, so I only need to get recently released updates.
After that, I’m assigning the $Classification and $Categories variables to the corresponding objects. And with the help of the $wsus.GetUpdates($UpdateScope) method, I am saving all updates that match my scope to the $updates variable. Then I’m adding some filtering to remove updates such as LanguagePack, FeatureOnDemand, and Itanium from the results because I don’t have these kinds of updates in my environment.
Now I have all updates I want to approve. Next, I’m looping through the WSUS groups to which I want to assign the updates. Then I loop through the updates, approving every update for every group. In this particular case, there is only one group. However, I use a loop here, just to be able to add more groups later.
After approving all updates, I only need to update the log file and send this file by email to myself. This way, I am sure I’ve approved the updates, and I receive brief information about them.
Like before, I’m using Task Scheduler to run the script:
Declining superseded updates ^
As you know, Microsoft frequently replaces single updates with packages of multiple updates. They call the replaced update a “superseded update,” which is no longer needed. Thus, it makes sense to decline those updates. For this purpose, I modified the PowerShell script below, which I found here.
My changes are in the lines 57–59, 99–100, and 242. I added the transcript file, so when the script ran via the Task Scheduler, I could see the number of declined updates. And after I ran the script the first time, I changed the update scope. So it’ll check and decline only updates within the last six months.
The screenshot below shows a sample log file:
Deleting declined updates from the WSUS database ^
After you decline the updates, they are still residing inside the WSUS database and taking up disk space. To remove them completely, you have to run the WSUS cleanup wizard. This is another task you can automate:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
$file = “c:\temp\WSUS_CleanUp_Wiz_{0:MMddyyyy_HHmm}.log” –f (Get–Date)
Start–Transcript –Path $file
$wsusserver = “wsus”
[reflection.assembly]::LoadWithPartialName(“Microsoft.UpdateServices.Administration”)` | out–null
$wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer($wsusserver, $False,8530);
$cleanupScope = new–object Microsoft.UpdateServices.Administration.CleanupScope;
$cleanupScope.DeclineSupersededUpdates = $true
$cleanupScope.DeclineExpiredUpdates = $true
$cleanupScope.CleanupObsoleteUpdates = $true
$cleanupScope.CompressUpdates = $false
$cleanupScope.CleanupObsoleteComputers = $true
$cleanupScope.CleanupUnneededContentFiles = $true
$cleanupManager = $wsus.GetCleanupManager();
$cleanupManager.PerformCleanup($cleanupScope);
Stop–Transcript
|
All I’m doing in the script above is defining a cleanup scope using the CleanUpScope object and then running CleanUpManager using the corresponding object against that scope. I’m not compressing updates because this operation takes a long time and doesn’t save much space.
The script also runs as scheduled task and produces the log file you can see below:
Because all of these procedures are making many changes in the WSUS database, it is good idea to re-index the database occasionally. To do that, I’m using this SQL query from the Microsoft Script Center. You can use the sqlcmd utility you find there to run the SQL query. Just create a scheduled task and run it once a month.
Arranging the maintenance scripts ^
Here is how I scheduled the maintenance scripts:
- Synchronize WSUS every Tuesday.
- Decline superseded updates after every WSUS synchronization.
- Run the WSUS cleanup wizard script after declining superseded updates finishes.
- Re-index the WSUS database after WSUS cleanup.
- Approve updates every Wednesday. This way I know I’m approving updates after removal of all superseded, outdated, and expired updates.
Scheduling updates ^
At this point I’m done with maintenance. However, I still need to install the updates. Unfortunately, WSUS also only offers poor choices when it comes to scheduling update installations. Basically, I can only pick the day of the week and the time. Of course, this is not always what you want. Because I have several environments, I created a Group Policy Object (GPO) for each of them and assigned them to the appropriate Active Directory organizational units (OUs).
As you can see, I configured this GPO to install updates every Friday at 7 p.m. The thing is, I just need to do this on a particular Friday every month. Thus, I wrote a tiny script for enabling this GPO and a second one for disabling it. Then I configured a scheduled task to run the first script a couple days before the update day and the second one after installing the updates.
1
2
3
4
5
6
7
|
Enabling GPO
$GPO = Get–GPO –Name “WSUS DEV OU – Automatic Updates”
$GPO.GpoStatus = “AllSettingsEnabled”
Disabling GPO
$GPO = Get–GPO –Name “WSUS DEV OU – Automatic Updates”
$GPO.GpoStatus = “AllSettingsDisabled”
|
Conclusion ^
And after I’ve completed my configuration, I’m just checking my WSUS server every once in a while to make sure everything works as intended.