# Windows Update Template - Development Session ## Goal Create a `customizeImage` template that applies Windows Updates to an existing image via the Windows Update COM API in Audit Mode, then compacts the qcow2. ## What was built ### New files - `lib/images/windows/templates/essentials/windows-update.nix` — template that boots into Audit Mode, runs Windows Update via COM API, handles reboots automatically, compacts image ### Modified files - `lib/images/windows/helpers/customizeImage.nix` — added `compact`, `qemuTimeout` parameters - `lib/images/windows/templates/default.nix` — wired `windowsUpdate` into `essentials` - `lib/images/windows/win10/images.nix` — added `updated` step between `upstream` and `basic` - `lib/images/windows/win11/images.nix` — same ### New customizeImage parameters - `compact ? false` — flattens COW chain via `qemu-img convert` into standalone qcow2 (no backing file) - `qemuTimeout ? 1800` — configurable QEMU timeout in seconds (was hardcoded 30 min) ### How the template works 1. Boots image in Audit Mode with QEMU user networking 2. Starts `wuauserv` service, removes update-blocking policies 3. Uses PowerShell COM API (`Microsoft.Update.Session`) to search, download, install updates 4. Accepts EULAs, logs per-update results 5. If reboot required: copies script, preserves Audit Mode registry keys, re-registers via RunOnce, reboots 6. After reboot: continues with next round (up to `maxRounds`, default 3) 7. When done: runs `dism /Cleanup-Image /StartComponentCleanup /ResetBase`, shuts down 8. Host-side: `qemu-img convert` flattens COW chain (when `compact=true`) ### Usage ```nix # Online mode (default) - triggers Windows Update service essentials.windowsUpdate {} essentials.windowsUpdate { maxRounds = 5; } # In image pipeline updated = customizeImage upstream (templates.essentials.windowsUpdate {}); basic = customizeImageFold updated [ ... ]; ``` ## Test results ### What works - Windows Update service starts successfully in Audit Mode - COM API finds all available updates (cumulative, .NET, Defender, MSRT) - Downloads and installs 6 updates including: - 2026-05 Cumulative Update KB5087544 - .NET Framework updates (KB5010472, KB5011048, KB5088859) - Windows Malicious Software Removal Tool (KB890830) - Microsoft Defender definitions (KB2267602) - `compact` flag works — produces standalone qcow2 without backing file - Image grows from 5.02 GB to ~8.33 GB with updates ### Issues found and fixed during session 1. **First build (no service start)**: COM API found 0 updates because `wuauserv` wasn't running - Fix: added `net start wuauserv` + `sc config wuauserv start= auto` + remove blocking policies + 30s wait 2. **Wrapper deletes script before reboot**: After round 1, the wrapper's `del /q C:\vmix-audit-script.cmd` runs before reboot completes, so RunOnce points to deleted file - Fix: script copies itself to `C:\vmix-update-continue.cmd` before rebooting; registers that path in RunOnce 3. **Audit Mode lost after reboot**: Cumulative updates can reset the Audit Mode flag, causing Windows to enter OOBE instead of Audit Mode after reboot - Fix: explicitly set `AuditBoot=1` and `AuditInProgress=1` registry keys before rebooting 4. **No shutdown after round 2**: When script runs from RunOnce (not wrapper), no shutdown command executes - Fix: script always calls `shutdown /s /f /t 10` when done, regardless of invocation method ### Remaining TODO (for next session on faster machine) - [ ] Verify the Audit Mode preservation fix (AuditBoot + AuditInProgress registry keys) actually works — last build was still in round 1 install when session ended - [ ] The cumulative update install takes ~60-90 min with 4GB RAM — consider using 8GB for faster builds - [ ] Test round 2 → round 3 flow (does it find additional updates after cumulative?) - [ ] Test with `compact = true` to verify final image is standalone - [ ] Test full pipeline: `upstream → updated → basic → generalize` - [ ] Test win11 images too - [ ] Consider: should `windowsUpdate` go before or after `virtioTools` in the pipeline? Currently it's before (applied to raw upstream), but having virtio drivers might help with disk I/O performance during update install - [ ] The template is impure (downloads from Microsoft during build) — document this clearly - [ ] Consider adding a `maxRounds = 1` fast mode that skips the reboot cycle (just installs whatever doesn't need reboot) ## Build command for testing ```bash nix build --print-build-logs --impure --option sandbox relaxed --expr ' let vmixLib = (builtins.getFlake "path:/storage/gitrepos/vmix.nix").lib.x86_64-linux; upstream = vmixLib.windows.images.win10.ltsc.upstream; withVirtio = vmixLib.windows.customizeImage upstream vmixLib.windows.templates.essentials.virtioTools; windowsUpdate = vmixLib.windows.templates.essentials.windowsUpdate {}; in vmixLib.windows.customizeImage withVirtio (windowsUpdate // { vncDisplay = ":55"; compact = false; }) ' ``` Monitor via VNC on port 5955 (`localhost:55`): ```bash gvnccapture localhost:55 /tmp/screenshot.png ```