Windows Server 2012 R2 Hyper-V provides the ability to easily export sysprepped virtual machines as reusable VM templates for quickly provisioning new VMs in an on-premises datacenter or on the Windows Azure cloud platform.  BUT … keeping those VM templates up-to-date with the latest Windows Updates can be time consuming, because the VM templates themselves are not actively running as VMs that can be updated online.  Fortunately, PowerShell makes it easy to keep your VM templates well maintained by using the offline operating system image servicing cmdlets that are present in the Dism PowerShell module.

  
Keep Your VM Templates Well Maintained with Offline Servicing

In this article, I’ll provide a sample PowerShell script, with comments, that you can leverage as the starting point for your own automated offline VM template servicing process.

Prepare Your Lab …

To prepare your lab environment for testing this script, you’ll need to complete the following:

  1. Download and install Windows Server 2012 R2 and enable the Hyper-V role.
     
    You can use the free online study guides available via the Early Experts program to help guide you through this process, if needed.
     
  2. Using Hyper-V Manager, provision one or more new VM’s to be used as your VM templates.
     
  3. Using the Sysprep command from the console of each VM, prepare each VM as a reusable generalized template.
     
    C:\Windows\System32\Sysprep.exe /Generalize /OOBE /Shutdown
     
  4. Using Hyper-V Manager, export each VM to a VMTemplates folder location.
     
  5. Download the latest set of relevant updates to an Updates folder location from the Microsoft Update Catalog site.
     
  6. Create a Temp folder location for use as a temporary location when extracting Windows Updates.

Done! Ready to script …

Once you’ve setup your lab environment, use the sample PowerShell script below to test and customize the automated process of applying patches to your exported VM templates.  Be sure to review and change the first few lines to reflect the folder paths being used in your lab.

# Set Base Folder containing Exported VM Templates
$VHDImagePath = "D:\VMTemplates"

# Set Windows OS Folder Name used inside VM Templates
$windowsOSFolder = "Windows"

# Set Base Folder containing downloaded Windows Updates
$updatePath = "D:\Updates"

# Set Scratch Folder to use when extracting Windows Updates
$tempPath ="D:\Temp"

# Find all VHD and VHDX files in the Template Folder
$VHDImages = Get-ChildItem -Path $VHDImagePath -Include *.vhd,*.vhdx -Recurse -Force | Select-Object -Property FullName

# Find all MSU and CAB file updates in the Update Folder
$windowsUpdates = Get-ChildItem -Path $updatePath -Include *.msu,*.cab -Recurse -Force | Select-Object -Property FullName

# Apply the updates using Offline Services for each VHD or VHDX template
ForEach ( $VHDImage in $VHDImages )
{

Write-Output "Processing: " $VHDImage.FullName         
   
# Mount the VHD or VHDX file          
$mountedVHD = [string](Mount-VHD -Path $VHDImage.FullName -Passthru | Get-Disk | Get-Partition | Get-Volume | Where-Object -Property FileSystemLabel -NE "System Reserved").DriveLetter + ":\"

$mountedVHD = $mountedVHD.Substring($mountedVHD.Length-3,3)

# Test whether mounted VHD or VHDX file is an OS Image          
If ( Test-Path $mountedVHD$windowsOSFolder )        
{

# Apply all updates to the Mounted VHD or VHDX          
ForEach ( $windowsUpdate in $windowsUpdates )        
{

Write-Output "Applying Update: " $windowsUpdate.FullName

# Apply a single Update          
Add-WindowsPackage -Path $mountedVHD -PackagePath $windowsUpdate.FullName -ScratchDirectory $tempPath -WindowsDirectory $windowsOSFolder

}

}

# Dismount the VHD or VHDX        
Dismount-VHD -Path $VHDImage.FullName

}

Share Your Results!

Have you modified this script for special scenarios in your environment? Be sure to share your changes and feedback in the Comments area below!