Building a PowerShell enabled WinPE for UEFI native boot

Summary: The Windows Preinstallation Environment (WinPE) is a small operating system used to install, deploy, and repair  many editions of Windows. The baseline WinPE is on its own a very powerful tool, but it is when you enable PowerShell that it reveals the full potential. In my instructions below make it very simple to create a custom WinPE 64 bit for UEFI native boot using a USB Flash Drive.

 

Before you start, you will need to install the most recent Windows Assessment and Deployment Kit (Windows ADK), on the computer you are going to use to create the WinPE USB. Get the Windows ADK from the Hardware Dev Center or the latest build from Connect.

You will also need a USB flash drive that can be completely cleaned. The capacity will depend of the size and how many Windows images, drivers and tools you will keep in the drive, and whenever possible use USB 3.0 for increased performance.

 

Once the Windows ADK is installed, start a PowerShell CLI or ISE.

The first step is to create two variables with Path to the WinPE files in the ADK and to a temporary folder were we will customize the WinPE and USB flash drive content.

 $WinADK=”C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64”
$WinPETemp=’C:\WinPETemp’ 

 

Next is to create the file structure in the temporary folder and copy the minimum required files from the ADK. This includes the EFI folder with the Windows Boot Manager, the Boot folder with the Boot Configuration Data Store, and the actual WinPE image to the Sources folder. The mount folder will be used to mount the WinPE image file.

 New-Item -ItemType Directory -Path “$WinPETemp\PEDrive\Sources” -Force
Copy-Item -path “$WinAdk\en-us\winpe.wim” -Destination “$WinPETemp\PEDrive\Sources\boot.wim” -Force
Copy-Item -path “$WinAdk\Media\Boot” -Destination “$WinPETemp\PEDrive\” -Recurse
Copy-Item -path “$WinAdk\Media\EFI” -Destination “$WinPETemp\PEDrive\” -Recurse
New-Item -ItemType Directory -Path “$WinPETemp\Mount” –Force 

 

With all the files in place, the next step is to mount and add the WinPE packages to the base WinPE image. The next set of commands will mount the image and add these from the ADK.

 Mount-WindowsImage -ImagePath “$WinPETemp\PEDrive\Sources\boot.wim” -Index 1 –path “$WinPETemp\Mount”
Add-WindowsPackage -PackagePath “$WinAdk\Winpe_OCS\WinPE-WMI.cab” -Path “$WinPeTemp\Mount” –IgnoreCheck
Add-WindowsPackage -PackagePath “$WinAdk\Winpe_OCS\en-us\WinPE-WMI_en-us.cab” -Path “$WinPeTemp\Mount” -IgnoreCheck
Add-WindowsPackage -PackagePath “$WinAdk\Winpe_OCS\WinPE-WMI.cab” -Path “$WinPeTemp\Mount” –IgnoreCheck
Add-WindowsPackage -PackagePath “$WinAdk\Winpe_OCS\en-us\WinPE-WMI_en-us.cab” -Path “$WinPeTemp\Mount” –IgnoreCheck
Add-WindowsPackage -PackagePath “$WinAdk\Winpe_OCS\WinPE-NetFx.cab” -Path “$WinPeTemp\Mount” –IgnoreCheck
Add-WindowsPackage -PackagePath “$WinAdk\Winpe_OCS\en-us\WinPE-NetFx_en-us.cab” -Path “$WinPeTemp\Mount” –IgnoreCheck
Add-WindowsPackage -PackagePath “$WinAdk\Winpe_OCS\WinPE-Scripting.cab” -Path “$WinPeTemp\Mount” –IgnoreCheck
Add-WindowsPackage -PackagePath “$WinAdk\Winpe_OCS\en-us\WinPE-Scripting_en-us.cab” -Path “$WinPeTemp\Mount” –IgnoreCheck
Add-WindowsPackage -PackagePath “$WinAdk\Winpe_OCS\WinPE-PowerShell.cab” -Path “$WinPeTemp\Mount” –IgnoreCheck
Add-WindowsPackage -PackagePath “$WinAdk\Winpe_OCS\en-us\WinPE-PowerShell_en-us.cab” -Path “$WinPeTemp\Mount” –IgnoreCheck
Add-WindowsPackage -PackagePath “$WinAdk\Winpe_OCS\WinPE-DismCmdlets.cab” -Path “$WinPeTemp\Mount” –IgnoreCheck
Add-WindowsPackage -PackagePath “$WinAdk\Winpe_OCS\en-us\WinPE-DismCmdlets_en-us.cab” -Path “$WinPeTemp\Mount” –IgnoreCheck
Add-WindowsPackage -PackagePath “$WinAdk\Winpe_OCS\WinPE-EnhancedStorage.cab” -Path “$WinPeTemp\Mount” –IgnoreCheck
Add-WindowsPackage -PackagePath “$WinAdk\Winpe_OCS\en-us\WinPE-EnhancedStorage_en-us.cab” -Path “$WinPeTemp\Mount” –IgnoreCheck
Add-WindowsPackage -PackagePath “$WinAdk\Winpe_OCS\WinPE-StorageWMI.cab” -Path “$WinPeTemp\Mount” –IgnoreCheck
Add-WindowsPackage -PackagePath “$WinAdk\Winpe_OCS\en-us\WinPE-StorageWMI_en-us.cab” -Path “$WinPeTemp\Mount” –IgnoreCheck

 

This point is the best time to customize the WinPE image that will be loaded to the RAM drive, also known as the X:\ drive. In this example I will just make two simple customizations; add PowerShell CLI to auto start and set the PowerShell execution policy to unrestricted so I can run my unsigned PowerShell scripts without the need to modify the policy on every WinPE boot.

 ### Add powershell CLI to startnet.cmd
Add-Content -Path $WinPeTemp\Mount\Windows\system32\startnet.cmd -Value 'start /MAX PowerShell'

### Set powershell script execution policy to unrestricted
Reg.exe load hklm\TempHive "$WinPeTemp\Mount\Windows\system32\Config\SOFTWARE"
Set-ItemProperty -Path HKLM:\TempHive\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell -Name ExecutionPolicy -Value Unrestricted
Reg.exe unload hklm\TempHive 
If you plan to use this WinPE image on a Windows Deployment Services server, it is a good idea to add a line to startnet.cmd to map a network drive, very similar to add the PowerShell CLI but using the NET USE command

 

Once all the customizations are done we just need to dismount the WinPE image and commit all the changes.

 Dismount-WindowsImage -path “$WinPETemp\Mount” -Save 
Important note here is that the WinPE image cannot exceed 500Mb due to the RAM drive limitations. If you need to access large files keep them in the USB flash drive but outside the WinPE image. in the example here copy them to the PEDrive folder and they will be accessible to WinPE on the USB flash Drive. Another option is to keep your large files in a file share and remote access them from the WinPE

 

As a last step we need to identify the USB Flash Drive, set the partition style to GPT and format it as FAT32. Then copy all the files in the temporary PEDrive folder to the drive. If you will be using WDS this next step is not required as will you only need to import the modified boot.wim in the temporary folder to the boot images in the WDS Server.

 $Disk= Get-Disk | Where-Object { $_.BusType –eq ‘USB’ } | Out-Gridview –passthru
if ($Disk)
 { 
 Get-Disk -Number ($Disk.number) | Get-Partition | Remove-partition -confirm:$false
 Clear-Disk -Number $Disk.Number -RemoveData -RemoveOEM -confirm:$false | Set-Disk -Number $Disk.DiskNumber -PartitionStyle GPT
 $Partition = New-Partition -DiskNumber $Disk.Number -UseMaximumSize -GptType "{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}"
 Add-PartitionAccessPath -DiskNumber $Partition.DiskNumber -PartitionNumber $Partition.PartitionNumber -AccessPath “$WinPETemp\Mount”
 Format-Volume -Partition $Partition -FileSystem FAT32 -NewFileSystemLabel PowerPE -Force

 Copy-Item -Path "$WinPETemp\PEDrive\*" -Destination "$WinPETemp\Mount" -Recurse -Force

 Remove-PartitionAccessPath -DiskNumber $Partition.DiskNumber -PartitionNumber $Partition.PartitionNumber -AccessPath “$WinPETemp\Mount”
 } 
The UEFI firmware will look for the bootx64.efi file in the FSx:EFI\boot folder and will be able to boot WinPE without the need of a Master Boot Record.

 

The different system vendors will have different interfaces to boot from the USB drive, but all of them should have that option.