A disk-mode overlay defaults to C:\uwfswap.sys -- on the very volume being
protected, which is the opposite of the point. uwfmgr grew a create-swapfile
subcommand for exactly this, and it only accepts the call while the filter is
off and the overlay is already in disk mode, so the ordering in the generated
script is forced rather than stylistic.
Configuration is deferred to the target's first boot through RunOnce instead
of running in the build. Two reasons: uwfmgr does not exist until the DISM
feature has been through a reboot, and the swapfile belongs on the real data
volume rather than on the throwaway copy the build attaches. Enabling the
filter needs one more restart after that, which the script asks for itself.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
Three things, all in service of putting Proxmox in a VM that can still hand a
GPU to its own guests, and of a Windows VM whose profile survives its OS disk.
pci.viommu.enable emits `-device intel-iommu,intremap=on,caching-mode=on` and
forces kernel-irqchip=split, which interrupt remapping requires. Without an
IOMMU of its own a guest cannot bind a passed-through device to vfio-pci, so
it can never forward one on. The device leads the command line because QEMU
realizes devices in order and intel-iommu must precede what it translates.
pci.vgaPassthrough (default true, so nothing changes for existing VMs) makes
x-vga=on optional. It was forced on the first passthrough device, which is
wrong for a card the guest only forwards onward: it claims the VGA path the
emulated console adapter needs.
customizeImage gains extraDisk, a blank disk attached for the Audit Mode boot
and emitted as the derivation's `data` output. generalize uses it for dataDisk
and profilesDirectory, so the disk is partitioned and the profile relocated
under OOBE in the build VM. That is what removes the need for delayOobeRun --
previously the volume ProfilesDirectory names could not exist until the image
reached real hardware. A second output rather than a directory keeps ${image}
meaning the OS qcow2 for every existing consumer.
generalize also picks up staticIP and profilesDirectory.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
- Switch MAS from /HWID to /Z-Windows (TSforge ZeroCID) which is
hardware-independent and survives VM migration
- Re-install product key and restart SPP service before TSforge
to restore licensing state after sysprep
- Add nicModel option to customizeImage and generalize for images
without VirtIO drivers
- Update MAS activation script to latest version
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Allows overriding the QEMU NIC model during builds (e.g. e1000 for
images without VirtIO drivers). Enables MAS activation on upstream
images that lack VirtIO network drivers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Win11 LTSC 2024 RDP works with MAS. The edition switch issue was
specific to Win10 LTSC 2021.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New-NetFirewallRule with -Profile Any is more reliable than
Enable-NetFirewallRule (predefined rules may not exist or be
profile-scoped). Set UserAuthentication=1 (NLA) per standard
RDP configuration. Settings take effect after reboot.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
MAS HWID switches Enterprise LTSC to IoT Enterprise S which lacks
the RDP server listener. Skip activation to preserve the edition.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
MAS HWID activation switches the edition from Enterprise LTSC to IoT
Enterprise LTSC (which lacks the RDP server listener). Re-apply the
Enterprise LTSC product key after activation to restore RDP capability.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
sc config fails silently for these services. Use reg add to set
Start=2 (automatic) directly in the registry instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
TermService alone doesn't create the RDP listener — SessionEnv (Remote
Desktop Configuration) and UmRdpService (Port Redirector) must also be
running. Use PowerShell Enable-NetFirewallRule to enable the built-in
Remote Desktop firewall rules for all network profiles instead of
creating custom netsh rules.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- generalize.nix: add enableRDP option that re-enables RDP in
post-oobe.cmd after sysprep resets registry (firewall rules,
TermService auto-start, disable NLA)
- Fix OOBE AutoLogon: create user with blank password (Windows
ignores unattend passwords), set real password via net user in
post-oobe.cmd, and explicitly set AutoAdminLogon registry values
- Add LogonCount=999 for persistent AutoLogon across reboots
- Remove unused rdpEntries import from registry/default.nix
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Laptop images now use AHCI storage + e1000 network instead of VirtIO.
This fixes "inaccessible boot device" on real hardware — the AHCI→NVMe
driver transition is handled by Windows, unlike VirtIO→NVMe which isn't.
- makeImage: useAHCI flag switches disk to ide-hd and network to e1000
- customizeImage: auto-detects useAHCI from original image, propagates it
- win10/win11 laptop images: useAHCI = true
- vmix run: --ahci flag for running laptop images in QEMU
- generalize: PlainText password tags in OOBE unattend XML
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SDL display:
- try SDL, auto-fallback to headless if it fails (no crash)
- SDL_VIDEODRIVER=x11 to avoid wayland socket path issues
- suppress XDG_RUNTIME_DIR warnings
Disk copy:
- zap-all before writing to clear old partition tables
- delete recovery partition (4) before resizing partition 3
- use parted resizepart (preserves partition GUID for BCD)
- remote: nix-shell for sgdisk/parted/ntfsresize on target
- remote: lz4 compression for faster streaming
- remote: pv progress bar with disk size
- -y/--yes flag to skip confirmation prompt
Generalize:
- delay-oobe-run=true defers OOBE + activation to real hardware
- clean cached Autounattend from Windows\Panther before sysprep
- taskkill sysprep.exe on first login (CopyProfile artifact)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CLI:
- `vmix run <qcow2>` boots image with QEMU (SDL if DISPLAY, snapshot mode)
- --generalize supports delay-oobe-run=true to defer OOBE + activation
to first boot on real hardware (for physical disk deployments)
Templates:
- essentials.virtioDrivers: installs VirtIO drivers only (no guest agent)
used in laptop bundle for network access during Office download
- generalize: delayOobeRun flag controls sysprep /shutdown vs /reboot
delays OOBE, user creation and HWID activation to target device
Build:
- suppress XDG_RUNTIME_DIR and homeless-shelter warnings in SDL mode
- remove invalid ICH9-LMB global properties
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>