🧪 Offline Boot Testing & Generalization of VHDX OS Images
This guide follows the Disk2vhd imaging and Azure upload process. In networks where the .vhdx
is synced to local hypervisor storage, the image must be tested offline to validate bootability before being generalized and cleaned.
1. 🔌 Boot Test in Local Staging Hypervisor
Objective:
Ensure the .vhdx
image boots in a virtual machine within a completely offline staging environment.
Steps:
- Import the
.vhdx
into your hypervisor (e.g., Hyper-V, Proxmox, VMware Workstation). - Create a test VM:
- Connect the disk as the primary boot volume.
- Ensure no internet/network adapters are connected.
- Power on the VM.
🏢 Special Case: Ridgebrook Client Deployment
For Ridgebrook:
- Images stored in the Azure file share:
- Storage account:
clientosimages01e2usdtc
- File share:
client-os-images-01
- Storage account:
- The image should be synced to the local staging hypervisor:
- Hypervisor name:
ELDERBRAIN
- Local path:
D:\Virtual Hard Disks\client-os-images-01
- Hypervisor name:
- Ridgebrook
.vhdx
images should be placed into a subfolder matching their PSA company name (e.g.,ridgebrook-industrial
).
Once synced, follow the standard boot validation and conversion process below.
2. 🔄 Convert MBR to GPT (If Needed)
If the VM fails to boot, the .vhdx
likely uses MBR instead of GPT.
✅ Option 1: MBR2GPT (Recommended)
Steps:
mbr2gpt /validate /disk:0 /allowFullOS
mbr2gpt /convert /disk:0 /allowFullOS
Make sure the OS volume is the last volume and delete any non-essential partitions (see below).
⚠️ Option 2: gptgen (Advanced / Manual Bootloader Required)
gptgen
is a third-party tool that can convert MBR to GPT when MBR2GPT fails or is unsupported (e.g., on older systems or modified layouts). However, it does not set up the bootloader automatically.
Key Points:
- You must manually recreate the EFI partition.
- You must manually install the UEFI bootloader using
bcdboot
after conversion. - Proceed only if MBR2GPT is not usable.
Always follow up
gptgen
with Step 4: Rebuild BCD Bootloader.
3. 📼 Cleaning Up Partitions With DiskPart
Target Partition Layout (Post-Conversion):
Partition | Purpose | Size |
---|---|---|
1 | EFI System | 10 GB |
2 | Recovery | 10 GB |
3 | OS Volume | Remainder |
Why fixed sizes?
Microsoft has misaligned these in the past — we standardize to 10 GB for EFI and Recovery to avoid risk.
DiskPart Steps:
diskpart
list disk
select disk 0
list partition
- Delete unwanted partitions (OEM, redundant recovery, etc).
- Create EFI:
create partition efi size=10240
format quick fs=fat32 label="System"
assign letter=S
- Create Recovery (optional):
create partition primary size=10240
format quick fs=ntfs label="Recovery"
assign letter=R
4. 🥾 Rebuild BCD Bootloader
bcdboot C:\Windows /s S: /f UEFI
C:
= OS pathS:
= EFI partition/f UEFI
= Force GPT boot
5. 📽 Generalize the OS Image (Post-Boot Cleanup)
Once the VHDX boots successfully in offline staging, perform the following steps to prepare it as a reusable image.
👥 Optional: Backup & Remove User Profiles (for shared deployments)
If this image will be deployed to multiple VMs or physical workstations, it's important to remove all local user accounts and profiles to avoid SID duplication and profile conflicts.
Step 1: Backup Profiles with ProfWiz
Use ForensiT User Profile Wizard (ProfWiz) to safely back up and preserve profiles before deletion.
Step 2: Delete Local User Profiles
wmic useraccount where "name!='Administrator' and name!='DefaultAccount' and name!='Guest' and name!='WDAGUtilityAccount'" delete
for /D %%i in (C:\Users\*) do (
rd /s /q "%%i"
)
⚠️ This permanently deletes all local user accounts and their data, except system accounts.
🔐 Clear Agent Identity & Token Data (Preserve Services)
Tool | Remove From Registry | Clear File System Path |
---|---|---|
NinjaRMM | HKLM\SOFTWARE\NinjaRMM |
C:\ProgramData\NinjaRMMAgent |
Blackpoint | HKLM\SOFTWARE\Blackpoint |
C:\ProgramData\Blackpoint |
Veeam Agent | HKLM\SOFTWARE\Veeam\Veeam Endpoint Backup |
C:\ProgramData\Veeam |
reg delete "HKLM\SOFTWARE\NinjaRMM" /f
reg delete "HKLM\SOFTWARE\Blackpoint" /f
reg delete "HKLM\SOFTWARE\Veeam\Veeam Endpoint Backup" /f
rd /s /q "C:\ProgramData\NinjaRMMAgent"
rd /s /q "C:\ProgramData\Blackpoint"
rd /s /q "C:\ProgramData\Veeam"
❗ Do not delete agent service keys under
HKLM\SYSTEM\CurrentControlSet\Services
.
🥵 Windows Log Cleanup
del /s /q C:\Windows\System32\winevt\Logs\*.*
del /f /q %SystemRoot%\Panther\*.*
6. 💾 Optional: Skip OOBE with unattend.xml
Use an unattend.xml
to skip setup UI and telemetry.
Sample Snippet
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend"
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- ========================== -->
<!-- 1️⃣ OOBE-SYSTEM (runs after specialize) -->
<!-- ========================== -->
<settings pass="oobeSystem">
<component name="Microsoft-Windows-Shell-Setup"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS">
<!-- A. Auto-logon (must be first inside this component) -->
<AutoLogon>
<Username>installadmin</Username>
<Password>
<Value>DTC@dental2025</Value>
<PlainText>true</PlainText>
</Password>
<Enabled>true</Enabled>
<LogonCount>1</LogonCount>
</AutoLogon>
<!-- B. Suppress *all* interactive OOBE pages -->
<OOBE>
<HideEULAPage>true</HideEULAPage>
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
<NetworkLocation>Work</NetworkLocation>
<ProtectYourPC>3</ProtectYourPC>
<SkipMachineOOBE>true</SkipMachineOOBE>
<SkipUserOOBE>true</SkipUserOOBE>
</OOBE>
<!-- C. Regional / time-zone preference -->
<TimeZone>Eastern Standard Time</TimeZone>
<!-- D. Create the local administrator account -->
<UserAccounts>
<LocalAccounts>
<LocalAccount wcm:action="add">
<Name>installadmin</Name>
<Group>Administrators</Group>
<Password>
<Value>DTC@dental2025</Value>
<PlainText>true</PlainText>
</Password>
</LocalAccount>
</LocalAccounts>
</UserAccounts>
</component>
</settings>
<!-- ========================== -->
<!-- 2️⃣ SPECIALIZE (optional tweaks, none that cause “component missing”) -->
<!-- ========================== -->
<settings pass="specialize">
<!-- Disable Application Experience telemetry that does still exist -->
<component name="Microsoft-Windows-ApplicationExperience"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS">
<AITEnable>false</AITEnable>
</component>
<!-- Disable Windows Error Reporting pop-ups -->
<component name="Microsoft-Windows-ErrorReportingCore"
processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35"
language="neutral"
versionScope="nonSxS">
<DisableWerReporting>true</DisableWerReporting>
</component>
</settings>
</unattend>
How to Use
sysprep /generalize /oobe /shutdown /unattend:C:\Windows\System32\Sysprep\unattend.xml
✅ Final Checklist
- Booted offline on hypervisor
- Converted to GPT with valid partitions
- Optional: user profiles backed up and deleted
- Agent identities reset (binaries remain)
-
unattend.xml
disables OOBE + telemetry - Sysprep executed successfully