Build a PowerShell-Enabled Windows PE Key: Part 2

Summary: Customize a Windows PE environment to contain Windows PowerShell and DISM modules.

Honorary Scripting Guy, Sean Kearney, is here again today with some early holiday gifts! A little PowerShell in a mean, lean green environment!

   Note   This is a five-part series that includes the following posts:

Now that we have the necessary software installed in Windows to build out a Windows PE environment, let’s take a look at building it.

The Windows PE environment is pretty neat because most of the actual boot environment is ready for you. But the actual “live environment” is built into a WIM file that loads into memory after boot.

A WIM file is a “Windows Imaging Format” file, which is really a specialized archive that holds all of the files for the Windows operating system.

The WIM file for Windows PE is located in one of two folders, depending on whether you’re working with the 32-bit or the 64-bit version. We’ll be dealing with the 64-bit version, but the process is identical for the 32-bit version.

The WIM file we need is located in this folder:

C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\en-us

In this folder, there is only one file called WinPE.wim. We’ll be making a copy of this file to work with.

First we’ll store the location of the Windows PE media in a PowerShell variable called $WinAdk to make things more readable. We’ll take advantage of the Get-ArchitectureString function that we created yesterday to ensure the path is always correct for our parent operating system.

$WinADK=”C:\Program Files$(Get-ArchitectureString)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\en-us”

We will then create a structure to hold the Windows PE WIM file and a place to temporarily mount the WIM file:


New-Item -ItemType Directory -Path “$WinPETemp\Media” -force
Copy-Item -path "$WinAdk\en-us\winpe.wim" -Destination "$WinPETemp\Media\boot.wim"
New-Item -ItemType Directory -Path "$WinPETemp\Mount" –Force

Now that we have the WIM file, we can mount it with PowerShell by using the Mount-WindowsImage cmdlet. This will open the WIM file for editing. It will extract the structure to a target folder where we can add files and content.

Mount-WindowsImage -ImagePath "$WinPETemp\Media\boot.wim" -Index 1 –path "$WinPETemp\Mount"

At this point, we need to add a series of packages to the WIM file to enhance its capabilities. To find the list and names of packages that are available for Windows PE, we can use this article from TechNet: WinPE: Add packages (Optional Components Reference). It describes all the current pacakges, including dependencies.

The list of these packages (OCS files) can be found in the WinPE_OCS folder.

In our environment, we will not only be adding Windows PowerShell but also the storage and DISM cmdlets.

For each component we need to add, we provide the CAB file of the optional component in Windows PE and the folder where we “mounted” the WIM file. You’ll need to add not only the OCS package but also its language equivalent for everything to work properly.

To do this, we use the Add-WindowsPackage cmdlet. Here’s an example of using this cmdlet to apply the WMI capability to a Windows PE environment:

Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\" -Path "$WinPeTemp\Mount" –IgnoreCheck
Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\en-us\" -Path "$WinPeTemp\Mount" -IgnoreCheck

To populate all of our components, the script looks like this:

Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\" -Path "$WinPeTemp\Mount" –IgnoreCheck
Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\en-us\" -Path "$WinPeTemp\Mount" –IgnoreCheck
Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\" -Path "$WinPeTemp\Mount" –IgnoreCheck
Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\en-us\" -Path "$WinPeTemp\Mount" –IgnoreCheck
Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\" -Path "$WinPeTemp\Mount" –IgnoreCheck
Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\en-us\" -Path "$WinPeTemp\Mount" –IgnoreCheck
Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\" -Path "$WinPeTemp\Mount" –IgnoreCheck
Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\en-us\" -Path "$WinPeTemp\Mount" –IgnoreCheck
Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\" -Path "$WinPeTemp\Mount" –IgnoreCheck
Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\en-us\" -Path "$WinPeTemp\Mount" –IgnoreCheck
Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\" -Path "$WinPeTemp\Mount" –IgnoreCheck
Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\en-us\" -Path "$WinPeTemp\Mount" –IgnoreCheck
Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\" -Path "$WinPeTemp\Mount" –IgnoreCheck
Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\en-us\" -Path "$WinPeTemp\Mount" –IgnoreCheck

Now that we’ve updated our WIM file, we can dismount and save the image:

Dismount-WindowsImage -path "$WinPETemp\Mount"

Let’s back up this file so we can use it later:

New-Item -Path $Destination -ItemType Directory –Force
Copy-Item -path "$WinPETemp\Media\boot.wim" -destination "$Destination\"

At this point, we have a WIM file for Windows PE that is packed with the bits for Windows PowerShell 5.0!

Stay tuned! Tomorrow we’ll use Windows PowerShell to create a bootable USB key.

I invite you to follow the Scripting Guys on Twitter and Facebook. If you have any questions, send email to them at, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then always remember that with great PowerShell comes great responsibility.

Sean Kearney, Honorary Scripting Guy, Cloud and Datacenter Management MVP 

Comments (8)

  1. Peter Ferry says:

    Hi, thanks for these articles, they are awesome and come at a perfect time for me as i was looking at creating one of these today. So i have set up your script as you have and noticed an error, under the $WinADK variable you have en-us on the end, took
    me a few goes to work out why the packages couldn’t be found.

    Quick question though, I have tested the add-package function with both

    Add-WindowsPackage -PackagePath "$($WinAdk)" -Path "$WinPeTempMount" –IgnoreCheck


    Add-WindowsPackage -PackagePath "$" -Path "$WinPeTempMount" –IgnoreCheck

    Both seem to work (although i havent booted to the boot.wim to check functionality) so i was wondering why do you put the $WinAdk in a $( ) ‘s? I have tried to googlefu it but come up blank so far (need to practice my googlefu some more).



  2. @Peter Ferry

    It’s actually from Lazniness 🙂

    It was copied directly from my DeployImage Module which has a New-WindowsPEWim Cmdlet that builds out this structure. There really is no difference. Putting an extra set of brackets allowed me to do stuff like this.


    Which was an idea I had which settled into just using a variable


    …in this case

    "$WinADK" and "$($WinAdk)" do EXACTLY the same thing 🙂


  3. … and you are most correct. The "en-us" should not be in the $WinADK path. typo!


  4. Also note that

    Dismount-WindowsImage -path "$WinPETempMount"

    Should read

    Dismount-WindowsImage -path "$WinPETempMount" -save


  5. L. says:

    Great stuff! I didn’t know it was possible to add Powershell to a WinPE image.
    Would it be possible to configure Powershell remoting in the image, for use on headless servers?
    On Hyper V, does Powershell Direct work when the VM boots a WinPE image?

  6. @l

    ** Would it be possible to configure Powershell remoting in the image, for use on headless servers? **

    I believe PowerShell in this scenario is limited. I don’t think you can use it to either server or Receive a PSREmote session. I haven’t tried PowerShell Direct to see if it would attach to WindowsPE w/PowerShell.

    Could be a fun try on the weekend tho 🙂


  7. Peter Ferry says:

    Thanks Sean.

  8. sapba says:

    Thank you for great series of useful articles.

    I followed the information provided in this article and planning to utilize DISM module but couldn’t import DISM module when booted in the WinPE.

    PS> import-module dism
    import-module: could not load file or assembly
    ‘file:///X:Windowssystem32WindowsPowerShellv1.0ModulesdismMicrosoft.Dism.PowerShell.dll’ or one of its dependencies.

    The ‘dism’ command itself works just fine.

    Side note:

    I could not use ‘Add-WindowsPackage’ command as is.
    My dev server is Windows Server 2012 R2. I installed ADK for Windows 10.
    When running ‘Add-WindowsPackage’, it’s using dism from Windows Server 2012 R2 that is version ‘6.3.9600’ while winpe.wim that I used required dism version ‘10.0.10240’. So instead of using ‘Add-WindowsPackage’, I was using dism from ‘ADK’ (‘C:Program Files
    (x86)Windows Kits10Assessment and Deployment KitDeployment Toolsamd64DISMdism.exe’) to install the packages.

Skip to main content