vmix.nix/lib/images/windows/helpers/makeConfigMedium.nix
Git Sagar 1510b6c5ff windows/seal: per-VM account on the config medium, not baked
username/password move from the sealed image to makeConfigMedium, so two
VMs get distinct logins (and, as before, distinct SIDs). The sealed
image bakes only a generic bootstrap account ("vmixsetup") whose sole job
is to carry OOBE to a logon; post-oobe then creates the real account from
the config CD, switches autologon to it, and reboots so it builds its own
SID-bound profile on D:\Users\<username>. A one-shot cleanup (RunOnce,
first logon of the real account) retires the bootstrap and its profile
and applies the per-user tint. So nothing about the account is shared or
baked, and the on-disk profile folder matches the real username.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
2026-09-18 09:24:04 -03:00

59 lines
2.8 KiB
Nix

# 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\<username>, 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 ]; }