Add essentials.windowsUpdate template that boots Audit Mode, uses the Windows Update COM API to search/download/install all available updates (cumulative, .NET, Defender), handles multi-round reboots with Audit Mode preservation, and compacts the image afterward. Known issues being worked: - Audit Mode preservation after update reboot needs verification - Install takes ~60-90 min with 4GB RAM on slow machines See wip/win10-update.session.md for full context and TODOs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 KiB
5 KiB
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— addedcompact,qemuTimeoutparameterslib/images/windows/templates/default.nix— wiredwindowsUpdateintoessentialslib/images/windows/win10/images.nix— addedupdatedstep betweenupstreamandbasiclib/images/windows/win11/images.nix— same
New customizeImage parameters
compact ? false— flattens COW chain viaqemu-img convertinto standalone qcow2 (no backing file)qemuTimeout ? 1800— configurable QEMU timeout in seconds (was hardcoded 30 min)
How the template works
- Boots image in Audit Mode with QEMU user networking
- Starts
wuauservservice, removes update-blocking policies - Uses PowerShell COM API (
Microsoft.Update.Session) to search, download, install updates - Accepts EULAs, logs per-update results
- If reboot required: copies script, preserves Audit Mode registry keys, re-registers via RunOnce, reboots
- After reboot: continues with next round (up to
maxRounds, default 3) - When done: runs
dism /Cleanup-Image /StartComponentCleanup /ResetBase, shuts down - Host-side:
qemu-img convertflattens COW chain (whencompact=true)
Usage
# 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)
compactflag 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
-
First build (no service start): COM API found 0 updates because
wuauservwasn't running- Fix: added
net start wuauserv+sc config wuauserv start= auto+ remove blocking policies + 30s wait
- Fix: added
-
Wrapper deletes script before reboot: After round 1, the wrapper's
del /q C:\vmix-audit-script.cmdruns before reboot completes, so RunOnce points to deleted file- Fix: script copies itself to
C:\vmix-update-continue.cmdbefore rebooting; registers that path in RunOnce
- Fix: script copies itself to
-
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=1andAuditInProgress=1registry keys before rebooting
- Fix: explicitly set
-
No shutdown after round 2: When script runs from RunOnce (not wrapper), no shutdown command executes
- Fix: script always calls
shutdown /s /f /t 10when done, regardless of invocation method
- Fix: script always calls
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 = trueto verify final image is standalone - Test full pipeline:
upstream → updated → basic → generalize - Test win11 images too
- Consider: should
windowsUpdatego before or aftervirtioToolsin 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 = 1fast mode that skips the reboot cycle (just installs whatever doesn't need reboot)
Build command for testing
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):
gvnccapture localhost:55 /tmp/screenshot.png