Templates/unattend.xml

<?xml version="1.0" encoding="utf-8"?>
<!--
  THE ANSWER FILE A NEW TASK SEQUENCE GETS, and MDT's Unattend_x64.xml in the
  same role: its New Task Sequence wizard copies a template answer file into the
  new sequence's folder, because a sequence whose Apply Windows Settings step
  names a file nobody supplied is a sequence that fails at the machine.
 
  IT IS THE SPIKES S7 DOCUMENT, TOKENISED. This is what was actually deployed to
  a Windows 11 Enterprise LTSC VM in SPIKES.md S7 - captured from the machine, not
  written from memory. S7 observed, on the machine it built:
 
    ComputerName applied from the specialize pass
    OOBE fully skipped
    the built-in Administrator enabled
    AutoLogon armed, with the password stored as an LSA SECRET and
      DefaultPassword absent from the registry
 
  THE TOKENS ARE NAMED HERE WITHOUT THEIR PER CENT SIGNS, on purpose: expansion
  runs over the WHOLE document, comments included, so a comment written with the
  token in it would put the machine's local Administrator password into a
  comment in the deployed answer file. The 'mints one password for a run'
  assertion counts occurrences, and it is what caught exactly that.
 
  HDTAdminPassword APPEARS TWICE BELOW - once under UserAccounts and once inside
  AutoLogon - because Setup reads them separately.
 
  HDTFullName AND HDTOrgName ARE WHAT THE WIZARD ASKS FOR on MDT's OS Settings
  page. A sequence that sets neither leaves the tokens unexpanded, which Windows
  accepts as literal text in those two fields - so the wizard writing them is
  what makes them meaningful, and leaving them out of the wizard is not a
  failure, only a machine registered to nobody in particular.
 
  New-HDTDeploymentPassword's alphabet excludes < > & " ' and %, on purpose, so
  a minted password substitutes into this document without escaping.
-->
<unattend xmlns="urn:schemas-microsoft-com:unattend">
  <settings pass="specialize">
    <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64"
               publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
               xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
      <ComputerName>%HDTComputerName%</ComputerName>
      <RegisteredOwner>%HDTFullName%</RegisteredOwner>
      <RegisteredOrganization>%HDTOrgName%</RegisteredOrganization>
 
      <!--
        AND THE ELEMENT THAT DISAPPEARS WHEN NOBODY SETS IT. MDT's ProductKey.
        Windows reads this in the specialize pass, and an EMPTY element fails
        the pass - so a token nobody supplied cannot simply be left here.
        ApplyUnattend removes the whole element when HDTProductKey resolves to
        nothing, which is exactly how this template behaved before the line
        existed and how a KMS or LTSC build has to keep behaving.
 
        A TEMPLATE MAY ALSO HARD-CODE A KEY HERE and never mention the
        variable. That is left alone: only the element still holding the
        unresolved token is removed.
      -->
      <ProductKey>%HDTProductKey%</ProductKey>
 
      <!--
        WITHOUT THIS, THE LOCALE DECIDES, AND en-US MEANS PACIFIC. Microsoft's
        rule is that an unspecified TimeZone is derived from the installed
        language and region: this document sets UserLocale en-US below, which
        gives every machine Pacific Standard Time. MDT has TimeZoneName in
        CustomSettings.ini for exactly this reason.
 
        THE VALUE COMES FROM THE BOOT IMAGE unless a rule says otherwise.
        Start-HDTDeployment seeds HDTTimeZone from the time zone the Windows PE
        window chose, so one answer covers WinPE's clock and the deployed
        machine's; rules.yaml overrides it per machine through the ordinary
        precedence.
      -->
      <TimeZone>%HDTTimeZone%</TimeZone>
    </component>
  </settings>
 
  <settings pass="oobeSystem">
    <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64"
               publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
               xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
      <!--
        LANGUAGE AND REGION, AND NOT en-US BECAUSE NOBODY DECIDED IT. These four
        were literals here, so every machine this template deployed came up with
        a US keyboard, a US system locale and a US display language whatever the
        site was - changeable only by hand-editing the XML of every task
        sequence.
 
        THEY ARE VARIABLES, WHICH IS THE ANSWER MDT GAVE: UILanguage, UserLocale
        and KeyboardLocale are CustomSettings.ini entries there, so one rule sets
        them for a site, a model or a subnet. InputLocale IS MDT's
        KeyboardLocale - the same setting under two names, so HDT keeps one.
 
        Start-HDTDeployment seeds all four when nothing else resolved them, so a
        share that never mentions them deploys exactly as it did before.
      -->
      <InputLocale>%HDTKeyboardLocale%</InputLocale>
      <SystemLocale>%HDTSystemLocale%</SystemLocale>
      <UILanguage>%HDTUILanguage%</UILanguage>
      <UserLocale>%HDTUserLocale%</UserLocale>
    </component>
 
    <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64"
               publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
               xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
      <OOBE>
        <HideEULAPage>true</HideEULAPage>
        <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
        <HideOnlineAccountScreens>true</HideOnlineAccountScreens>
        <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
        <NetworkLocation>Work</NetworkLocation>
        <ProtectYourPC>3</ProtectYourPC>
      </OOBE>
 
      <UserAccounts>
        <AdministratorPassword>
          <Value>%HDTAdminPassword%</Value>
          <PlainText>true</PlainText>
        </AdministratorPassword>
      </UserAccounts>
 
      <!-- DESIGN 4.5.1: the oobeSystem AutoLogon block is what arms the FIRST
           logon of the deployed machine. Every leg after that is armed by
           Set-HDTAutoLogon against the machine's own registry and LSA store. -->
      <AutoLogon>
        <Enabled>true</Enabled>
        <LogonCount>999</LogonCount>
        <Username>Administrator</Username>
        <Password>
          <Value>%HDTAdminPassword%</Value>
          <PlainText>true</PlainText>
        </Password>
      </AutoLogon>
 
      <!--
        AND WHAT THE FIRST LOGON RUNS. The AutoLogon block above logs the
        machine in; on its own that is a machine that logs itself in and sits
        at a desktop, which is exactly what every deployment did before this
        line existed - the whole State Restore group silently skipped while
        the run reported success.
 
        THIS IS MDT'S HOOK, NOT A NEW IDEA. LiteTouch continues through a
        FirstLogonCommands entry running LTIBootstrap.vbs, for the reason it
        matters here too: the answer file is the only thing that survives the
        transition from WinPE to a Windows that did not exist yet. A registry
        key written in WinPE is written on a RAM disk.
 
        C:\HDT, NOT THE LETTER WinPE USED. Copy-HDTResumeAgent staged this
        folder onto the OS volume, which had whatever letter the partition step
        published; the machine reading this line has booted, and that volume is
        C:. Later legs are launched by the RunOnce entry Set-HDTAutoLogon
        writes, whose command line is this same one - one spelling of "start
        the engine", asserted by tests/unit/SequenceTemplate.Tests.ps1.
      -->
      <FirstLogonCommands>
        <SynchronousCommand wcm:action="add">
          <Order>1</Order>
          <CommandLine>powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\HDT\Start-HDTResume.ps1</CommandLine>
          <Description>Hephaestus - resume the task sequence</Description>
          <RequiresUserInput>false</RequiresUserInput>
        </SynchronousCommand>
      </FirstLogonCommands>
    </component>
  </settings>
</unattend>