# Per-VM config medium for a sealed Windows image (see templates.seal). # # A sealed image carries no per-VM data. The values that differ between VMs -- # hostname, the static address the guest asserts, timezone, desktop tint -- are # written here as a PowerShell data file and packed into a tiny ISO. config.nix # attaches it as a read-only CD-ROM; the image's baked first-boot scripts # (vmix-load-config.cmd finds it, then dot-source it) apply the values. So one # sealed store path is shared by every VM, and only this cheap ISO is per-VM. # # Usage: # makeConfigMedium { # name = "win-config"; # hostname = "panda-win"; # staticIP = { address = "10.10.10.26"; prefixLength = 24; # gateway = "10.10.10.1"; dns = [ "10.10.10.1" ]; }; # timezone = "E. South America Standard Time"; # bgColor = "#856558"; # } { pkgs, lib, makeFilesISO, ... }: { name ? "vmix-config", hostname ? "", # The per-VM account. The sealed image carries a generic bootstrap account # (only there to carry OOBE); on first boot this real account is created from # here, gets the SID-bound profile on D:\Users\, and the bootstrap # is retired. Distinct per VM -- nothing about the account is shared/baked. username ? "", password ? "", # { address; prefixLength; gateway; dns = [ ... ]; } staticIP ? null, timezone ? null, # Solid desktop background as a hex string, e.g. "#856558". Converted to the # registry's decimal "R G B" on the target, in vmix-apply-config.ps1. bgColor ? null, }: let dnsList = lib.optionalString (staticIP != null) (lib.concatMapStringsSep "," (s: "'${s}'") staticIP.dns); # Consumed by dot-sourcing (. C:\vmix-config.ps1), so it only assigns # variables. Anything not set here is simply absent, and the baked scripts # guard on that ($VmixIpAddress being null skips the static-IP assignment). configPs1 = pkgs.writeText "vmix-config.ps1" '' # vmix per-VM config -- generated, read by the sealed image's baked scripts. $VmixHostname = '${hostname}' ${lib.optionalString (username != "") "$VmixUsername = '${username}'"} ${lib.optionalString (username != "") "$VmixPassword = '${password}'"} ${lib.optionalString (staticIP != null) '' $VmixIpAddress = '${staticIP.address}' $VmixPrefixLength = ${toString staticIP.prefixLength} $VmixGateway = '${staticIP.gateway}' $VmixDns = @(${dnsList})''} ${lib.optionalString (timezone != null) "$VmixTimeZone = '${timezone}'"} ${lib.optionalString (bgColor != null) "$VmixBgColor = '${bgColor}'"} ''; in # makeFilesISO strips the store-hash prefix, so this lands at the ISO root as # exactly vmix-config.ps1 -- which is what vmix-load-config.cmd scans for. makeFilesISO { inherit name; files = [ configPs1 ]; }