feat(windows): unattended install — noprompt boot + disk config (M2)
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (push) Successful in 3m25s

VM boot test proved the ISO boots under UEFI+SecureBoot+TPM2 but stopped at the
"press any key" prompt and (post-boot) the disk screen. Enable hands-off install:
- build.ps1: use efisys_noprompt.bin (fall back to efisys.bin) so the ISO boots
  without a keypress.
- autounattend.xml: add GPT/UEFI DiskConfiguration (wipe disk 0 -> EFI/MSR/Win),
  ImageInstall index 1, AcceptEula (eval = no key). Bootstrap local-admin pw is a
  PLACEHOLDER the SKU pipeline must replace.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
sysadmin
2026-06-08 21:55:47 +01:00
parent d26595d26f
commit b4d303cbaa
2 changed files with 47 additions and 19 deletions

View File

@@ -2,12 +2,14 @@
<!--
SilverMetal Enhanced - Windows : Windows 11 IoT Enterprise LTSC answer file.
Purpose: automate OOBE, force a LOCAL account (no Microsoft Account / no cloud
key escrow), set regional defaults, prepare a BitLocker-ready disk layout, and
hand off to $OEM$\SetupComplete.cmd for first-boot hardening.
Automates OOBE, wipes disk 0 to a clean GPT/UEFI layout, installs the LTSC
image (index 1), forces a LOCAL account (no Microsoft Account / no cloud key
escrow), and hands off to C:\Windows\Setup\Scripts\SetupComplete.cmd (run
automatically as SYSTEM at end of setup) for the §A-H hardening.
SCAFFOLD (M0). Disk layout + image selectors are filled at M2 against the
licensed media. Do NOT embed product keys, PINs, or secrets here.
SECURITY NOTE: the bootstrap local-admin password below is a PLACEHOLDER for
unattended setup only. The shippable SKU pipeline MUST inject a per-device
credential (or force change at first logon); never ship this value.
Design: ../../iso-builder.md Controls: ../../hardening-spec.md (domains A, C)
-->
@@ -24,10 +26,35 @@
</component>
<component name="Microsoft-Windows-Setup" processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<!-- TODO-M2: DiskConfiguration (GPT, ESP + MSR + Windows), single GPT disk, BitLocker-ready.
ImageInstall/OSImage/InstallFrom MetaData = IoT Enterprise LTSC index (see inputs.manifest.json). -->
<DiskConfiguration>
<WillShowUI>OnError</WillShowUI>
<Disk wcm:action="add" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
<DiskID>0</DiskID>
<WillWipeDisk>true</WillWipeDisk>
<CreatePartitions>
<CreatePartition wcm:action="add"><Order>1</Order><Type>EFI</Type><Size>300</Size></CreatePartition>
<CreatePartition wcm:action="add"><Order>2</Order><Type>MSR</Type><Size>16</Size></CreatePartition>
<CreatePartition wcm:action="add"><Order>3</Order><Type>Primary</Type><Extend>true</Extend></CreatePartition>
</CreatePartitions>
<ModifyPartitions>
<ModifyPartition wcm:action="add"><Order>1</Order><PartitionID>1</PartitionID><Label>System</Label><Format>FAT32</Format></ModifyPartition>
<ModifyPartition wcm:action="add"><Order>2</Order><PartitionID>2</PartitionID></ModifyPartition>
<ModifyPartition wcm:action="add"><Order>3</Order><PartitionID>3</PartitionID><Label>Windows</Label><Format>NTFS</Format><Letter>C</Letter></ModifyPartition>
</ModifyPartitions>
</Disk>
</DiskConfiguration>
<ImageInstall>
<OSImage>
<InstallTo><DiskID>0</DiskID><PartitionID>3</PartitionID></InstallTo>
<InstallFrom>
<MetaData wcm:action="add" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
<Key>/IMAGE/INDEX</Key><Value>1</Value>
</MetaData>
</InstallFrom>
</OSImage>
</ImageInstall>
<UserData>
<ProductKey><!-- TODO-M2: IoT Enterprise LTSC key, build-time injected; NOT committed --></ProductKey>
<!-- IoT Enterprise LTSC eval media is pre-pidded; no product key required. -->
<AcceptEula>true</AcceptEula>
</UserData>
</component>
@@ -38,28 +65,26 @@
publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<OOBE>
<HideEULAPage>true</HideEULAPage>
<HideOnlineAccountScreens>true</HideOnlineAccountScreens> <!-- force local account -->
<HideOnlineAccountScreens>true</HideOnlineAccountScreens>
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
<ProtectYourPC>3</ProtectYourPC> <!-- disable "send data" express settings -->
<ProtectYourPC>3</ProtectYourPC>
</OOBE>
<UserAccounts>
<!-- TODO-M2: provision a local admin (no MSA). Password set at provisioning, not committed. -->
<LocalAccounts>
<LocalAccount wcm:action="add" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
<Name>silvermetal</Name>
<Group>Administrators</Group>
<DisplayName>SilverMetal</DisplayName>
<Password><Value>SM-setup-CHANGEME-1!</Value><PlainText>true</PlainText></Password>
</LocalAccount>
</LocalAccounts>
</UserAccounts>
<!--
Hardening runs from C:\Windows\Setup\Scripts\SetupComplete.cmd, which
Windows Setup executes automatically (as SYSTEM) at the end of setup,
before first interactive logon. build.ps1 stages it + the hardening/
modules into the image. No FirstLogonCommands needed (avoids a double-run).
-->
<RegisteredOwner>SilverMetal</RegisteredOwner>
<RegisteredOrganization>SilverLABS</RegisteredOrganization>
<!--
Hardening runs from C:\Windows\Setup\Scripts\SetupComplete.cmd, which
Windows Setup executes automatically (as SYSTEM) at the end of setup.
-->
</component>
</settings>

View File

@@ -142,8 +142,11 @@ function Invoke-Brand { Write-Stage 'Stage 5: branding'; Write-Warning ' defer
function Invoke-Repack {
Write-Stage 'Stage 6: repack UEFI-bootable ISO (oscdimg)'
$etfs = Join-Path $isoRoot 'boot\etfsboot.com'
$efi = Join-Path $isoRoot 'efi\microsoft\boot\efisys.bin'
if (-not (Test-Path $efi)) { throw "missing UEFI boot image: $efi" }
# Prefer the no-prompt UEFI boot image so the ISO boots hands-off (no "press
# any key"); fall back to the prompt variant if absent.
$efi = Join-Path $isoRoot 'efi\microsoft\boot\efisys_noprompt.bin'
if (-not (Test-Path $efi)) { $efi = Join-Path $isoRoot 'efi\microsoft\boot\efisys.bin' }
if (-not (Test-Path $efi)) { throw "missing UEFI boot image under efi\microsoft\boot\" }
# Work paths have no spaces (SYSTEM TEMP / runner temp), so omit oscdimg's
# inner quotes around the boot images -- otherwise PowerShell mangles the
# native -bootdata arg into doubled quotes (oscdimg Error 123).