vmix.nix/lib/images/windows/templates/generalize.nix
Git Sagar 94f299bb81 sync with labv2.nix + standalone flake with toDisk app
Previous history:
- c359054 daku working!
- 8de5cff fix integer overflow in vmix network lib
- 9c25a66 daku on 25.05. with ollama
- 385a3bf vmix enables relaxed sandbox
- c363da1 restructure vmixLib into linux/windows subattrs with OS-specific customizeImage
- edd4dc2 vmix: port namespace model and module improvements from conf.nix
- 6666ecf vmix: add SPICE support, install virtio guest tools with SPICE agent
- 46f5671 vmix: add QEMU guest agent channel for Windows VMs
- e1fea34 vmix: add Win11 LTSC 2024 image, refactor VirtIO driver selection
- c27ae68 vmix: make customizeImage chroot-sandboxed by default, opt-in impure
- 305fbac virt customize needs chroot for now due to usr bin env things. could be fixed later
- 264d30f vmix: add win10 VM on desk, disable SMB signing for guest Samba access
- 9b64f51 vmix: split Windows templates into per-category files, add comprehensive debloat
- ef91bf8 vmix: fix missing parent registry keys in Windows templates
- f87f340 win10 VM on panda with AMD GPU + USB passthrough
- 38e474f vmix: split Windows build into Audit Mode install + composable templates
- a6a8db3 vmix: win11 support, remove build VNC, switch VMs to SPICE
- 6cf5a21 generalize stage sets bg color, accent color and sets visual effects to performance
- a84849f remove rdp template since it doesn't even work
- 5245263 vmix: best performance template + generalize cleanup
- ab12dd3 vmix: use CopyProfile for best performance visual effects
- bce3326 vmix: CopyProfile for best performance visual effects
- 2496107 vmix: add app templates (7zip, VLC, ImageGlass, Edge WebView, VC++ runtimes)
- 29a6123 wip: debug default associations xml
- 2a2e5f5 vmix: fix DefaultAssociations.xml cmd.exe escaping
- cc6ff9d vmix: move DefaultAssociations.xml to template only
- a4a78ec vmix: add removeWMP template to remove Windows Media Player
- 3fe56de vmix: improved Edge removal (files, shortcuts, scheduled tasks)
- a491767 vmix: fully remove Edge via post-oobe AppxPackage removal
- 6ca1619 vmix: remove Edge DevToolsClient SystemApps + AppxPackage
- 0c1ec35 vmix: sandboxie windows app template
- 628bbd2 vmix: add Sandboxie-Plus template
- f055a41 vmix: reorganize templates, add file associations, remove Paint
- 34326f4 vmix: set Thorium as default browser via PS-SFTA in post-oobe
- 86af258 vmix: Active Setup for default browser (all users, no post-oobe needed)
- 35b8cb0 remove vnc display from thorium template
- c7e0af6 vmix: fix Win11 generalize timeout + UCPD disable for URL associations
- 43a1345 vmix: add Office 2024 template + Ohook activation in generalize
- 03bbce0 vmix: updated office installation xml. more privacy options enabled
- 790a0ee vmix: thorium installation - hide SFTA window
- a0e5c18 vmix: fix office install.bat call + add privacy registry policies
- 3df38ca vmix: fix Ohook activation + suppress Office theme dialog
- df39ba3 vmix: remove sandboxie shortcut from desktop
- 50d5972 vmix: skip Sandboxie desktop shortcut via installer flag
- ee2fa0f vmix: fix win10 default browser
- 938315b vmix: windows: set accent color to automatic. remove accent color from unnecessary elements
- beceda8 vmix: allow ISO-only VMs without OS disk, add WinPE VM to panda

Flake outputs: overlays.default, nixosModules.default, lib, apps.toDisk

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 19:18:10 -03:00

168 lines
7.1 KiB
Nix

# Generalize image via sysprep + OOBE in two phases.
# Phase 1 (sysprep): runs sysprep /generalize /oobe /shutdown in Audit Mode
# Phase 2 (oobe): boots through OOBE, creates user, activates Windows, shuts down
# Between phases, NTUSER.DAT can be modified offline.
# Usage: (templates.generalize { username = "User"; password = ""; })
{ pkgs, lib, makeFilesISO, ... }:
let
masScript = pkgs.fetchurl {
url = "https://raw.githubusercontent.com/massgravel/Microsoft-Activation-Scripts/97602941e5724316aa31b6ca1da5c70245d234d5/MAS/All-In-One-Version-KL/MAS_AIO.cmd";
hash = "sha256-1hl89jQf2p+RtE3ue/+cZevSoz7Ra3p3u350aE/Xy74=";
};
in
{
username ? "User",
password ? "",
autoLogon ? true,
hostname ? "WIN-VM",
locale ? "en-US",
timezone ? "UTC",
# Desktop background solid color as hex string (e.g. "8e8cd8")
bgColor ? null,
}: let
# Convert "8e8cd8" hex to "142 140 216" decimal RGB for Windows registry
hexToRgbStr = hex: let
hexChars = lib.stringToCharacters hex;
hexToDec = h: let
c = lib.toLower h;
m = { "0"=0; "1"=1; "2"=2; "3"=3; "4"=4; "5"=5; "6"=6; "7"=7; "8"=8; "9"=9; "a"=10; "b"=11; "c"=12; "d"=13; "e"=14; "f"=15; };
in m.${c};
r = hexToDec (builtins.elemAt hexChars 0) * 16 + hexToDec (builtins.elemAt hexChars 1);
g = hexToDec (builtins.elemAt hexChars 2) * 16 + hexToDec (builtins.elemAt hexChars 3);
b = hexToDec (builtins.elemAt hexChars 4) * 16 + hexToDec (builtins.elemAt hexChars 5);
in "${toString r} ${toString g} ${toString b}";
stripHash = s: lib.removePrefix "#" s;
bgRgb = if bgColor != null then hexToRgbStr (stripHash bgColor) else null;
# Post-OOBE script: runs as the created user via FirstLogonCommands.
postOobeScript = pkgs.writeText "post-oobe.cmd" ''
@echo off
${lib.optionalString (!autoLogon) ''
reg delete "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v AutoAdminLogon /f 2>nul
reg delete "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v DefaultUserName /f 2>nul
reg delete "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v DefaultPassword /f 2>nul
''}
${lib.optionalString (bgColor != null) ''
:: Set solid background color
reg add "HKCU\Control Panel\Desktop" /v WallPaper /t REG_SZ /d "" /f
reg add "HKCU\Control Panel\Colors" /v Background /t REG_SZ /d "${bgRgb}" /f
reg add "HKCU\Control Panel\Desktop" /v WallpaperStyle /t REG_SZ /d "0" /f
''}
:: Remove Edge AppxPackage for current user (runs in user context during OOBE)
:: The app is already removed on one of the templates but a ghost appx entry remains that can only be deleted at the user level
powershell -Command "Get-AppxPackage *MicrosoftEdge* | Remove-AppxPackage -ErrorAction SilentlyContinue"
powershell -Command "Get-AppxPackage *MicrosoftEdgeDevToolsClient* | Remove-AppxPackage -ErrorAction SilentlyContinue"
:: Activate Windows using HWID method
if exist C:\MAS_AIO.cmd (
echo. | call C:\MAS_AIO.cmd /HWID
)
:: Activate Office using Ohook method (if Office is installed)
if exist "C:\Program Files\Microsoft Office\root\Office16\WINWORD.EXE" (
if exist C:\MAS_AIO.cmd (
echo. | call C:\MAS_AIO.cmd /Ohook
)
)
del /q C:\MAS_AIO.cmd 2>nul
:: Clean up
del /q C:\oobe-unattend.xml 2>nul
del /q C:\vmix-audit-script.cmd 2>nul
del /q C:\vmix-audit-wrapper.cmd 2>nul
shutdown /s /t 5 /c "vmix generalize complete"
del /q C:\post-oobe.cmd 2>nul
'';
oobeXml = pkgs.writeText "oobe-unattend.xml" ''
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend"
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
<!-- Copy Administrator profile to default (preserves Audit Mode customizations) -->
<settings pass="specialize">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<CopyProfile>true</CopyProfile>
<Themes>
<WindowColor>Automatic</WindowColor>
</Themes>
</component>
</settings>
<settings pass="oobeSystem">
<component name="Microsoft-Windows-International-Core" processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<InputLocale>${locale}</InputLocale>
<SystemLocale>${locale}</SystemLocale>
<UILanguage>${locale}</UILanguage>
<UserLocale>${locale}</UserLocale>
</component>
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64"
publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<OOBE>
<HideEULAPage>true</HideEULAPage>
<HideLocalAccountScreen>true</HideLocalAccountScreen>
<HideOnlineAccountScreens>true</HideOnlineAccountScreens>
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
<NetworkLocation>Work</NetworkLocation>
<SkipMachineOOBE>true</SkipMachineOOBE>
<SkipUserOOBE>true</SkipUserOOBE>
<ProtectYourPC>3</ProtectYourPC>
</OOBE>
<UserAccounts>
<LocalAccounts>
<LocalAccount wcm:action="add">
<Password>
<Value>${password}</Value>
</Password>
<Group>Administrators</Group>
<Name>${username}</Name>
</LocalAccount>
</LocalAccounts>
</UserAccounts>
<AutoLogon>
<Password>
<Value>${password}</Value>
</Password>
<Enabled>true</Enabled>
<Username>${username}</Username>
</AutoLogon>
<ComputerName>${hostname}</ComputerName>
<TimeZone>${timezone}</TimeZone>
<FirstLogonCommands>
<SynchronousCommand wcm:action="add">
<Order>1</Order>
<CommandLine>C:\post-oobe.cmd</CommandLine>
<RequiresUserInput>false</RequiresUserInput>
</SynchronousCommand>
</FirstLogonCommands>
</component>
</settings>
</unattend>
'';
in {
name = "generalize";
uploads = [
{ source = oobeXml; dest = "/oobe-unattend.xml"; }
{ source = postOobeScript; dest = "/post-oobe.cmd"; }
{ source = masScript; dest = "/MAS_AIO.cmd"; }
];
# Sysprep reboots into OOBE within the same QEMU session
auditScript = ''
@echo off
C:\Windows\System32\Sysprep\sysprep.exe /generalize /oobe /reboot /quiet /unattend:C:\oobe-unattend.xml
'';
}
# :: Enable RDP (sysprep resets offline registry changes)
# reg add "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server" /v fDenyTSConnections /t REG_DWORD /d 0 /f
# reg add "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" /v UserAuthentication /t REG_DWORD /d 0 /f
# netsh advfirewall firewall add rule name="RDP" dir=in protocol=tcp localport=3389 action=allow
# :: Start and enable the RDP service
# sc config TermService start= auto
# net start TermService