WSUS (bundled or installable) child updates

This morning there was question on Patchmanagement.org about how to find if the following .Net Rollup update (4019112) has been installed.

The person approved 4019112 but noticed that the WMI Win32_QuickFixEngineering class didn’t list it.
Instead it contained the following updates KB4014504 and KB4014511 that are actually bundled inside the rollup 4019112

The person was also asking if there’s another way to find what child updates an update installs instead of what’s listed on its support page.

Yes, there is one. If you use the WSUS API GetInstallableItems method. It will list what child updates an update will install (more precisely, it will get the directly bundled, non-explicitly-deployable child updates of this update. Creates an installable item on behalf of this update if the update itself has files.)

(Get-WsusServer).SearchUpdates('4019112') |
Out-GridView -PassThru -Title 'Select an update'| 
ForEach-Object { 
    $_.GetInstallableItems() 
} | ft -AutoSize

As you can see the KB4019112 (logically) bundles an update for every supported version of .Net.

Unfortunately, we couldn’t use the GetRelatedUpdates method and its enum UpdatesBundledByThisUpdate that should also list child updates that are bundled in this update.

# what's the numeric value of the UpdatesBundledByThisUpdate enum
[Microsoft.UpdateServices.Administration.UpdateRelationship]::UpdatesBundledByThisUpdate.value__

(Get-WsusServer).SearchUpdates('4019112') | 
ForEach-Object {
 $_ | 
 Add-Member -MemberType ScriptProperty -Name UpdatesBundledByThisUpdate -Value {
  ($this.GetRelatedUpdates(2)).Title
 } -Force -PassThru
} | Select Title,UpdatesBundledByThisUpdate | 
Out-GridView -PassThru | Select -ExpandProperty UpdatesBundledByThisUpdate


As you can see, it doesn’t return anything. Why?

(Get-WsusServer).SearchUpdates('4014504')
(Get-WsusServer).SearchUpdates('4014511')

Well, because the above two searches also don’t return anything.
It means that these updates don’t exist as individual updates in WSUS.

To find out if the .Net rollup 4019112 has been installed on a target computer you can either:

# use the Get-HotFix cmdlet
Get-HotFix -Id 'kb4014504','kb4014508','kb4014511','kb4014514'
# or the  Get-WmiObject cmdlet
Get-WmiObject -Query 'Select * FROM Win32_QuickFixEngineering WHERE HotFixID = "KB4014504" OR HotfixID = "KB4014508" OR HotfixID = "KB4014511" OR HotFixID = "KB4014514"' 

Quick tip: disable WSUS approval rules

I stumbled upon the following article about WSUS and PowerShell a few weeks ago.
After seeing the second point, I realized, it’s nice to verify how “auto-approval” rules are configured but it would be better to know how to enable or disable these rules.

Here’s how to disable rules:


(Get-WsusServer).GetInstallApprovalRules() | 
Out-GridView -Passthru| 
ForEach-Object {
    $r = $_
    try {
        $_.Enabled = $false
        $_.Save()
        Write-Verbose "Successfully disabled $($_.Name)" -Verbose

    } catch {
        Write-Warning "Failed on rule $($r.Name) because $($_.Exception.Message)"
    }
}

With the above code snippet, you first get the out-grid view where you can select rules and then click ok:

I selected the only rule I had and got the following result:

If you want to seem more articles about WSUS from my blog, you can use WSUS tag to list them all πŸ™‚

WSUS synchronization report

The other day I wondered how to get the equivalent of this WSUS synchronization summary and its details.
More precisely I wanted to get the summary of what you see in the mmc snap-in under the synchronizations node:
wsus-sync-01
and the details that you get when you right-click a synchronization and hit ‘Synchronization report’ but I got instead the following message telling me that the report viewer wasn’t installed
wsus-sync-02

After a few minutes, I found the following MSDN page about Reporting Newly Synchronized Updates
I first looked at the GetUpdates method that has many ways to call it.
wsus-sync-03
The msdn article proposes to use the 2nd one where you specify all the 5 arguments:

  • Microsoft.UpdateServices.Administration.ApprovedStates approvedStates,
  • datetime fromArrivalDate,
  • datetime toArrivalDate,
  • Microsoft.UpdateServices.Administration.UpdateCategoryCollection updateCategories,
  • Microsoft.UpdateServices.Administration.UpdateClassificationCollection updateClassifications

where both the updatecategories and updateclassifications are set to $null
I gave it a try but I couldn’t get reliable results 😦 even when the two collections (UpdateCategoryCollection and UpdateClassificationCollection) were properly defined and not set to null.
Forget that method, it’s too unpredictable.

I switched to the 3rd method where you just use a single updateScope object as argument instead of the above 5 arguments and finally got the expected reliability πŸ˜€

# Get the last sync info (start and end times)
$lastSync = (Get-WsusServer).GetSubscription().GetLastSynchronizationInfo()

# Create an updatescope object
$UpdateScope = New-Object -TypeName Microsoft.UpdateServices.Administration.UpdateScope

# Set the start time
$UpdateScope.FromArrivalDate = $lastSync.StartTime
# Set the end time
$UpdateScope.ToArrivalDate = $lastSync.EndTime

# Invoke the getupdates method using the update scope object
$SyncUpdates = (Get-WsusServer).GetUpdates($UpdateScope)

Now to get the summary, I just do

$SyncUpdates | 
Group-Object -Property publicationstate -NoElement

wsus-sync-04
To view the details of what was synchronized I do:

$SyncUpdates | Out-GridView
# or 
$SyncUpdates | 
Select Title,SecurityBulletins,UpdateClassificationTitle,PublicationState | 
Sort PublicationState | 
Format-Table -AutoSize

wsus-sync-05

Easy-peasy and as always PowerShell rocks 😎

Testing WSUS server operational status

During the summer, someone asked the following questionwsus-monitoring-question on the WSUS patchmanagement.org mailing list.

I replied and immediately thought that Pester would do the job and quickly showed how he could test if he can connect to the console.

I’ve actually more than a WSUS server to manage. So, I started separating the environmental configuration data from the pester tests code almost the same way Mike F. Robbins did in his recent post where he goes far beyond to what I did.

I think it’s a great idea and here’s what I did in my case to monitor my WSUS server operational status.

To get started, I copied the Pester module on my WSUS server, imported the module and did in the ISE:

# helper to create the required files and folder if not present
New-Fixture -Path  ~/Documents/Pester -Name Test-WSUS
# Put the config data into that file:
psEdit ~/Documents/Pester/Test-WSUS.ps1
# Put the pester code into that file:
psEdit ~/Documents/Pester/Test-WSUS.Tests.ps1

After the first 3 commands, here’s what the ISE console looked liked
pester-new-fixture-wsus-tests

The first file Test-WSUS.ps1 looks like this by default.
It will be used to store my configuration data.
pester-wsus-tests-01

The second file Test-WSUS.Tests.ps1 is where I’ll write the pester tests code
pester-wsus-tests-02

After editing the 1rst file Test-WSUS.ps1 like this:
pester-test-wsus-file (fake data in this case)

…and the 2nd file Test-WSUS.Tests.ps1 like this:

…I’m actually ready to assess the operational readiness of my WSUS configuration by using the following cmdlet:

Invoke-Pester -Script ~/Documents/Pester/Test-WSUS.Tests.ps1

wsus-server-config-invoke-pester

I still feel like a Pester newbie but no doubt that Pester rocks 😎

Import the convenience update into WSUS

My WSUS server runs on a Windows Core edition where I don’t have Internet Explorer installed.

Whatif I want to import the Convenience rollup update for Windows 7 SP1 and Windows Server 2008 R2 SP1 from the catalog?

Here’s the way to go:

On a client computer with a GUI, launch internet explorer with high privileges to be able to install the ActiveX.

$HT = @{
 FilePath = 'C:\Program Files (x86)\Internet Explorer\iexplore.exe' ;
 ArgumentList = 'http://catalog.update.microsoft.com/v7/site/Search.aspx?q=KB3125574' ;
 Verb = 'Runas'
}
Start-Process @HT

MS-catalog-ActiveX-install

Install the ActiveX, add the update to the basket, view your basket, download, browse…
Move the file onto the WSUS server…and check if the file is digitally signed

Get-AuthenticodeSignature -FilePath ~\downloads\Catalog\*\*.msu

The WSUS API has an ImportUpdateFromCatalogSite, documented here on msdn.
WSUS-import-method

I’m missing the UpdateID. Where do I find this Id?

It appears that if you click on the link highlighted below…

MS-catalog-KB-link

…you get on a page where you’ve more details about the update.

Convenience-update-Id

The updateId actually appears in the address bar. πŸ˜€

Now, to import the update into WSUS, I do:

$MSUfile = 'C:\Users\administrator\Downloads\Catalog\Update for Windows 7 for x64-based Systems (KB3125574)\AMD64-all-windows6.1-kb3125574-v4-x64_2dafb1d203c8964239af3048b5dd4b1264cd93b9.msu'
(Get-WsusServer).ImportUpdateFromCatalogSite(
    '49924c88-1b31-4b0f-ad3d-48df9877f385',
    $MSUfile
)
# Find the imported file
(Get-WsusServer).SearchUpdates('3125574') | fl *

The convenience update has been successfully imported and can now be approved.

Easily identify your WSUS updates

I’ve been using some piece of code on WSUS to enrich non-security updates to help me identify what problem they address.

Let’s first have a look at the result as a picture is worth a thousand words πŸ˜€
Enrich-WSUS-updates

Now, let’s see the code

I wanted to share this a long time ago but the initial piece of code broke while Microsoft was upgrading their support.microsoft.com website.
It may broke again of course…

Switch WSUS to https

The following was presented at Black Hat 2015 USA during the summer.

The whitepaper is available at http://ctx.is/WSUSpect
The slides are available at http://www.contextis.com/documents/162/WSUSpect_Presentation.pdf
The code is available at https://github.com/ctxis/wsuspect-proxy

The only mitigation identified was an HTTPS-enabled WSUS. In this post, I’d like to show how to switch your HTTP based WSUS to HTTPS.

I’ll assume that:

  • your server is running Windows 2012 R2, has already an http based WSUS
  • your server may not be connected directly to the Internet
  • you don’t want to buy a certificate from a public certiticate authority
  • you don’t have PKI in your infrastructure

This official documentation is available on Technet on this page

But let’s do this with PowerShell πŸ™‚


# 1. Create a self-signed certificate
$SelfSignedHT = @{
 DnsName = "$($env:COMPUTERNAME).$($env:USERDNSDOMAIN)".ToLower()
 CertStoreLocation = "Cert:\LocalMachine\My"
}
New-SelfSignedCertificate @SelfSignedHT
$cert = Get-ChildItem -Path Cert:\LocalMachine\My -SSLServerAuthentication

# 2. Export its public key
Export-Certificate -Cert $cert -Type CERT -FilePath ~/documents/cert.cer

# 3. Import the public key in the Trusted Root Certificate Authorities store
Import-Certificate -FilePath ~/documents/cert.cer -CertStoreLocation Cert:\LocalMachine\Root

# 4. Select this certificate in the SSL bindings
$cert | New-Item IIS:\SslBindings\0.0.0.0!8531

# 5. Require SSL for the following virtual roots only:
'SimpleAuthWebService','DSSAuthWebService',
'ServerSyncWebService','APIRemoting30',
'ClientWebService' | ForEach-Object {
 Set-WebConfigurationProperty -Filter 'system.webserver/security/access' -Location "WSUS Administration/$($_)" -Name sslFlags -Value 8
}

# 6. Switch WSUS to SSL
& 'C:\Program Files\Update Services\Tools\WsusUtil.exe' configuressl $("$($env:COMPUTERNAME).$($env:USERDNSDOMAIN)".ToLower())

# 7. Change your GPO to point to the new URL
$key = 'HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate'
$uri = 'https://{0}:8531' -f $("$($env:COMPUTERNAME).$($env:USERDNSDOMAIN)".ToLower())
Get-GPO -All | Foreach-Object { 
 if ($_ | Get-GPRegistryValue -Key $key -ValueName WUServer -EA 0) {
  $_ | Set-GPRegistryValue -Key $key -ValueName WUServer       -Value $uri -Type String
  $_ | Set-GPRegistryValue -Key $key -ValueName WUStatusServer -Value $uri -Type String
 }
}

You’re almost done.
The last step consists in distributing the public key of your self-signed certificate to your clients so that they trust your self-signed certificate.
To achieve this, you’ll need to edit your GPO that targets clients and import your ~/documents/cert.cer file into ‘Computer Configuration / Policies / Windows Settings / Security Settings / Public Key Policies / Trusted Root Certificate Authrorities
Import-PK-into-Root

You’ve successfully mitigated the threat reported at BH2015 😎
Note that the certificate is valid for 1 year.