Templates (Golden Image Creation) with automatic Entra Domain Enrollment

Overview

This guide explains how to create a Windows template in Softdrive that can automatically join a device to Microsoft Entra ID (and optionally enroll in Intune) on first boot. The approach uses:

  • A provisioning package (.ppkg) to automate enrollment
  • An Apply-PPKG.ps1 script to apply the package during setup
  • An unattend.xml file to automate OOBE and run the script

Prerequisites

  • Access to the Softdrive Dashboard (Softnet).
  • A PPKG for automated domain enrollment ready. More info
  • Signed in as a Local Administrator on the Softdrive Windows machine.
  • Windows is fully updated.
  • The machine is not already joined to a Domain, Intune, or Entra ID.
  • All required applications are installed before starting.
  • If applicable, confirm required drivers/tools are installed:

Index

  1. Disable BitLocker
  2. Remove provisioned Appx packages
  3. Create the Apply-PPKG PowerShell script
  4. Create the unattend.xml file
  5. Create the C:\Setup folder and place required files
  6. Run Sysprep with unattend.xml
  7. Save the machine as a Softdrive template
  8. Common mistakes and fixes

Content

1) Disable BitLocker


  • Open an elevated PowerShell or Command Promptand run:
    manage-bde -status
  • If BitLocker is ON for C:, turn it off:
    manage-bde -off C:
  • Wait until full decryption completes.Check progress with:
    manage-bde -status
    Do not run Sysprep until decryption is 100% complete.




Clean up the system (Optional but recommended)

Open Command Prompt and run:

cleanmgr /sagerun:1
  • Remove unused user accounts and files.
  • Optionally, run third-party cleanup utilities.

2) Remove provisioned Appx packages

Some Appx packages can cause Sysprep failures if they exist for a user but are not properly provisioned system-wide. The example below removes one commonly problematic package.

Run this in PowerShell as Administrator:

# Remove from current user:
Get-AppxPackage -Name Microsoft.WidgetsPlatformRuntime | Remove-AppxPackage -ErrorAction SilentlyContinue

# Remove provisioned copy (if any):
Get-AppxProvisionedPackage -Online |
  Where-Object {$_.DisplayName -like "*Microsoft.WidgetsPlatformRuntime*"} |
  ForEach-Object { Remove-AppxProvisionedPackage -Online -PackageName $_.PackageName }

Retry Sysprep afterward.


3) Create the Apply-PPKG PowerShell script

This script runs during setup and applies the provisioning package (.ppkg). It also writes a log file so you can confirm it ran successfully.

# Create Apply-PPKG.ps1
$pkg = "C:\Setup\enroll.ppkg"
$log = "C:\Setup\Apply-PPKG.log"

"[$(Get-Date -Format s)] Starting PPKG apply. Looking for $pkg" | Out-File $log -Encoding utf8

if (Test-Path $pkg) {
    try {
        Install-ProvisioningPackage -PackagePath $pkg -ForceInstall -QuietInstall -LogsDirectory "C:\Setup"
        "[$(Get-Date -Format s)] PPKG installed successfully." | Out-File $log -Append -Encoding utf8

        # Optional: trigger Intune Management Extension sync if present
        schtasks /run /tn "Microsoft\IntuneManagementExtension\PushLaunch" 2>$null | Out-Null
    }
    catch {
        "[$(Get-Date -Format s)] ERROR applying PPKG: $($_.Exception.Message)" | Out-File $log -Append -Encoding utf8
        exit 1
    }
}
else {
    "[$(Get-Date -Format s)] PPKG not found at path." | Out-File $log -Append -Encoding utf8
    exit 2
}

4) Create the unattend.xml file

In the specialize pass, the unattend file runs Apply-PPKG.ps1. In the oobeSystem pass, it sets OOBE options so the initial setup experience can be minimized or bypassed.

<?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">

  <!-- SPECIALIZE -->
  <settings pass="specialize">
    <component name="Microsoft-Windows-Deployment"
               processorArchitecture="amd64"
               publicKeyToken="31bf3856ad364e35"
               language="neutral"
               versionScope="nonSxS">
      <RunSynchronous>
        <RunSynchronousCommand wcm:action="add">
          <Order>1</Order>
          <Description>Apply Enroll PPKG</Description>
          <Path>powershell.exe -ExecutionPolicy Bypass -File "C:\Setup\Apply-PPKG.ps1"</Path>
        </RunSynchronousCommand>
      </RunSynchronous>
    </component>

    <component name="Microsoft-Windows-Shell-Setup"
               processorArchitecture="amd64"
               publicKeyToken="31bf3856ad364e35"
               language="neutral"
               versionScope="nonSxS">
      <ComputerName>*</ComputerName>
      <RegisteredOwner>Softdrive</RegisteredOwner>
      <RegisteredOrganization>Softdrive</RegisteredOrganization>
      <TimeZone>SA Western Standard Time</TimeZone>
    </component>
  </settings>

  <!-- OOBE SYSTEM -->
  <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>
        <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
        <HideOnlineAccountScreens>true</HideOnlineAccountScreens>
        <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
        <NetworkLocation>Work</NetworkLocation>
        <ProtectYourPC>1</ProtectYourPC>
        <SkipMachineOOBE>true</SkipMachineOOBE>
        <SkipUserOOBE>true</SkipUserOOBE>
      </OOBE>
      <RegisteredOwner>Softdrive</RegisteredOwner>
      <RegisteredOrganization>Softdrive</RegisteredOrganization>
    </component>
  </settings>

</unattend>

Customizing the unattend.xml (Optional)

The provided unattend.xml is a baseline example designed to support automated provisioning, Entra ID enrollment, and OOBE bypass.

End users and administrators are free to customize this file to meet their organization’s requirements. Common customizations include:

  • Changing the time zone
  • Setting a custom computer naming convention
  • Adjusting regional and language settings
  • Enabling or disabling specific OOBE options
  • Adding additional RunSynchronous commands

If changes are made, ensure the file remains valid XML and is tested on a non-production template before deployment.


5) Create the C:\Setup folder and move files there

On the Softdrive VM that will be used as a template, create a folder named C:\Setup and copy the following files into it:

  • enroll.ppkg (created using this guide)
  • Apply-PPKG.ps1 (created in Step 3)
  • unattend.xml (created in Step 4)
C:\
└── Setup\
    ├── enroll.ppkg
    ├── Apply-PPKG.ps1
    └── unattend.xml

6) Run Sysprep via CMD (with unattend.xml)

Run the following command from an elevated Command Prompt. This will generalize the image, start OOBE on next boot, and shut down the machine when complete.

C:\Windows\System32\Sysprep\sysprep.exe /generalize /oobe /shutdown /unattend:C:\Setup\unattend.xml

After Sysprep completes, the VM will shut down. Do not boot it again until you are ready to save it as a template in Softnet.


7) Save the machine as a template


  • Go to the Softnet Dashboard.
  • Navigate to Computers and find the machine that was sysprepped.



  • Click the computer and select "Save as Template".



This notifies Softdrive to complete the template creation process. Please do not power the machine on until you receive a confirmation email from support@softdrive.co.


First boot after Sysprep

  • On next boot, Windows should show the Entra/organization sign-in screen (depending on your configuration).
  • The provisioning package should apply during setup, and the device should join the tenant automatically.

Additional Notes

  • If your environment requires encryption, enable BitLocker after the template is created.

8) Common mistakes and fixes

Sysprep fails immediately

  • Common cause: Provisioned Appx packages or Store apps blocking Sysprep.
  • Fix: Remove the problematic Appx package(s) and retry. (See Step 2.)

BitLocker still enabled or decryption not finished

  • Common cause: Sysprep is run while disk decryption is still in progress.
  • Fix: Confirm manage-bde -status shows decryption at 100% before running Sysprep.

PPKG does not apply on first boot

  • Common cause: Incorrect path or missing files in C:\Setup.
  • Fix: Verify C:\Setup\enroll.ppkg, Apply-PPKG.ps1, and unattend.xml exist before Sysprep.
  • Tip: Check the log file C:\Setup\Apply-PPKG.log after first boot.

Machine boots and asks for normal OOBE setup

  • Common cause: unattend.xml is not being used (wrong command) or XML is malformed.
  • Fix: Confirm the Sysprep command includes /unattend:C:\Setup\unattend.xml and validate the XML structure.

Template saved, but someone powered it on too early

  • Common cause: The sysprepped VM is booted before Softdrive completes template processing.
  • Fix: Avoid powering on the VM until you receive the confirmation email from Support.