Skip to content

Creating a Golden Image

A golden image is an optimised VM template used to provision VDI session VMs. Building one has two parts: preparing the Windows VM itself (this page), and validating and publishing it as a template through the portal's built-in test workflow (see Validating and Publishing a Golden Image below) — you don't have to manually convert the VM to a template yourself.

Prerequisites

  • Windows 10/11 Pro or Enterprise image available in your location
  • Access to the Domain Controller created above
  • A cloudspace connected to the same network as the Domain Controller

Step 1: Create the Golden Image VM

  1. Create a VM from the Windows 10/11 image.
  2. Name the VM VDI-GoldenImage.
  3. Recommended specs: 4 vCPUs, 8 GB RAM, 120 GB disk.
  4. Attach two network interfaces:
  5. Network 1: Internal (to reach the Domain Controller).
  6. Network 2: Public/external (for RDP access).

Step 2: Configure DNS and Disable Automatic Updates

Open PowerShell as Administrator:

# Disable Windows Update
Set-Service -Name wuauserv -StartupType Disabled
Stop-Service -Name wuauserv -Force

# Set DNS to the Domain Controller's internal IP
Get-NetAdapter | Where-Object {$_.Status -eq "Up"} |
    Set-DnsClientServerAddress -ServerAddresses "10.x.x.x"  # Replace with DC IP

Step 3: Optimise for VDI

# Disable hibernation
powercfg /h off

# Disable Windows Search, Superfetch, and telemetry
Stop-Service -Name "WSearch" -Force; Set-Service -Name "WSearch" -StartupType Disabled
Stop-Service -Name "SysMain" -Force; Set-Service -Name "SysMain" -StartupType Disabled
Stop-Service -Name "DiagTrack" -Force; Set-Service -Name "DiagTrack" -StartupType Disabled

# Enable Remote Desktop
Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' -Name "fDenyTSConnections" -Value 0
Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -Name "UserAuthentication" -Value 1
Enable-NetFirewallRule -DisplayGroup "Remote Desktop"

Step 4: Create the Domain Join Script

Tip

Important: The golden image must not be domain-joined before running Sysprep. Verify this with (Get-WmiObject Win32_ComputerSystem).Domain — it must return WORKGROUP.

Create a domain join script that runs at startup:

New-Item -Path "C:\Scripts" -ItemType Directory -Force

$joinScript = @'
$logFile = "C:\Windows\Temp\domain-join.log"
function Write-Log {
    param($Message)
    "$((Get-Date -Format "yyyy-MM-dd HH:mm:ss")) - $Message" | Out-File -FilePath $logFile -Append -Force
}

try {
    Write-Log "Domain join started"
    # Check if already domain-joined
    if ((Get-WmiObject Win32_ComputerSystem).Domain -ne "WORKGROUP") {
        Unregister-ScheduledTask -TaskName "VDI-DomainJoin" -Confirm:$false -ErrorAction SilentlyContinue
        exit 0
    }
    # Wait for network
    Start-Sleep -Seconds 60
    # Test DNS
    Resolve-DnsName corp.local -ErrorAction Stop | Out-Null
    # Join domain
    $password = ConvertTo-SecureString "YourAdminPassword123!" -AsPlainText -Force
    $cred = New-Object PSCredential("CORP\Administrator", $password)
    Add-Computer -DomainName corp.local -Credential $cred -Force
    Unregister-ScheduledTask -TaskName "VDI-DomainJoin" -Confirm:$false -ErrorAction SilentlyContinue
    Restart-Computer -Force
} catch {
    Write-Log "ERROR: $($_.Exception.Message)"
    exit 1
}
'@

Set-Content -Path "C:\Scripts\JoinDomain.ps1" -Value $joinScript

$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-ExecutionPolicy Bypass -WindowStyle Hidden -File C:\Scripts\JoinDomain.ps1"
$trigger = New-ScheduledTaskTrigger -AtStartup
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -ExecutionTimeLimit (New-TimeSpan -Minutes 10)

Register-ScheduledTask -TaskName "VDI-DomainJoin" -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Description "Automatic domain join for VDI"

Replace corp.local and YourAdminPassword123! with your actual domain and administrator password.

How it works

This scheduled task runs at every system startup as SYSTEM (no user login required). The script checks whether the machine is already domain-joined, waits for the network to come up, joins the domain, then unregisters itself so it doesn't run again. If a step fails (for example, DNS isn't ready yet), it exits and automatically retries on the next boot. All activity is logged to C:\Windows\Temp\domain-join.log for troubleshooting. This is what lets every VM created from the template join the domain and pick up its final hostname automatically, without manual setup per VM.

Step 5: Create an Unattend.xml for Sysprep

Create C:\Windows\System32\Sysprep\unattend.xml to skip OOBE screens:

<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
    <settings pass="specialize">
        <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64"
            publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
            <TimeZone>UTC</TimeZone>
        </component>
    </settings>
    <settings pass="oobeSystem">
        <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64"
            publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
            <InputLocale>en-US</InputLocale>
            <SystemLocale>en-US</SystemLocale>
            <UILanguage>en-US</UILanguage>
            <UserLocale>en-US</UserLocale>
        </component>
        <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64"
            publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
            <OOBE>
                <HideEULAPage>true</HideEULAPage>
                <HideOnlineAccountScreens>true</HideOnlineAccountScreens>
                <SkipUserOOBE>true</SkipUserOOBE>
                <SkipMachineOOBE>true</SkipMachineOOBE>
            </OOBE>
        </component>
    </settings>
</unattend>

Step 6: Clean Up and Create a Snapshot

# Sync filesystem
Write-VolumeCache C

# Remove temp files and event logs
Remove-Item "$env:TEMP\*" -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item "C:\Windows\Temp\*" -Recurse -Force -ErrorAction SilentlyContinue
wevtutil el | ForEach-Object { wevtutil cl $_ 2>$null }

# Remove Windows Store apps (can cause Sysprep issues)
Get-AppxPackage -AllUsers | Remove-AppxPackage -ErrorAction SilentlyContinue

# Disable BitLocker
Disable-BitLocker -MountPoint "C:" -ErrorAction SilentlyContinue

In the portal:

  1. Navigate to the VDI-GoldenImage VM.
  2. Select the Disks tab, then open the boot disk's details page.
  3. Click TAKE SNAPSHOT.
  4. Name it pre-sysprep-snapshot.
  5. Click Create.

Note

This snapshot lets you roll back to this exact state for future updates or troubleshooting. You can create new template versions later by reverting to this snapshot, making your updates, and running Sysprep again.

Step 7: Run Sysprep

Run your validation checklist first — after Sysprep, some checks (like BitLocker state) may be reset or the image may no longer boot to verify them:

Check Command / Method Expected Result
Domain membership (Get-WmiObject Win32_ComputerSystem).Domain WORKGROUP
QEMU Guest Agent running Get-Service qemu-ga Status: Running
BitLocker disabled Get-BitLockerVolume C: Protection: Off
Local administrator enabled net user Administrator Account active
Remote Desktop enabled (Get-ItemProperty 'HKLM:\System\CurrentControlSet\Control\Terminal Server').fDenyTSConnections 0
Display resolution Manually verify at least 1024×768 Resolvable

Then run Sysprep:

C:\Windows\System32\Sysprep\sysprep.exe /oobe /generalize /shutdown /mode:vm

The VM will shut down automatically. Do not boot it again — you'll test it as-is via a test VDI profile in the next section, which can roll back to a pre-Sysprep snapshot if something goes wrong.

Validating and Publishing a Golden Image

Rather than manually converting the VM to a template and hoping it works, the portal has a dedicated test-and-publish workflow: it spins up real VDI sessions against your actual prepared VM, and only turns it into a reusable template once you're satisfied.

Create a Test VDI Profile

  1. Navigate to VDI and click Validate VDI Image, next to Add VDI Profile. This is a separate, five-step wizard.
  2. Select Cloudspace — the cloudspace containing your prepared VM.
  3. Select Virtual machine — the VM you just prepared. A snapshot of its boot disk is used as the base image for test sessions.
  4. Select Rollback Snapshot — normally the most recent snapshot of the boot disk, taken before you ran Sysprep. If the test doesn't go well, you can roll back to this exact state.
  5. Connection Mode — configured the same way as a regular profile.
  6. Configuration — enter a name, select the service account and user role, and submit.

This creates a test VDI profile, distinct from a regular one — its sessions run directly against your prepared VM rather than a pool of provisioned copies.

Test It

Share the test profile's landing page and request a session, exactly as you would for a real profile. Confirm domain login, desktop performance, and anything else your checklist covers.

If something is wrong, use Rollback test VM on the profile's details page: it stops the VM, rolls its boot disk back to the rollback snapshot you selected, and restarts it — so you can fix the issue and test again without starting from scratch.

Publish the Golden Image

Once you're satisfied:

  1. On the test profile's details page, click Publish golden image from test VM.
  2. Select snapshot — choose the snapshot to publish from (normally the post-Sysprep state).
  3. Enter a template name (e.g. VDI-Win11-Template-v1).
  4. Submit. The portal creates a VM image from that snapshot and publishes it as a template — this can take a few minutes, with progress shown on screen.

Your golden image template is now ready to use when creating regular VDI profiles.

Troubleshooting

Domain join fails — trust relationship error:

Cause: The golden image was domain-joined before Sysprep.

Fix: Use Rollback test VM to return to the pre-Sysprep snapshot, remove the VM from the domain, and re-run Sysprep:

Remove-Computer -WorkgroupName "WORKGROUP" -Force -Restart

Sysprep fails — BitLocker enabled:

Disable-BitLocker -MountPoint "C:"

Sysprep fails — Windows Store apps present:

Get-AppxPackage -AllUsers | Remove-AppxPackage -ErrorAction SilentlyContinue