# Build a pre-installed Windows qcow2 image using QEMU unattended install. # Boots into Audit Mode after install and shuts down. # Apply templates via customizeImageFold to install software (auditScript) # and customize registry (windowsRegistry), then generalize when done. { pkgs, lib, drivers, makeWinISO, makeAuditModeAutounattend, ... }: { name ? "windows", upstreamISO, productKey ? "", imageIndex ? 1, diskSize ? "64G", bypassRequirements ? false, locale ? "en-US", efi ? true, smp ? 4, memSize ? 4096, vncDisplay ? null, # e.g. ":10" to enable VNC on port 5910 for monitoring windowsVersionForVirtioDrivers ? "w10", # "w10", "w11", "2k22", "2k19", etc. }: let AutounattendedXml = makeAuditModeAutounattend { inherit locale productKey imageIndex efi bypassRequirements windowsVersionForVirtioDrivers; diskIndex = 0; virtioDriverLetter = "E"; }; iso = makeWinISO { iso = upstreamISO; inherit AutounattendedXml; }; displayArg = if vncDisplay != null then "-vnc ${vncDisplay}" else null; drv = pkgs.runCommand "${name}-vmix.qcow2" { __noChroot = true; requiredSystemFeatures = [ "kvm" ]; nativeBuildInputs = with pkgs; [ qemu ]; } '' echo "=== vmix: creating ${diskSize} disk ===" qemu-img create -f qcow2 disk.qcow2 ${diskSize} cp ${pkgs.OVMF.fd}/FV/OVMF_VARS.fd vars.fd chmod +w vars.fd echo "=== vmix: installing ${name} (unattended, 5-10 min) ===" VMIX_DISPLAY="-nographic" ${lib.optionalString (displayArg != null) ''VMIX_DISPLAY="${displayArg}"''} ${lib.optionalString (displayArg == null) '' VMIX_DF=$(ls -t /tmp/.vmix-display-* 2>/dev/null | head -1) if [ -n "$VMIX_DF" ]; then export DISPLAY=$(cat "$VMIX_DF") export HOME=$(mktemp -d) export XDG_RUNTIME_DIR=$HOME export SDL_VIDEODRIVER=x11 VMIX_DISPLAY="-display sdl" fi ''} # Windows installs unattended, reboots into Audit Mode, # deletes cached Autounattend, shuts down → QEMU exits. QEMU_ARGS="-accel kvm -m ${toString memSize} -smp ${toString smp} -cpu host -machine type=q35 \ -drive if=pflash,format=raw,readonly=on,file=${pkgs.OVMF.fd}/FV/OVMF_CODE.fd \ -drive if=pflash,format=raw,file=vars.fd \ -rtc base=localtime,clock=host \ -device qemu-xhci -device usb-tablet \ -drive file=disk.qcow2,format=qcow2,if=virtio \ -drive file=${iso},media=cdrom,readonly=on \ -drive file=${drivers.virtio-iso},media=cdrom,readonly=on \ -nic user,model=virtio-net-pci" timeout 3600 qemu-system-x86_64 $VMIX_DISPLAY $QEMU_ARGS || \ if [[ "$VMIX_DISPLAY" == "-display sdl" ]]; then echo "=== vmix: SDL failed, retrying headless ===" cp ${pkgs.OVMF.fd}/FV/OVMF_VARS.fd vars.fd && chmod +w vars.fd timeout 3600 qemu-system-x86_64 -nographic $QEMU_ARGS else exit 1 fi echo "=== vmix: ${name} install complete ===" mv disk.qcow2 $out ''; in drv // { _vmixOsType = "windows"; }