A lightweight, fully automated Windows imaging solution using WinPE, a custom HTA progress UI, and a CMD deployment script. No MDT, no SCCM, no server infrastructure required — just a bootable USB and a captured WIM image.
This project provides everything you need to deploy a captured Windows image to bare-metal hardware automatically from a bootable USB drive. It consists of:
startnet.cmd— the deployment engine that runs diskpart, DISM, and bcdboot sequentiallydeploy.hta— a branded HTML Application that displays real-time deployment progresswinpeshl.ini— configures WinPE to launch the deployment automatically on bootpartition.txt— diskpart script for UEFI/GPT partitioning
The HTA UI and the CMD script communicate via flag files written to the WinPE RAM disk (X:\), keeping the two completely decoupled — the CMD does all the work, the HTA just watches and displays progress.
Boot USB
└── winpeshl.ini
├── wpeinit.exe (loads WinPE drivers)
└── startnet.cmd
├── Launches deploy.hta (progress UI)
├── Finds USB drive containing install.wim
├── Runs diskpart → writes X:\s_step1 / X:\s_step1_done
├── Runs DISM → writes X:\s_step2 / X:\s_step2_done
├── Runs bcdboot → writes X:\s_step3 / X:\s_step3_done
├── Writes X:\s_step4_done
└── wpeutil reboot
deploy.hta (running in parallel)
└── Polls flag files every 2-3 seconds via JavaScript setTimeout
└── Updates progress bars and step indicators as each flag appears
- Windows ADK with WinPE add-on installed
- A captured Windows image (
install.wim) — see Capturing an Image below - USB drive — minimum 64 GB recommended
- Rufus for writing the ISO to USB — https://rufus.ie
- Target hardware must support UEFI boot (GPT partition scheme)
winpe-deploy/
├── deploy/
│ ├── deploy.hta # Progress UI (HTA)
│ ├── startnet.cmd # Deployment engine
│ ├── launch_hta.vbs # Launches HTA without creating a console window
│ └── partition.txt # Diskpart UEFI/GPT partition script
└── winpeshl.ini # WinPE shell configuration
Open an elevated command prompt with the ADK environment loaded (use the Deployment and Imaging Tools Environment shortcut installed with the ADK).
copype amd64 C:\WinPE_amd64Add required WinPE optional components:
dism /Mount-Image /ImageFile:"C:\WinPE_amd64\media\sources\boot.wim" /Index:1 /MountDir:C:\WinPE_Mount
dism /Add-Package /Image:C:\WinPE_Mount /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-WMI.cab"
dism /Add-Package /Image:C:\WinPE_Mount /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-WMI_en-us.cab"
dism /Add-Package /Image:C:\WinPE_Mount /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-Scripting.cab"
dism /Add-Package /Image:C:\WinPE_Mount /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-Scripting_en-us.cab"
dism /Add-Package /Image:C:\WinPE_Mount /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-HTA.cab"
dism /Add-Package /Image:C:\WinPE_Mount /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-HTA_en-us.cab"
dism /Unmount-Image /MountDir:C:\WinPE_Mount /CommitBefore you can deploy, you need a captured Windows image (install.wim). Start from a fully configured Windows installation.
Install and configure Windows on your reference machine exactly as you want it deployed:
- Install all drivers
- Install all required software
- Configure settings, user accounts, wallpapers, etc.
- Do not join a domain before capturing (join post-deployment via script if needed)
Sysprep generalises the image so it can be deployed to different hardware and generates unique SIDs on first boot.
Open an elevated command prompt on the reference machine and run:
C:\Windows\System32\Sysprep\sysprep.exe /oobe /generalize /shutdown/oobe— boots into Out-of-Box Experience on first deployment/generalize— removes hardware-specific information and unique identifiers/shutdown— shuts down after sysprep completes (do not boot back in — this would break the generalisation)
Important: After sysprep shuts the machine down, do not boot it back into Windows. Boot it from your WinPE USB instead.
Boot the sysprepped reference machine from your WinPE USB. Once at the WinPE environment, identify your drive letters and run DISM to capture:
:: List volumes to confirm drive letters
diskpart
list volume
exit
:: Capture the image (replace C: with your Windows volume letter if different)
dism /Capture-Image ^
/ImageFile:"D:\sources\install.wim" ^
/CaptureDir:C:\ ^
/Name:"Windows 11 Custom Image" ^
/Description:"Captured on %date%" ^
/Compress:max ^
/CheckIntegrityReplace D: with the drive letter of your USB drive. The capture will take 15–45 minutes depending on image size and USB speed.
dism /Get-ImageInfo /ImageFile:"D:\sources\install.wim"This confirms the image was captured correctly and shows the index number (you'll need this for deployment — usually Index 1).
Copy your captured install.wim to the sources folder of your WinPE working directory:
copy "D:\sources\install.wim" "C:\WinPE_amd64\media\sources\install.wim"If an install.wim already exists there, replace it.
Create the deploy folder in the WinPE mount and copy all project files:
dism /Mount-Image /ImageFile:"C:\WinPE_amd64\media\sources\boot.wim" /Index:1 /MountDir:C:\WinPE_Mount
mkdir C:\WinPE_Mount\deploy
copy "deploy\deploy.hta" "C:\WinPE_Mount\deploy\deploy.hta" /y
copy "deploy\startnet.cmd" "C:\WinPE_Mount\deploy\startnet.cmd" /y
copy "deploy\launch_hta.vbs" "C:\WinPE_Mount\deploy\launch_hta.vbs" /y
copy "deploy\partition.txt" "C:\WinPE_Mount\deploy\partition.txt" /y
copy "winpeshl.ini" "C:\WinPE_Mount\Windows\System32\winpeshl.ini" /y
dism /Unmount-Image /MountDir:C:\WinPE_Mount /CommitMakeWinPEMedia /ISO C:\WinPE_amd64 "C:\Output\WinPE_Deploy.iso"- Open Rufus (https://rufus.ie)
- Select your USB drive under Device
- Click SELECT and choose
WinPE_Deploy.iso - Click START
- When the Windows User Experience popup appears, untick all options and click OK
- Click OK to confirm — this will erase the USB drive
- Wait for Rufus to show READY, then eject the USB
- Disable Secure Boot in the target machine's BIOS (required for WinPE to boot)
- Insert the deployment USB
- Boot from the USB (select it from the boot override menu in BIOS)
- The deployment UI will appear and the process runs automatically
- When complete, remove the USB — the machine reboots into the deployed Windows image
Edit deploy/partition.txt to change the partition scheme. The default creates a UEFI/GPT layout:
select disk 0
clean
convert gpt
create partition efi size=100
format quick fs=fat32 label=System
assign letter=S
create partition msr size=16
create partition primary
format quick fs=ntfs label=Windows
assign letter=W
exit
The deployment script uses drive letters S: (EFI) and W: (Windows). If you change these, update the bcdboot command in startnet.cmd accordingly:
bcdboot W:\Windows /s S: /f UEFIWhen you need to update the deployed image (new software, settings changes, etc.):
- Boot an already-deployed machine into Windows
- Make your changes
- Run Sysprep again:
C:\Windows\System32\Sysprep\sysprep.exe /oobe /generalize /shutdown
- Boot from the WinPE USB and capture a new
install.wim(see Step 2c) - Replace
C:\WinPE_amd64\media\sources\install.wimwith the new image - Rebuild the ISO (Step 5) and rewrite the USB (Step 6)
- Done — all deployment scripts remain unchanged
The HTA (deploy.hta) is a pure display layer — it makes no shell calls and runs no commands. It works by:
- Being launched via
wscript.exe //B launch_hta.vbsfromstartnet.cmd— this avoids creating a new console host window - Using a JavaScript
setTimeoutloop to poll flag files onX:\every 2–3 seconds - Updating DOM elements (progress bars, step indicators, status text) as each flag file appears
startnet.cmdwrites flag files at each stage:X:\s_step1,X:\s_step1_done,X:\s_step2, etc.
Why JavaScript for the timer? WinPE's
mshta.exeuses a legacy IE engine where VBScriptsetTimeoutpauses when the HTA window loses focus. JavaScriptsetTimeoutcontinues running regardless — essential for keeping the UI alive while the console is visible.
| File | Written when |
|---|---|
X:\s_step1 |
diskpart starts |
X:\s_step1_done |
diskpart completes |
X:\s_step2 |
DISM starts |
X:\s_step2_done |
DISM completes |
X:\s_step3 |
bcdboot starts |
X:\s_step3_done |
bcdboot completes |
X:\s_step4_done |
all done, reboot imminent |
X:\s_error_usb |
install.wim not found |
X:\s_error_part |
diskpart failed |
X:\s_error_dism |
DISM failed |
X:\s_error_bcd |
bcdboot failed |
- Console windows always visible: In WinPE (no Explorer shell), any console application spawned from a GUI app ignores window style parameters and creates a visible window. This is why the deployment engine runs in
startnet.cmd(its own console) rather than being called from the HTA. - VBScript
setTimeoutpauses on focus loss: Use JavaScript timers for anything that must keep running when the HTA is not the focused window. - No flexbox/CSS Grid: WinPE's mshta uses an IE7–IE9 engine. Use
display:table/table-cellor floats for layouts. Force IE9 mode with<meta http-equiv="x-ua-compatible" content="ie=9">— do not useie=11orie=edgeas these break VBScript. WshShell.Execpipe deadlock: Long-running processes with lots of output (like DISM) will deadlock if called viaExecbecause internal pipe buffers fill up. Always useShell.Runwith flag file polling for DISM.
Deployment doesn't start / HTA appears but nothing happens
- Ensure
wpeinit.exeruns beforestartnet.cmd(it's listed first inwinpeshl.ini) - Check that all files are in
X:\deploy\on the WinPE image
USB drive not found / DISM can't find install.wim
- The script scans drive letters C through L for
sources\install.wim - After diskpart runs, drive letters may shift — check that your USB isn't assigned S or W (reserved for the partitions)
- Expand the drive letter scan range in
startnet.cmdif needed
DISM fails with an error
- Verify your
install.wimis not corrupted:dism /Get-ImageInfo /ImageFile:"D:\sources\install.wim" - Ensure the target disk is large enough for the image
- Check that diskpart completed successfully before DISM ran
Device won't boot from USB
- Disable Secure Boot in BIOS
- Ensure the USB was written in ISO mode by Rufus (not DD mode)
- Try a different USB port
HTA progress bar freezes but deployment continues
- The deployment itself runs in
startnet.cmdindependently of the HTA - Alt+Tab to the console window to verify progress
- This was a known issue with VBScript timers — the JavaScript timer fix addresses it
MIT — do whatever you want with it.
PRs welcome. If you've tested this on different hardware or WinPE versions, please open an issue with your findings.