Offline Servicing of VHDs against WSUS

Every month we are hit by Microsoft's patchday. And every month the same workflow fires: learn about updates, test them, approve necessary updates, check compliance of machines and manually fix machines with failed updates . But don't think you are done. You still need to update VM templates so that new VMs have the latest updates. I'd like to provide you with a useful script to automate this important task.

Why build your own solution?

Virtual Machine Servicing Tool (VMST) is actually a collection of scripts built for System Center Virtual Machine Manager to enable servicing of offline VMs. Although I do not have an official statement, the latest release from August 2012 does not work with Service Pack 1 of System Center 2012 Virtual Machine Manager. In addition, VMST was built using VBScript using the COM interface to WSUS.

My solution is independent of any management product and only relies on PowerShell, .NET API, WSUS and built-in tools. Here is an example how it is invoked:

Apply-WindowsUpdates.ps1 -VhdPath .\WS2012_Template.vhdx -MountDir "c:\temp\mnt" -WsusServerName wsus01 -WsusServerPort 8530 -WsusTargetGroupName "Windows Server 2012" -WsusContentPath "c:\WsusContent"

Most of the command line parameters are necessary to narrow down which updates to apply to the image. I recommend you do not remove those lines (UpdateScope) from the script. Although inappropriate updates will be skipped it takes some time to determine whether an update is applicable. Therefore, you better try as few updates as possible.

Important note: There are some updates which cannot be applied through offline servicing. Those are rare and mainly affect the servicing stack. But you will have to apply them through some other means, e.g. manually or through WSUS after the first boot of a VM or by manually starting the template.

Prerequisites: You need to run the script either on the WSUS server or on a server which has the WSUS management tools installed.

Download the script from the TechNet gallery.

Walkthrough

The script itself contains many comment to illustrate the approach taken to apply the updates. In addition, it also lists references to the API documentation of the .NET classes used. In case you decide to reuse excerpts in another script you will easily find the appropriate documentation.

For the script to work it requires several parameters like which VHD to update, which WSUS server to contact and where to find updates on the hard disk.

param (
    [string]$VhdPath = $(throw "Parameter VhdPath is required."),
    [string]$MountDir = $(throw "Parameter MountDir is required."),
    [string]$WsusServerName = $(throw "Parameter WsusServerName is required."),
    [Int32]$WsusServerPort = $(throw "Parameter WsusServerPort is required."),
    [string]$WsusTargetGroupName = $(throw "Parameter WsusTargetGroupName is required."),
    [string]$WsusContentPath = $(throw "Parameter WsusContentPath is required.")
)

Microsoft has done a brilliant job in exposing WSUS in .NET and therefore in PowerShell. The classes relevant to this script are located in the namespace Microsoft.UpdateServices.Administration which needs to be loaded before it can be used.

[reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") | Out-Null

The connection with a WSUS server is established using the static method getUpdateServer of the class AdminProxy and requires the name and port of the WSUS server.

$wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer($WsusServerName, $False, $WsusServerPort)

Before the WSUS server is asked for a list of update, an update scope is created to narrow down the relevant updates. There are several criteria like a computer target group and a timestamp.

$UpdateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope

The updates will be applied using the tool dism (Deployment Image Servicing and Management tool) which requires the image to be mounted. This is also done using dism.

dism /Mount-Image /ImageFile:"$VhdPath" /Index:1 /MountDir:"$MountDir"

The WSUS server is asked for a list using the update scope. The list is then processed to apply all updates.

$wsus.GetUpdates($UpdateScope) | ForEach {

The files associated with an update are retrieved by the method GetInstallableItems and returns a list of files with a virtual path inside the WSUS content directory. Therefore, the path needs to be translated to the actual location on the hard disk.

$_.GetInstallableItems().Files {

Each of the files (installable items) is then applied to the mounted VHD file using dism. BY the way, dism uses the term "package" for updates to the image because there are other types of packages next to updates.

dism /Image:"$MountDir" /Add-Package /PackagePath:"$FileName"

After all files have been applied to the mounted VHD file, it is important to unmount it using the Commit switch to write the changes to the file. Otherwise all the changes are lost.

dism /Unmount-Image /MountDir:"$MountDir" /Commit

AnhangGröße
apply-windowsupdates.zip2.39 KB

++++ Wir suchen Verstärkung ++++ Arbeitskultur, IT Kompetenz und Innovation werden bei sepago zum Wohle unserer Mitarbeiter und Kunden maximal gefördert. Das ist der Sinn der sepago. Wenn Dich das anspricht, dann schau doch mal im Karrierebereich.

4 responses for "Offline Servicing of VHDs against WSUS"

This is just what I was

This is just what I was looking for to update some offline wim images with updates from wsus. I noticed your script excludes the psf files but not the exe files. As far as I know Dism cannot integrate an exe file and will work only with the .cab files. Was this an oversight or has something changed recently that I am not aware of. In any case I believe dism will just skip over files it cannot integrate so it will still continue normally. It may take more time to do so however.

As far as I know dism only

As far as I know dism only integrated CAB files. The filter you are talking about is not exhaustive at the moment and is one thing that needs improvement. I'll put it on my todo list with the other planned improvements for the script. Unfortunately, I cannot promise an update soon :-(

Best regards,
Nicholas

Hi I ran your script and got

Hi I ran your script and got the following error. My WSUS server is a standalone server.

PS F:\BaseVHDs> .\Apply-WindowsUpdates.ps1 -VhdPath F:\BaseVHDs\2012.en_us.2012.07.vhd -MountDir "f:\tmp" -WsusServerNam
e wsus2k3 -WsusServerPort 8530 -WsusTargetGroupName "Windows Server 2012" -WsusContentPath "D:\WSUS\WsusContent"
Unable to find type [Microsoft.UpdateServices.Administration.AdminProxy]: make sure that the assembly containing this
type is loaded.
At F:\BaseVHDs\Apply-WindowsUpdates.ps1:15 char:1
+ $wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer($W ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Microsoft.Updat...tion.AdminProxy:TypeName) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound

New-Object : Cannot find type [Microsoft.UpdateServices.Administration.UpdateScope]: make sure the assembly containing
this type is loaded.
At F:\BaseVHDs\Apply-WindowsUpdates.ps1:19 char:16
+ $UpdateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

Unable to find type [Microsoft.UpdateServices.Administration.ApprovedStates]: make sure that the assembly containing
this type is loaded.
At F:\BaseVHDs\Apply-WindowsUpdates.ps1:22 char:1
+ $UpdateScope.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedS ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Microsoft.Updat....ApprovedStates:TypeName) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound

Unable to find type [Microsoft.UpdateServices.Administration.UpdateInstallationStates]: make sure that the assembly
containing this type is loaded.
At F:\BaseVHDs\Apply-WindowsUpdates.ps1:25 char:1
+ $UpdateScope.IncludedInstallationStates = [Microsoft.UpdateServices.Administrati ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Microsoft.Updat...tallationStates:TypeName) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound

Property 'FromArrivalDate' cannot be found on this object; make sure it exists and is settable.
At F:\BaseVHDs\Apply-WindowsUpdates.ps1:28 char:1
+ $UpdateScope.FromArrivalDate = $Now.AddDays(-1 * ($Now.Day - 1))
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFound

You cannot call a method on a null-valued expression.
At F:\BaseVHDs\Apply-WindowsUpdates.ps1:30 char:1
+ $TargetGroup = $wsus.GetComputerTargetGroups() | where { $_.Name -eq $WsusTarget ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

You cannot call a method on a null-valued expression.
At F:\BaseVHDs\Apply-WindowsUpdates.ps1:31 char:1
+ $UpdateScope.ApprovedComputerTargetGroups.Add($TargetGroup) | Out-Null
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

Deployment Image Servicing and Management tool
Version: 6.2.9200.16384

Mounting
[==========================100.0%==========================]
The operation completed successfully.
You cannot call a method on a null-valued expression.
At F:\BaseVHDs\Apply-WindowsUpdates.ps1:39 char:1
+ $wsus.GetUpdates($UpdateScope) | ForEach {
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

Deployment Image Servicing and Management tool
Version: 6.2.9200.16384

Unmounting
[==========================100.0%==========================]
The operation completed successfully.

Hi, the errors are caused by

Hi,

the errors are caused by the assemblies not being loaded. You need to run the script either on the WSUS server or on a server which has the WSUS management tools installed.

I have just updated the script with a more robust and enhanced version. It will now bail out in your situation telling you about the root cause.

Best regards,
Nicholas

Microsoft Competence Blog

Application Infrastructure mit Microsoft Technologien ist ein wichtiges Fokusthema der sepago. Wir haben langjährige Projekterfahrung, sind neugierig auf neue Technologien und möchten diese bis in letzte Detail verstehen. Die Competence Blogs berichten davon.

RSS-Feed Alle Artikel des Competence Blogs abonnieren.

 

About the author

Bild von Nicholas Dille
Nicholas Dille
Head of Technology and Innovation
Blogs about Centralized computing, virtualization and performance monitoring

All articles