diff --git a/wfvm/bundle/default.nix b/bundle/default.nix
similarity index 100%
rename from wfvm/bundle/default.nix
rename to bundle/default.nix
diff --git a/wfvm/bundle/go.mod b/bundle/go.mod
similarity index 100%
rename from wfvm/bundle/go.mod
rename to bundle/go.mod
diff --git a/wfvm/bundle/main.go b/bundle/main.go
similarity index 100%
rename from wfvm/bundle/main.go
rename to bundle/main.go
diff --git a/wfvm/bundle/shell.nix b/bundle/shell.nix
similarity index 100%
rename from wfvm/bundle/shell.nix
rename to bundle/shell.nix
diff --git a/flake.nix b/flake.nix
index af1b4a5..3d82254 100644
--- a/flake.nix
+++ b/flake.nix
@@ -297,6 +297,333 @@
# ============
+ # autounattend
+ build-autounattend = { fullName ? "John Doe"
+ , organization ? "KVM Authority"
+ , administratorPassword ? "123456"
+ , uiLanguage ? "en-US"
+ , inputLocale ? "en-US"
+ , userLocale ? "en-US"
+ , systemLocale ? "en-US"
+ , users ? { wfvm = { password = "1234";
+ description = "WFVM Administrator";
+ groups = [ "Administrators" ]; }; }
+ , productKey ? null
+ , defaultUser ? "wfvm"
+ , setupCommands ? []
+ , timeZone ? "UTC"
+ , services ? {}
+ , impureShellCommands ? []
+ , driveLetter ? "D:"
+ , imageSelection ? "Windows 10 Pro"
+ , ...
+ }:
+
+ let
+ lib = pkgs.lib;
+ serviceCommands = lib.mapAttrsToList (
+ serviceName: attrs: "powershell Set-Service -Name ${serviceName} " + (
+ lib.concatStringsSep " " (
+ (
+ lib.mapAttrsToList (
+ n: v: if builtins.typeOf v != "bool" then "-${n} ${v}" else "-${n}"
+ )
+ ) (
+ # Always run without interaction
+ { Force = true; } // attrs
+ )
+ )
+ )
+ ) services;
+
+ sshSetupCommands =
+ # let
+ # makeDirs = lib.mapAttrsToList (n: v: ''mkdir C:\Users\${n}\.ssh'') users;
+ # writeKeys = lib.flatten (lib.mapAttrsToList (n: v: builtins.map (key: let
+ # commands = [
+ # ''powershell.exe Set-Content -Path C:\Users\${n}\.ssh\authorized_keys -Value '${key}' ''
+ # ];
+ # in lib.concatStringsSep "\n" commands) (v.sshKeys or [])) users);
+ # mkDirsDesc = builtins.map (c: {Path = c; Description = "Make SSH key dir";}) makeDirs;
+ # writeKeysDesc = builtins.map (c: {Path = c; Description = "Add SSH key";}) writeKeys;
+ # in
+ # mkDirsDesc ++ writeKeysDesc ++
+ [
+ {
+ Path = ''powershell.exe ${driveLetter}\install-ssh.ps1'';
+ Description = "Install OpenSSH service.";
+ }
+ ];
+
+ assertCommand = c: builtins.typeOf c == "string" || builtins.typeOf c == "set" && builtins.hasAttr "Path" c && builtins.hasAttr "Description" c;
+
+ commands = builtins.map (x: assert assertCommand x; if builtins.typeOf x == "string" then { Path = x; Description = x; } else x) (
+ [
+ {
+ Path = "powershell.exe Set-ExecutionPolicy -Force Unrestricted";
+ Description = "Allow unsigned powershell scripts.";
+ }
+ ]
+ ++ [
+ {
+ Path = ''powershell.exe ${driveLetter}\win-bundle-installer.exe'';
+ Description = "Install any declared packages.";
+ }
+ ]
+ ++ setupCommands
+ ++ [
+ {
+ Path = ''powershell.exe ${driveLetter}\setup.ps1'';
+ Description = "Setup SSH and keys";
+ }
+ ]
+ ++ serviceCommands
+ ++ impureShellCommands
+ );
+
+ mkCommand = attrs: ''
+
+ ${lib.concatStringsSep "\n" (lib.attrsets.mapAttrsToList (n: v: "<${n}>${v}${n}>") attrs)}
+
+ '';
+ mkCommands = commands: (
+ builtins.foldl' (
+ acc: v: rec {
+ i = acc.i + 1;
+ values = acc.values ++ [ (mkCommand (v // { Order = builtins.toString i; })) ];
+ }
+ ) {
+ i = 0;
+ values = [];
+ } commands
+ ).values;
+
+ mkUser =
+ { name
+ , password
+ , description ? ""
+ , displayName ? ""
+ , groups ? []
+ # , sshKeys ? [] # Handled in scripts
+ }: ''
+
+
+ ${password}
+ true
+
+ ${description}
+ ${displayName}
+ ${builtins.concatStringsSep ";" (lib.unique ([ "Users" ] ++ groups))}
+ ${name}
+
+ '';
+
+ # Windows expects a flat list of users while we want to manage them as a set
+ flatUsers = builtins.attrValues (builtins.mapAttrs (name: s: s // { inherit name; }) users);
+
+ diskId =
+ if efi then 2 else 1;
+
+ autounattendXML = pkgs.writeText "autounattend.xml" ''
+
+
+
+
+
+
+ D:\
+
+
+ E:\
+
+
+ C:\virtio\amd64\w10
+
+
+ C:\virtio\NetKVM\w10\amd64
+
+
+ C:\virtio\qxldod\w10\amd64
+
+
+
+
+
+
+
+
+
+ 1
+ ${if efi then "EFI" else "Primary"}
+ 300
+
+
+ 2
+ ${if efi then "MSR" else "Primary"}
+ 16
+
+
+ 3
+ Primary
+ true
+
+
+
+
+ 1
+ ${if efi then "FAT32" else "NTFS"}
+
+ 1
+
+
+ 2
+ 2
+
+
+ 3
+ NTFS
+
+ C
+ 3
+
+
+ ${toString diskId}
+ true
+
+
+
+
+
+
+ ${toString diskId}
+ 3
+
+
+
+ /IMAGE/NAME
+ ${imageSelection}
+
+
+
+
+
+
+
+ ${if productKey != null then "${productKey}" else ""}
+ OnError
+
+ true
+ ${fullName}
+ ${organization}
+
+
+
+
+
+ ${uiLanguage}
+
+ ${inputLocale}
+ ${systemLocale}
+ ${uiLanguage}
+ en-US
+ ${userLocale}
+
+
+
+
+
+ ${inputLocale}
+ ${systemLocale}
+ ${uiLanguage}
+ en-US
+ ${userLocale}
+
+
+
+ true
+ true
+ true
+ true
+ true
+ 1
+
+ ${timeZone}
+
+
+ ${if administratorPassword != null then ''
+
+ ${administratorPassword}
+ true
+
+ '' else ""}
+
+ ${builtins.concatStringsSep "\n" (builtins.map mkUser flatUsers)}
+
+
+
+ ${if defaultUser == null then "" else ''
+
+
+ ${(builtins.getAttr defaultUser users).password}
+ true
+
+ true
+ ${defaultUser}
+
+ ''}
+
+
+
+
+ true
+ OOBE
+
+
+
+
+
+
+
+ ${lib.concatStringsSep "\n" (mkCommands commands)}
+
+
+
+ 0
+
+
+
+
+
+
+ false
+
+
+
+
+
+ '';
+
+ in {
+ # Lint and format as a sanity check
+ autounattendXML = pkgs.runCommandNoCC "autounattend.xml" {} ''
+ ${pkgs.libxml2}/bin/xmllint --format ${autounattendXML} > $out
+ '';
+
+ # autounattend.xml is _super_ picky about quotes and other things
+ setupScript = pkgs.writeText "setup.ps1" (
+ ''
+ # Setup SSH and keys
+ '' +
+ lib.concatStrings (
+ builtins.map (c: ''
+ # ${c.Description}
+ ${c.Path}
+ '') sshSetupCommands
+ )
+ );
+ };
+ # ============
+
# makeWindowsImage
makeWindowsImage = { diskImageSize ? "70G", windowsImage ? null, autoUnattendParams ? {}
, impureMode ? false, installCommands ? []
@@ -345,20 +672,16 @@
sha256 = "1dw6n054r0939501dpxfm7ghv21ihmypdx034van8cl21gf1b4lz";
};
- autounattend = import ./autounattend.nix (
- attrs // {
- inherit pkgs;
- users = users // {
- wfvm = {
- password = "1234";
- description = "WFVM Administrator";
- groups = [
- "Administrators"
- ];
- };
+ autounattend = build-autounattend attrs // {
+ users = users // { wfvm = {
+ password = "1234";
+ description = "WFVM Administrator";
+ groups = [
+ "Administrators"
+ ];
};
- }
- );
+ };
+ };
# bundle
bundleInstaller = pkgs.runCommandNoCC "win-bundle-installer.exe" {} ''
@@ -493,23 +816,8 @@
if !(impureMode) then finalImage else assert installCommands == []; installScript;
# end of makeWindowsImage
- in {
- inherit utils;
- inherit makeWindowsImage;
-
- demo-ssh = utils.wfvm-run {
- name = "demo-ssh";
- image = import ./demo-image.nix { inherit pkgs; };
- isolateNetwork = false;
- script = ''
- ${pkgs.sshpass}/bin/sshpass -p1234 -- ${pkgs.openssh}/bin/ssh -p 2022 wfvm@localhost -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
- '';
- };
-
- packages.x86_64-linux = {
- demo-image = makeWindowsImage {
+ build-demo-image = { impureMode ? false }: makeWindowsImage {
# Build install script & skip building iso
- impureMode = false;
# Custom base iso
# windowsImage = pkgs.requireFile rec {
@@ -563,7 +871,6 @@
# productKey = throw "Search the f* web"
imageSelection = "Windows 10 Pro";
-
# Locales
# uiLanguage = "en-US";
# inputLocale = "en-US";
@@ -571,6 +878,35 @@
# systemLocale = "en-US";
};
+ in {
+
+ # bundle dev env
+ devShell.x86_64-linux = pkgs.mkShell {
+ name = "wfvm-dev-shell";
+ buildInputs = with pkgs; [
+ go
+ ];
+ shellHook = ''
+ unset GOPATH
+ '';
+ };
+
+ inherit utils;
+ inherit makeWindowsImage;
+
+ demo-ssh = utils.wfvm-run {
+ name = "demo-ssh";
+ image = import ./demo-image.nix { inherit pkgs; };
+ isolateNetwork = false;
+ script = ''
+ ${pkgs.sshpass}/bin/sshpass -p1234 -- ${pkgs.openssh}/bin/ssh -p 2022 wfvm@localhost -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
+ '';
+ };
+
+ packages.x86_64-linux = {
+ demo-image = build-demo-image {};
+ demo-image-impure = build-demo-image { impureMode = true; };
+
make-msys-packages = utils.wfvm-run {
name = "get-msys-packages";
image = makeWindowsImage { installCommands = [ layers.msys2 ]; };
diff --git a/wfvm/layers/default.nix b/layers/default.nix
similarity index 100%
rename from wfvm/layers/default.nix
rename to layers/default.nix
diff --git a/wfvm/layers/make_msys_packages.sh b/layers/make_msys_packages.sh
similarity index 100%
rename from wfvm/layers/make_msys_packages.sh
rename to layers/make_msys_packages.sh
diff --git a/wfvm/autounattend.nix b/wfvm/autounattend.nix
deleted file mode 100644
index 99b0429..0000000
--- a/wfvm/autounattend.nix
+++ /dev/null
@@ -1,325 +0,0 @@
-{ pkgs
-, fullName ? "John Doe"
-, organization ? "KVM Authority"
-, administratorPassword ? "123456"
-, uiLanguage ? "en-US"
-, inputLocale ? "en-US"
-, userLocale ? "en-US"
-, systemLocale ? "en-US"
-, users ? {}
-, productKey ? null
-, defaultUser ? "wfvm"
-, setupCommands ? []
-, timeZone ? "UTC"
-, services ? {}
-, impureShellCommands ? []
-, driveLetter ? "D:"
-, efi ? true
-, imageSelection ? "Windows 10 Pro"
-, ...
-}:
-
-let
- lib = pkgs.lib;
- serviceCommands = lib.mapAttrsToList (
- serviceName: attrs: "powershell Set-Service -Name ${serviceName} " + (
- lib.concatStringsSep " " (
- (
- lib.mapAttrsToList (
- n: v: if builtins.typeOf v != "bool" then "-${n} ${v}" else "-${n}"
- )
- ) (
- # Always run without interaction
- { Force = true; } // attrs
- )
- )
- )
- ) services;
-
- sshSetupCommands =
- # let
- # makeDirs = lib.mapAttrsToList (n: v: ''mkdir C:\Users\${n}\.ssh'') users;
- # writeKeys = lib.flatten (lib.mapAttrsToList (n: v: builtins.map (key: let
- # commands = [
- # ''powershell.exe Set-Content -Path C:\Users\${n}\.ssh\authorized_keys -Value '${key}' ''
- # ];
- # in lib.concatStringsSep "\n" commands) (v.sshKeys or [])) users);
- # mkDirsDesc = builtins.map (c: {Path = c; Description = "Make SSH key dir";}) makeDirs;
- # writeKeysDesc = builtins.map (c: {Path = c; Description = "Add SSH key";}) writeKeys;
- # in
- # mkDirsDesc ++ writeKeysDesc ++
- [
- {
- Path = ''powershell.exe ${driveLetter}\install-ssh.ps1'';
- Description = "Install OpenSSH service.";
- }
- ];
-
- assertCommand = c: builtins.typeOf c == "string" || builtins.typeOf c == "set" && builtins.hasAttr "Path" c && builtins.hasAttr "Description" c;
-
- commands = builtins.map (x: assert assertCommand x; if builtins.typeOf x == "string" then { Path = x; Description = x; } else x) (
- [
- {
- Path = "powershell.exe Set-ExecutionPolicy -Force Unrestricted";
- Description = "Allow unsigned powershell scripts.";
- }
- ]
- ++ [
- {
- Path = ''powershell.exe ${driveLetter}\win-bundle-installer.exe'';
- Description = "Install any declared packages.";
- }
- ]
- ++ setupCommands
- ++ [
- {
- Path = ''powershell.exe ${driveLetter}\setup.ps1'';
- Description = "Setup SSH and keys";
- }
- ]
- ++ serviceCommands
- ++ impureShellCommands
- );
-
- mkCommand = attrs: ''
-
- ${lib.concatStringsSep "\n" (lib.attrsets.mapAttrsToList (n: v: "<${n}>${v}${n}>") attrs)}
-
- '';
- mkCommands = commands: (
- builtins.foldl' (
- acc: v: rec {
- i = acc.i + 1;
- values = acc.values ++ [ (mkCommand (v // { Order = builtins.toString i; })) ];
- }
- ) {
- i = 0;
- values = [];
- } commands
- ).values;
-
- mkUser =
- { name
- , password
- , description ? ""
- , displayName ? ""
- , groups ? []
- # , sshKeys ? [] # Handled in scripts
- }: ''
-
-
- ${password}
- true
-
- ${description}
- ${displayName}
- ${builtins.concatStringsSep ";" (lib.unique ([ "Users" ] ++ groups))}
- ${name}
-
- '';
-
- # Windows expects a flat list of users while we want to manage them as a set
- flatUsers = builtins.attrValues (builtins.mapAttrs (name: s: s // { inherit name; }) users);
-
- diskId =
- if efi then 2 else 1;
-
- autounattendXML = pkgs.writeText "autounattend.xml" ''
-
-
-
-
-
-
- D:\
-
-
- E:\
-
-
- C:\virtio\amd64\w10
-
-
- C:\virtio\NetKVM\w10\amd64
-
-
- C:\virtio\qxldod\w10\amd64
-
-
-
-
-
-
-
-
-
- 1
- ${if efi then "EFI" else "Primary"}
- 300
-
-
- 2
- ${if efi then "MSR" else "Primary"}
- 16
-
-
- 3
- Primary
- true
-
-
-
-
- 1
- ${if efi then "FAT32" else "NTFS"}
-
- 1
-
-
- 2
- 2
-
-
- 3
- NTFS
-
- C
- 3
-
-
- ${toString diskId}
- true
-
-
-
-
-
-
- ${toString diskId}
- 3
-
-
-
- /IMAGE/NAME
- ${imageSelection}
-
-
-
-
-
-
-
- ${if productKey != null then "${productKey}" else ""}
- OnError
-
- true
- ${fullName}
- ${organization}
-
-
-
-
-
- ${uiLanguage}
-
- ${inputLocale}
- ${systemLocale}
- ${uiLanguage}
- en-US
- ${userLocale}
-
-
-
-
-
- ${inputLocale}
- ${systemLocale}
- ${uiLanguage}
- en-US
- ${userLocale}
-
-
-
- true
- true
- true
- true
- true
- 1
-
- ${timeZone}
-
-
- ${if administratorPassword != null then ''
-
- ${administratorPassword}
- true
-
- '' else ""}
-
- ${builtins.concatStringsSep "\n" (builtins.map mkUser flatUsers)}
-
-
-
- ${if defaultUser == null then "" else ''
-
-
- ${(builtins.getAttr defaultUser users).password}
- true
-
- true
- ${defaultUser}
-
- ''}
-
-
-
-
- true
- OOBE
-
-
-
-
-
-
-
- ${lib.concatStringsSep "\n" (mkCommands commands)}
-
-
-
- 0
-
-
-
-
-
-
- false
-
-
-
-
-
- '';
-
-in {
- # Lint and format as a sanity check
- autounattendXML = pkgs.runCommandNoCC "autounattend.xml" {} ''
- ${pkgs.libxml2}/bin/xmllint --format ${autounattendXML} > $out
- '';
-
- # autounattend.xml is _super_ picky about quotes and other things
- setupScript = pkgs.writeText "setup.ps1" (
- ''
- # Setup SSH and keys
- '' +
- lib.concatStrings (
- builtins.map (c: ''
- # ${c.Description}
- ${c.Path}
- '') sshSetupCommands
- )
- );
-
-}