aboutsummaryrefslogtreecommitdiff

win11-qemu-install

Mostly-unattended install of Windows 11 onto a disk image under QEMU/KVM. The script generates an Autounattend.xml, hands it to Windows Setup on a virtual floppy, and runs QEMU against the install ISO and a target disk created beforehand with qemu-img. The answer file presets the en-US locale, bypasses the TPM 2.0, Secure Boot, and minimum RAM checks via LabConfig, creates one local administrator, and auto-logs in once. The pages it leaves alone (EULA, edition, target disk) are clicked once over VNC.

Whatever disk is picked on Setup's disk page is wiped.

Requirements

  • qemu-system-x86_64 with KVM and seccomp sandbox support
  • a POSIX shell, sed, id, and mktemp
  • a Windows 11 install ISO and a disk image made with qemu-img
  • a VNC client such as remote-viewer for the Setup pages
  • optionally the VirtIO driver ISO

Run as an unprivileged user with access to /dev/kvm. The script refuses to run as root.

Usage

Create a disk, then run the install:

qemu-img create -f qcow2 win11.qcow2 64G
./win11-qemu-install win11.iso win11.qcow2

QEMU runs in the foreground. Connect a VNC client and click through the pages the answer file leaves alone:

remote-viewer vnc://127.0.0.1:5900

A third argument names the VirtIO driver ISO. The install disk is then attached over VirtIO, which is faster:

./win11-qemu-install win11.iso win11.qcow2 virtio-win.iso

Setup shows an empty disk list until viostor is loaded by hand:

Load driver -> virtio CD -> viostor -> w11 -> amd64

Without it the disk is attached over SATA/AHCI and Setup sees it with no extra drivers. The same driver ISO covers network and balloon after the install.

Configuration

Environment variables, all optional.

  • RAM_MB guest memory in MB during install (default 8192)
  • SMP guest CPU count during install (default 8)
  • IMAGE_FORMAT disk image format: qcow2 or raw (default qcow2)
  • LOCAL_USER administrator account name (default user)
  • LOCAL_PASS administrator account password (default 123456)

Example:

RAM_MB=16384 SMP=4 LOCAL_USER=admin LOCAL_PASS=hunter2 \
    ./win11-qemu-install win11.iso win11.qcow2

Networking

The installer runs with -nic none. With no way to reach Microsoft, OOBE settles for a local account instead of forcing an online one, and restarting inside Setup changes nothing. Networking arrives when the installed image is booted afterwards, with a NIC model Windows has an in-box driver for:

qemu-system-x86_64 -enable-kvm -machine q35 -cpu host -m 8192 \
    -drive file=win11.qcow2,if=ide \
    -nic user,model=e1000e \
    -vnc 127.0.0.1:0 -usb -device usb-tablet

Use if=virtio for the drive if the install ran in VirtIO mode. virtio-net stays dead until NetKVM is installed from the driver ISO; e1000e works out of the box.

Quirks

The windowsPE pass is minimal on purpose. Automating the EULA, edition, and disk pages with UserData, DiskConfiguration, and DriverPaths makes the Windows 11 24H2 setup engine abort right after boot ("Windows installation encountered an unexpected error", extend code 0x40031), so those pages are left to the VNC session.

The answer file is written to a per-run mktemp directory (mode 0700, under TMPDIR), reaches the guest as fat:floppy:, and is removed by an EXIT trap. SIGKILL and host failure cannot run the trap; remove stale win11-qemu-install.* directories from TMPDIR after either.

QEMU runs as a background command so HUP, INT, and TERM stop it and then clean up, exiting 129, 130, or 143. In the foreground the trap would not run until QEMU had already exited.

LOCAL_USER and LOCAL_PASS are XML-escaped, so values containing & < > " ' are safe.

Debugging

Watch the install over VNC. While QEMU runs the generated answer file can be read at win11-qemu-install.*/Autounattend.xml under TMPDIR.

  • "must not contain a comma": QEMU separates drive options with commas; use a path without one.
  • Black VNC screen: Setup is still booting.
  • Empty disk list in Setup: load the VirtIO driver as shown above, or rerun without the VirtIO ISO for the SATA/AHCI path.
  • "installation encountered an unexpected error" right after boot: the answer file automates more of windowsPE than the setup engine accepts.
  • KVM errors on start: check that /dev/kvm exists and is accessible.

Security

The default password is 123456 and the account is a local administrator, so this is for throwaway lab VMs. Set LOCAL_PASS for anything kept. VNC has no authentication and any local host user can reach the guest console; it is bound to 127.0.0.1, and the QEMU monitor, serial port, and guest network are not exposed.

The answer file holds the password in plain text in the 0700 temp dir. QEMU refuses to run as root and runs under its seccomp sandbox with privilege elevation, process spawning, obsolete system calls, and resource-control changes denied, but it still has the invoking user's file access. Use a dedicated unprivileged account when installing untrusted media.