Generalize the base image from audit mode, with hibernation off

The answer file now boots into audit mode after Setup instead of logging on
as the built-in Administrator with a password: no account or credentials in
the answer file, and no copy of one left in C:\Windows\Panther. The auditUser
commands disable the built-in Administrator, turn hibernation off (no
hiberfil.sys in the image), remove cached answer files and setup logs, and run
sysprep /generalize.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
GuofangandClaude Sonnet 5.5 committed 2026-11-02 12:51:54 +08:00
1 parent cb01d2c120
commit 9c5abac9bf
3 files changed
+36 -49

No files matched your search

+3 -3
View File
@@ -117,9 +117,9 @@ does not run Windows Setup again. `build-windows-base.sh` makes that base image
(by default at the path `./download-images.sh --print-path windows11` prints): it
installs Windows unattended from the ISO in `install-iso/` (it does everything
itself: answer file in `windows11/seed/`, QEMU hardware in `windows11/qemu/`; this
directory has no dependency on any other repository). The answer file logs on once
with the built-in Administrator, which it disables again, and runs
`sysprep /generalize` as its last step; the script waits for the guest to power
directory has no dependency on any other repository). The answer file boots
into audit mode (no account and no credentials are needed) and runs
`sysprep /generalize` there as its last step; the script waits for the guest to power
off and stores the disk read-only. The image is clean and generic, like a cloud
image: nothing but Windows, with no account, no SSH, no settings, no VirtIO or
other hardware-specific drivers and no machine identity. Each VM is configured
+4 -3
View File
@@ -6,13 +6,14 @@
# made once, and this script needs nothing outside this directory:
# 1. an answer ISO is built from windows11/seed/Autounattend.xml;
# 2. QEMU installs Windows unattended from the ISO into a scratch directory.
# After the first logon, the answer file runs sysprep /generalize, which
# removes the machine identity and shuts the guest down;
# The answer file then boots into audit mode, where it runs sysprep
# /generalize, which removes the machine identity and shuts the guest down;
# 3. the shut-down disk is converted into the output file and made read-only.
# The image holds nothing but Windows itself, like a cloud image: no VirtIO or
# other hardware-specific drivers (the installation hardware, windows11/qemu,
# only uses devices with inbox Windows drivers), no machine identity, no user
# account (the built-in Administrator is disabled), no SSH, no settings. Every
# account (the answer file holds no credentials either: audit mode needs no
# account; the built-in Administrator is disabled), no SSH, no settings. Every
# VM is configured on its first boot: Windows looks for an Unattend.xml in the
# root of an attached CD-ROM during its specialize and OOBE passes, and so
# takes the host name, account, auto logon, SSH and so on from a seed ISO.
+29 -43
View File
@@ -96,59 +96,45 @@
</RunSynchronous>
</component>
</settings>
<!-- After Setup, boot into audit mode instead of OOBE: Windows logs on as the
built-in Administrator (no password, no account is created) and runs the
auditUser commands below, the last of which generalizes the image and powers
off. So the answer file holds no credentials. -->
<settings pass="oobeSystem">
<component name="Microsoft-Windows-International-Core" processorArchitecture="amd64"
<component name="Microsoft-Windows-Deployment" processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<InputLocale>en-US</InputLocale>
<SystemLocale>en-US</SystemLocale>
<UILanguage>en-US</UILanguage>
<UserLocale>en-US</UserLocale>
<Reseal>
<Mode>Audit</Mode>
</Reseal>
</component>
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64"
</settings>
<settings pass="auditUser">
<component name="Microsoft-Windows-Deployment" processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
<OOBE>
<HideEULAPage>true</HideEULAPage>
<HideOnlineAccountScreens>true</HideOnlineAccountScreens>
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
<ProtectYourPC>3</ProtectYourPC>
</OOBE>
<!-- The built-in Administrator only exists to log on once and run sysprep; it
is disabled again before that, and the password is a throwaway. -->
<UserAccounts>
<AdministratorPassword>
<Value>Build-Only-1</Value>
<PlainText>true</PlainText>
</AdministratorPassword>
</UserAccounts>
<AutoLogon>
<Enabled>true</Enabled>
<LogonCount>1</LogonCount>
<Username>Administrator</Username>
<Password>
<Value>Build-Only-1</Value>
<PlainText>true</PlainText>
</Password>
</AutoLogon>
<FirstLogonCommands>
<SynchronousCommand wcm:action="add">
<RunSynchronous>
<RunSynchronousCommand wcm:action="add">
<Order>1</Order>
<Description>Disable the built-in Administrator again</Description>
<CommandLine>net.exe user Administrator /active:no</CommandLine>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Path>net.exe user Administrator /active:no</Path>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>2</Order>
<Description>Remove the cached copies of this answer file (Windows would read
them before the Unattend.xml on a seed ISO, and one has the build password)
and the setup logs</Description>
<CommandLine>cmd.exe /c "del /f /q C:\Windows\Panther\unattend*.xml C:\Windows\Panther\Unattend\unattend*.xml C:\Windows\Panther\*.log &amp; rd /s /q C:\Windows\Panther\UnattendGC &amp; exit /b 0"</CommandLine>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Description>Turn hibernation off, which deletes hiberfil.sys (3 GB) from the image</Description>
<Path>powercfg.exe /hibernate off</Path>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>3</Order>
<Description>Remove the cached copies of this answer file (Windows would read
them before the Unattend.xml on a seed ISO) and the setup logs</Description>
<Path>cmd.exe /c "del /f /q C:\Windows\Panther\unattend*.xml C:\Windows\Panther\Unattend\unattend*.xml C:\Windows\Panther\*.log &amp; rd /s /q C:\Windows\Panther\UnattendGC &amp; exit /b 0"</Path>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>4</Order>
<Description>Generalize the image and power off</Description>
<CommandLine>C:\Windows\System32\Sysprep\sysprep.exe /generalize /oobe /shutdown /quiet</CommandLine>
</SynchronousCommand>
</FirstLogonCommands>
<Path>C:\Windows\System32\Sysprep\sysprep.exe /generalize /oobe /shutdown /quiet</Path>
</RunSynchronousCommand>
</RunSynchronous>
</component>
</settings>
</unattend>