diff --git a/lib/images/windows/templates/generalize.nix b/lib/images/windows/templates/generalize.nix index b38811b..d5eafae 100644 --- a/lib/images/windows/templates/generalize.nix +++ b/lib/images/windows/templates/generalize.nix @@ -42,6 +42,16 @@ in # delayOobeRun = true: sysprep only, OOBE + activation on real hardware # delayOobeRun = false: sysprep + OOBE + activation in build VM delayOobeRun ? false, + # Skip sysprep's SID reset (use /oobe without /generalize), so every rebuild + # of the layers above the cached base install carries the same machine SID -- + # and therefore the same account SID. A profile kept on a persistent disk then + # matches on file ACLs, its NTUSER.DAT hive, and ProfileList across rebuilds, + # with no ownership fixups. As a side effect MountedDevices survives too, so + # the data disk keeps its drive letter without a boot-time reassign. + # + # Correct only for an image that is always this one machine; a fleet that + # deploys the same image to many hosts wants the default generalization. + keepMachineSid ? false, }: let # Convert "8e8cd8" hex to "142 140 216" decimal RGB for Windows registry hexToRgbStr = hex: let @@ -229,6 +239,17 @@ in ${profilesDirectory} ''; + # ProfilesDirectory as an offline .reg merge, for the keepMachineSid path + # where the specialize pass (and its FolderLocations) does not run. virt-win-reg + # applies this before the Audit Mode boot, so it is in place when OOBE creates + # the account. Backslashes are doubled for .reg syntax. + profileListRegistry = lib.optionalString (keepMachineSid && profilesDirectory != null) '' + Windows Registry Editor Version 5.00 + + [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList] + "ProfilesDirectory"="${builtins.replaceStrings [''\''] [''\\''] profilesDirectory}" + ''; + dataDiskXml = lib.optionalString (dataDisk != null) ''