Weekend Scripter: Use PowerShell to Patch VHD and WIM Image Files


    Summary: Microsoft PFE Ben Pearce shows how to use Windows PowerShell to perform offline servicing of Virtual Disks (VHD) and Windows Imaging Format (WIM) files.   Microsoft Scripting Guy Ed Wilson here. We continue with guest blogger Weekend Scripter today by having a post written by Ben Pearce. Ben Pearce is a UK based Premier Field Engineer at Microsoft who spends his time helping customers understand how to deploy and maintain healthy IT systems.  Ben specialises in helping customers with Windows PowerShell and Virtualisation technologies and can be found helping run 2 blogs: http://blogs.technet.com/mspremuk and http://blogs.technet.com/benp.  Ben co-authored this script with deployment expert Jason Stiff http://blogs.technet.com/jasons. Here is Ben.   This post discusses the ServiceImage.ps1 script that is located on the Scripting Guys Script Repository.  This script injects patches to an offline .vhd or .wim file. It is a really useful script and also shows nicely how to wrap regular command line tools in Windows PowerShell. Hopefully this post will help you do the following tasks.

  1. Understand the high level flow of the script
  2. Use Deployment Image Servicing and Management (DISM) and DiskPart command lines
  3. Generate command lines at run time
  4. Run command lines within Windows PowerShell

  1. High Level Script Flow Hopefully you will not find the flow of this script too tricky to follow. It really starts just below the section that says Common Processing. However, if you are still coming to grips with scripting this is roughly how the script works:

  1. Prompt the user for the location of the .vhd/.wim and the patches.
  2. Check that there is an image and patches in specified folders.
  3. If the image is a .vhd file:
    1. Generate cmdlines to mount and unmount the vhd with DiskPart.
    2. Generate cmdlines to inject the patches with DISM.
    3. Mount the vhd with Diskpart.
    4. Inject the patches with DISM.
    5. Unmount the vhd with Diskpart.
  4. If the image is a .wim file.
    1. Specify which part of the .wim file to patch.
    2. Mount the .wim file in a directory with DISM.
    3. Generate cmdlines to inject the patches with DISM.
    4. Inject the Patches with DISM.
    5. Unmount the .wim file.

  2. DISM and DiskPart DISM is a really powerful command line tool that enumerates, installs, uninstalls, configures, and updates features and packages in Windows images.  More details about how to use DISM can be found in the Microsoft TechNet library or by typing dism /? at the CMD prompt or inside the Windows PowerShell console. Remember that to run dism (which includes obtaining help information about the program) Administrator rights are required. DiskPart is a very powerful command line tool that can be used to make changes to disks and volumesand mount .vhd files as volume. The problem with using DiskPart is that you cannot give it a simple command line to execute a command.  DiskPart uses the concept of script files that can be used to automate a sequence of tasks. Therefore, to automate the use of DiskPart you must first generate a text file that contains the commands and then call DiskPart with reference to the script file. More details about how to use DiskPart can be found in support article KB300415.   3. Generating Command Lines at Runtime The main challenge in creating this script is generating the command lines that can be invoked. To resolve this, a number of functions have been created:

  • Gendismcmd – used to generate DISM command lines
  • Gendpmountscript – used to generate a script file to be called by DiskPart
  • GendpUnmountscript – used to generate a script file to be called by DiskPart

  We will not examine all these functions because lots of the ideas are repeated, but let’s examine gendismcmd and gendpmountscript.   Gendismcmd This function takes 2 parameters: an array of .cab files (patches) and the path of the image to be patched. The function generates the beginning of the command line and then loops through each of the patches in the array and appends (+=) that patch to the end of the command line. Finally, the function returns the entire command line.

function gendismcmd($cabs,$img)



          $cmdline = “DISM /image:” + $img + ” /Add-Package”

          foreach($cab in $cabs)


                   $cmdline += ” /PackagePath:” + “‘” + $cab.fullname + “‘”


          return $cmdline


}   Gendpmountscript This function takes 3 parameters: a temporary drive to mount the .vhd, the path of where you want to create the DiskPart script and the path to the .vhd file. We first create an empty array and then append (+=) command lines to it. After we have finished creating the array full of command lines we use set-content to dump the array in a text file. You will notice we have to create 2 scripts because we have to use DiskPart in 2 stages.

function gendpmountscript($drive, $scriptpath, $vhd)


          $dpscript = @()

          $dpscript += (“select vdisk file=” + “‘” + $vhd +”‘”)

          $dpscript += “attach vdisk”

          $dpscript += “exit”

          set-content -path “$scriptpathdpmount1.txt” -value $dpscript


          $dpscript = @()

          $dpscript += (“select vdisk file=” + “‘” + $vhd +”‘”)

          $dpscript += “Select partition 2”

          $dpscript += (“assign letter=” + $drive)

          $dpscript += “exit”

          set-content -path “$scriptpathdpmount2.txt” -value $dpscript

}   4. Running Command Lines from Windows PowerShell Running command lines from Windows PowerShell is really easy if you use the Windows PowerShell cmdlet Invoke-Expression. I’ve seen people (including me) struggle using the ampersand (&) operator, get stuck using double quotation marks (“) and single quotation marks (‘) and get frustrated calling other command line tools. If you are not sure, use Invoke-Expression. After you have generated the command line, running the command line is as easy as:

Invoke-Expression $dismcmdline   However, you might feel it can be overkill.  If you have a really simple command line, you might not want to use it.  For example, when calling DiskPart in the script we do the following:

diskpart /s “$dpscriptpathdpmount2.txt”   My philosophy is that as soon as the command line is becoming tricky I use the Invoke-Expression cmdlet. I hope that helped and if you have any questions, get in touch.   That wraps up another weekend. Thanks Ben for taking the time to write this blog post for everyone to learn a new tactic. I invite you to follow me on Twitter or Facebook. If you have any questions, send email to me at scripter@microsoft.com or post them on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.   Ed Wilson, Microsoft Scripting Guy


Discussion is closed.

Feedback usabilla icon