/dev/null 2>/dev/null || ! ip -6 r g :: >/dev/null 2>/dev/null; then echo "n"; else echo "y"; fi)
15 | hasTar=$(has tar)
16 | hasCpio=$(has cpio)
17 | hasSudo=$(has sudo)
18 | hasDoas=$(has doas)
19 | hasWget=$(has wget)
20 | hasCurl=$(has curl)
21 | hasSetsid=$(has setsid)
22 | hasNixOSFacter=$(command -v nixos-facter >/dev/null && echo "y" || echo "n")
23 | FACTS
24 |
--------------------------------------------------------------------------------
/terraform/.envrc:
--------------------------------------------------------------------------------
1 | use flake .#terraform
2 |
--------------------------------------------------------------------------------
/terraform/.terraform-docs.yml:
--------------------------------------------------------------------------------
1 | output:
2 | mode: inject
3 | template: |-
4 |
5 | {{ .Content }}
6 |
7 |
--------------------------------------------------------------------------------
/terraform/README.md:
--------------------------------------------------------------------------------
1 | # NixOS-Anywhere Terraform Modules Overview
2 |
3 | The nixos-Anywhere terraform modules allow you to use Terraform for installing
4 | and updating NixOS. It simplifies the deployment process by integrating
5 | nixos-anywhere functionality.
6 |
7 | Here's a brief overview of each module:
8 |
9 | - **[All-in-One](all-in-one.md)**: This is a consolidated module that first
10 | installs NixOS using nixos-anywhere and then keeps it updated with
11 | nixos-rebuild. If you choose this, you won't need additional deployment tools
12 | like colmena.
13 | - **[Install](install.md)**: This module focuses solely on installing NixOS via
14 | nixos-anywhere.
15 | - **[NixOS-Rebuild](nixos-rebuild.md)**: Use this module to remotely update an
16 | existing NixOS machine using nixos-rebuild.
17 | - **[Nix-Build](nix-build.md)**: This is a handy helper module designed to build
18 | a flake attribute or an attribute from a nix file.
19 |
20 | For detailed information and usage examples, click on the respective module
21 | links above.
22 |
--------------------------------------------------------------------------------
/terraform/all-in-one.md:
--------------------------------------------------------------------------------
1 | # All-in-one
2 |
3 | Combines the install and nixos-rebuild module in one interface to install NixOS
4 | with nixos-anywhere and then keep it up-to-date with nixos-rebuild.
5 |
6 | ## Example
7 |
8 | ```hcl
9 | locals {
10 | ipv4 = "192.0.2.1"
11 | }
12 |
13 | module "deploy" {
14 | source = "github.com/nix-community/nixos-anywhere//terraform/all-in-one"
15 | # with flakes
16 | nixos_system_attr = ".#nixosConfigurations.mymachine.config.system.build.toplevel"
17 | nixos_partitioner_attr = ".#nixosConfigurations.mymachine.config.system.build.diskoScript"
18 | # without flakes
19 | # file can use (pkgs.nixos []) function from nixpkgs
20 | #file = "${path.module}/../.."
21 | #nixos_system_attr = "config.system.build.toplevel"
22 | #nixos_partitioner_attr = "config.system.build.diskoScript"
23 |
24 | target_host = local.ipv4
25 | # when instance id changes, it will trigger a reinstall
26 | instance_id = local.ipv4
27 | # useful if something goes wrong
28 | # debug_logging = true
29 | # build the closure on the remote machine instead of locally
30 | # build_on_remote = true
31 | # script is below
32 | extra_files_script = "${path.module}/decrypt-ssh-secrets.sh"
33 | disk_encryption_key_scripts = [{
34 | path = "/tmp/secret.key"
35 | # script is below
36 | script = "${path.module}/decrypt-zfs-key.sh"
37 | }]
38 | # Optional, arguments passed to special_args here will be available from a NixOS module in this example the `terraform` argument:
39 | # { terraform, ... }: {
40 | # networking.interfaces.enp0s3.ipv4.addresses = [{ address = terraform.ip; prefixLength = 24; }];
41 | # }
42 | # Note that this will means that your NixOS configuration will always depend on terraform!
43 | # Skip to `Pass data persistently to the NixOS` for an alternative approach
44 | #special_args = {
45 | # terraform = {
46 | # ip = "192.0.2.0"
47 | # }
48 | #}
49 | }
50 | ```
51 |
52 | _Note:_ You need to mark scripts as executable (`chmod +x`)
53 |
54 | ### ./decrypt-ssh-secrets.sh
55 |
56 | ```bash
57 | #!/usr/bin/env bash
58 |
59 | mkdir -p etc/ssh var/lib/secrets
60 |
61 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
62 |
63 | umask 0177
64 | sops --extract '["initrd_ssh_key"]' --decrypt "$SCRIPT_DIR/secrets.yaml" >./var/lib/secrets/initrd_ssh_key
65 |
66 | # restore umask
67 | umask 0022
68 |
69 | for keyname in ssh_host_rsa_key ssh_host_rsa_key.pub ssh_host_ed25519_key ssh_host_ed25519_key.pub; do
70 | if [[ $keyname == *.pub ]]; then
71 | umask 0133
72 | else
73 | umask 0177
74 | fi
75 | sops --extract '["'$keyname'"]' --decrypt "$SCRIPT_DIR/secrets.yaml" >"./etc/ssh/$keyname"
76 | done
77 | ```
78 |
79 | ### ./decrypt-zfs-key.sh
80 |
81 | ```bash
82 | #!/usr/bin/env bash
83 |
84 | set -euo pipefail
85 |
86 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
87 | cd "$SCRIPT_DIR"
88 | sops --extract '["zfs-key"]' --decrypt "$SCRIPT_DIR/secrets.yaml"
89 | ```
90 |
91 | ## See also
92 |
93 | - [nixos-wiki setup](https://github.com/NixOS/nixos-wiki-infra/blob/main/terraform/nixos-wiki/main.tf)
94 | for hetzner-cloud
95 |
96 | ## Pass data persistently to the NixOS
97 |
98 | This guide outlines how to pass data from Terraform to NixOS by generating a
99 | file during Terraform execution and including it in your NixOS configuration.
100 | This approach works well if your Terraform and NixOS configurations are stored
101 | in the same Git repository.
102 |
103 | ### Why Use This Method?
104 |
105 | This method provides a straightforward way to transfer values from Terraform to
106 | NixOS without relying on special_args.
107 |
108 | - **Advantages**:
109 | - You can continue to use nix build or nixos-rebuild to evaluate your
110 | configuration without interruption. Simplifies configuration management by
111 | centralizing state in a single repository.
112 | - **Disadvantages**:
113 | - Deploying new machines requires tracking additional state. Every time
114 | Terraform updates the JSON file, you'll need to commit these changes to your
115 | repository.
116 |
117 | ### Implementation
118 |
119 | Add the following snippet to your Terraform configuration to create and manage a
120 | JSON file containing the necessary variables for NixOS. This file will be
121 | automatically added to your Git repository, ensuring the data persists.
122 |
123 | Assuming you have your terraform and nixos configuration in the same git
124 | repository. You can use the following snippet to `git add` a file generated by
125 | `terraform` during execution to pass data from terraform to NixOS. These changes
126 | should be committed afterwards. This is an alternative over using
127 | `special_args`. Advantage: you can still use nix build or nixos-rebuild on your
128 | flake to evaluate your configuration. Disadvantage: Deploying new machines also
129 | means you need to track additional state and make additional commits whenever
130 | terraform updates the json file.
131 |
132 | ```hcl
133 | locals {
134 | nixos_vars_file = "nixos-vars.json" # Path to the JSON file containing NixOS variables
135 | nixos_vars = {
136 | ip = "192.0.2.0" # Replace with actual variables
137 | }
138 | }
139 | resource "local_file" "nixos_vars" {
140 | content = jsonencode(local.nixos_vars) # Converts variables to JSON
141 | filename = local.nixos_vars_file # Specifies the output file path
142 | file_permission = "600"
143 |
144 | # Automatically adds the generated file to Git
145 | provisioner "local-exec" {
146 | interpreter = ["bash", "-c"]
147 | command = "git add -f '${local.nixos_vars_file}'"
148 | }
149 | }
150 | ```
151 |
152 | After applying the Terraform changes, ensure you commit the updated
153 | `nixos-vars.json` file to your Git repository:
154 |
155 | ```bash
156 | git commit -m "Update NixOS variables from Terraform"
157 | ```
158 |
159 | You can import this json file into your configuration like this:
160 |
161 | ```nix
162 | let
163 | nixosVars = builtins.fromJSON (builtins.readFile ./nixos-vars.json);
164 | in
165 | {
166 | # Example usage of imported variables
167 | networking.hostName = "example-machine";
168 | networking.interfaces.eth0.ipv4.addresses = [
169 | {
170 | address = nixosVars.ip; # Use the IP from nixos-vars.json
171 | prefixLength = 24;
172 | }
173 | ];
174 | }
175 | ```
176 |
177 |
178 |
179 | ## Requirements
180 |
181 | No requirements.
182 |
183 | ## Providers
184 |
185 | No providers.
186 |
187 | ## Modules
188 |
189 | | Name | Source | Version |
190 | | -------------------------------------------------------------------------------------- | ---------------- | ------- |
191 | | [install](#module_install) | ../install | n/a |
192 | | [nixos-rebuild](#module_nixos-rebuild) | ../nixos-rebuild | n/a |
193 | | [partitioner-build](#module_partitioner-build) | ../nix-build | n/a |
194 | | [system-build](#module_system-build) | ../nix-build | n/a |
195 |
196 | ## Resources
197 |
198 | No resources.
199 |
200 | ## Inputs
201 |
202 | | Name | Description | Type | Default | Required |
203 | | --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | :------: |
204 | | [build\_on\_remote](#input_build_on_remote) | Build the closure on the remote machine instead of building it locally and copying it over | `bool` | `false` | no |
205 | | [debug\_logging](#input_debug_logging) | Enable debug logging | `bool` | `false` | no |
206 | | [deployment\_ssh\_key](#input_deployment_ssh_key) | Content of private key used to deploy to the target\_host after initial installation. To ensure maximum security, it is advisable to connect to your host using ssh-agent instead of relying on this variable | `string` | `null` | no |
207 | | [disk\_encryption\_key\_scripts](#input_disk_encryption_key_scripts) | Each script will be executed locally. Output of each will be created at the given path to disko during installation. The keys will be not copied to the final system | list(object({
path = string
script = string
}))
| `[]` | no |
208 | | [extra\_environment](#input_extra_environment) | Extra environment variables to be set during installation. This can be useful to set extra variables for the extra\_files\_script or disk\_encryption\_key\_scripts | `map(string)` | `{}` | no |
209 | | [extra\_files\_script](#input_extra_files_script) | A script that should place files in the current directory that will be copied to the targets / directory | `string` | `null` | no |
210 | | [file](#input_file) | Nix file containing the nixos\_system\_attr and nixos\_partitioner\_attr. Use this if you are not using flake | `string` | `null` | no |
211 | | [install\_bootloader](#input_install_bootloader) | Install/re-install the bootloader | `bool` | `false` | no |
212 | | [install\_port](#input_install_port) | SSH port used to connect to the target\_host, before installing NixOS. If null than the value of `target_port` is used | `string` | `null` | no |
213 | | [install\_ssh\_key](#input_install_ssh_key) | Content of private key used to connect to the target\_host during initial installation | `string` | `null` | no |
214 | | [install\_user](#input_install_user) | SSH user used to connect to the target\_host, before installing NixOS. If null than the value of `target_host` is used | `string` | `null` | no |
215 | | [instance\_id](#input_instance_id) | The instance id of the target\_host, used to track when to reinstall the machine | `string` | `null` | no |
216 | | [kexec\_tarball\_url](#input_kexec_tarball_url) | NixOS kexec installer tarball url | `string` | `null` | no |
217 | | [nix\_options](#input_nix_options) | the options of nix | `map(string)` | `{}` | no |
218 | | [nixos\_facter\_path](#input_nixos_facter_path) | Path to which to write a `facter.json` generated by `nixos-facter`. | `string` | `""` | no |
219 | | [nixos\_generate\_config\_path](#input_nixos_generate_config_path) | Path to which to write a `hardware-configuration.nix` generated by `nixos-generate-config`. | `string` | `""` | no |
220 | | [nixos\_partitioner\_attr](#input_nixos_partitioner_attr) | Nixos partitioner and mount script i.e. your-flake#nixosConfigurations.your-evaluated-nixos.config.system.build.diskoNoDeps or just your-evaluated.config.system.build.diskNoDeps. `config.system.build.diskNoDeps` is provided by the disko nixos module | `string` | n/a | yes |
221 | | [nixos\_system\_attr](#input_nixos_system_attr) | The nixos system to deploy i.e. your-flake#nixosConfigurations.your-evaluated-nixos.config.system.build.toplevel or just your-evaluated-nixos.config.system.build.toplevel if you are not using flakes | `string` | n/a | yes |
222 | | [no\_reboot](#input_no_reboot) | DEPRECATED: Use `phases` instead. Do not reboot after installation | `bool` | `false` | no |
223 | | [phases](#input_phases) | Phases to run. See `nixos-anywhere --help` for more information | `set(string)` | [
"kexec",
"disko",
"install",
"reboot"
]
| no |
224 | | [special\_args](#input_special_args) | A map exposed as NixOS's `specialArgs` thru a file. | `any` | `{}` | no |
225 | | [stop\_after\_disko](#input_stop_after_disko) | DEPRECATED: Use `phases` instead. Exit after disko formatting | `bool` | `false` | no |
226 | | [target\_host](#input_target_host) | DNS host to deploy to | `string` | n/a | yes |
227 | | [target\_port](#input_target_port) | SSH port used to connect to the target\_host after installing NixOS. If install\_port is not set than this port is also used before installing. | `number` | `22` | no |
228 | | [target\_user](#input_target_user) | SSH user used to connect to the target\_host after installing NixOS. If install\_user is not set than this user is also used before installing. | `string` | `"root"` | no |
229 |
230 | ## Outputs
231 |
232 | | Name | Description |
233 | | ----------------------------------------------------- | ----------- |
234 | | [result](#output_result) | n/a |
235 |
236 |
237 |
--------------------------------------------------------------------------------
/terraform/all-in-one/main.tf:
--------------------------------------------------------------------------------
1 | module "system-build" {
2 | source = "../nix-build"
3 | attribute = var.nixos_system_attr
4 | debug_logging = var.debug_logging
5 | file = var.file
6 | nix_options = var.nix_options
7 | special_args = var.special_args
8 | }
9 |
10 | module "partitioner-build" {
11 | source = "../nix-build"
12 | attribute = var.nixos_partitioner_attr
13 | debug_logging = var.debug_logging
14 | file = var.file
15 | nix_options = var.nix_options
16 | special_args = var.special_args
17 | }
18 |
19 | locals {
20 | install_user = var.install_user == null ? var.target_user : var.install_user
21 | install_port = var.install_port == null ? var.target_port : var.install_port
22 | }
23 |
24 | module "install" {
25 | source = "../install"
26 | kexec_tarball_url = var.kexec_tarball_url
27 | target_user = local.install_user
28 | target_host = var.target_host
29 | target_port = local.install_port
30 | nixos_partitioner = module.partitioner-build.result.out
31 | nixos_system = module.system-build.result.out
32 | ssh_private_key = var.install_ssh_key
33 | debug_logging = var.debug_logging
34 | extra_files_script = var.extra_files_script
35 | disk_encryption_key_scripts = var.disk_encryption_key_scripts
36 | extra_environment = var.extra_environment
37 | instance_id = var.instance_id
38 | phases = var.phases
39 | nixos_generate_config_path = var.nixos_generate_config_path
40 | nixos_facter_path = var.nixos_facter_path
41 | build_on_remote = var.build_on_remote
42 | # deprecated attributes
43 | stop_after_disko = var.stop_after_disko
44 | no_reboot = var.no_reboot
45 | }
46 |
47 | module "nixos-rebuild" {
48 | depends_on = [
49 | module.install
50 | ]
51 |
52 | # Do not execute this step if var.stop_after_disko == true
53 | count = var.stop_after_disko ? 0 : 1
54 |
55 | source = "../nixos-rebuild"
56 | nixos_system = module.system-build.result.out
57 | ssh_private_key = var.deployment_ssh_key
58 | target_host = var.target_host
59 | target_user = var.target_user
60 | target_port = var.target_port
61 | install_bootloader = var.install_bootloader
62 | }
63 |
64 | output "result" {
65 | value = module.system-build.result
66 | }
67 |
--------------------------------------------------------------------------------
/terraform/all-in-one/variables.tf:
--------------------------------------------------------------------------------
1 | variable "kexec_tarball_url" {
2 | type = string
3 | description = "NixOS kexec installer tarball url"
4 | default = null
5 | }
6 |
7 | # To make this re-usable we maybe should accept a store path here?
8 | variable "nixos_partitioner_attr" {
9 | type = string
10 | description = "Nixos partitioner and mount script i.e. your-flake#nixosConfigurations.your-evaluated-nixos.config.system.build.diskoNoDeps or just your-evaluated.config.system.build.diskNoDeps. `config.system.build.diskNoDeps` is provided by the disko nixos module"
11 | }
12 |
13 | # To make this re-usable we maybe should accept a store path here?
14 | variable "nixos_system_attr" {
15 | type = string
16 | description = "The nixos system to deploy i.e. your-flake#nixosConfigurations.your-evaluated-nixos.config.system.build.toplevel or just your-evaluated-nixos.config.system.build.toplevel if you are not using flakes"
17 | }
18 |
19 | variable "file" {
20 | type = string
21 | description = "Nix file containing the nixos_system_attr and nixos_partitioner_attr. Use this if you are not using flake"
22 | default = null
23 | }
24 |
25 | variable "target_host" {
26 | type = string
27 | description = "DNS host to deploy to"
28 | }
29 |
30 | variable "install_user" {
31 | type = string
32 | description = "SSH user used to connect to the target_host, before installing NixOS. If null than the value of `target_host` is used"
33 | default = null
34 | }
35 |
36 | variable "install_port" {
37 | type = string
38 | description = "SSH port used to connect to the target_host, before installing NixOS. If null than the value of `target_port` is used"
39 | default = null
40 | }
41 |
42 | variable "target_user" {
43 | type = string
44 | description = "SSH user used to connect to the target_host after installing NixOS. If install_user is not set than this user is also used before installing."
45 | default = "root"
46 | }
47 |
48 | variable "target_port" {
49 | type = number
50 | description = "SSH port used to connect to the target_host after installing NixOS. If install_port is not set than this port is also used before installing."
51 | default = 22
52 | }
53 |
54 | variable "instance_id" {
55 | type = string
56 | description = "The instance id of the target_host, used to track when to reinstall the machine"
57 | default = null
58 | }
59 |
60 | variable "install_ssh_key" {
61 | type = string
62 | description = "Content of private key used to connect to the target_host during initial installation"
63 | default = null
64 | }
65 |
66 | variable "deployment_ssh_key" {
67 | type = string
68 | description = "Content of private key used to deploy to the target_host after initial installation. To ensure maximum security, it is advisable to connect to your host using ssh-agent instead of relying on this variable"
69 | default = null
70 | }
71 |
72 | variable "debug_logging" {
73 | type = bool
74 | description = "Enable debug logging"
75 | default = false
76 | }
77 |
78 | variable "stop_after_disko" {
79 | type = bool
80 | description = "DEPRECATED: Use `phases` instead. Exit after disko formatting"
81 | default = false
82 | }
83 |
84 | variable "no_reboot" {
85 | type = bool
86 | description = "DEPRECATED: Use `phases` instead. Do not reboot after installation"
87 | default = false
88 | }
89 |
90 | variable "phases" {
91 | type = set(string)
92 | description = "Phases to run. See `nixos-anywhere --help` for more information"
93 | default = ["kexec", "disko", "install", "reboot"]
94 | }
95 |
96 | variable "extra_files_script" {
97 | type = string
98 | description = "A script that should place files in the current directory that will be copied to the targets / directory"
99 | default = null
100 | }
101 |
102 | variable "disk_encryption_key_scripts" {
103 | type = list(object({
104 | path = string
105 | script = string
106 | }))
107 | description = "Each script will be executed locally. Output of each will be created at the given path to disko during installation. The keys will be not copied to the final system"
108 | default = []
109 | }
110 |
111 | variable "extra_environment" {
112 | type = map(string)
113 | description = "Extra environment variables to be set during installation. This can be useful to set extra variables for the extra_files_script or disk_encryption_key_scripts"
114 | default = {}
115 | }
116 |
117 | variable "nix_options" {
118 | type = map(string)
119 | description = "the options of nix"
120 | default = {}
121 | }
122 |
123 | variable "nixos_generate_config_path" {
124 | type = string
125 | description = "Path to which to write a `hardware-configuration.nix` generated by `nixos-generate-config`."
126 | default = ""
127 | }
128 |
129 | variable "nixos_facter_path" {
130 | type = string
131 | description = "Path to which to write a `facter.json` generated by `nixos-facter`."
132 | default = ""
133 | }
134 |
135 | variable "special_args" {
136 | type = any
137 | default = {}
138 | description = "A map exposed as NixOS's `specialArgs` thru a file."
139 | }
140 |
141 | variable "build_on_remote" {
142 | type = bool
143 | description = "Build the closure on the remote machine instead of building it locally and copying it over"
144 | default = false
145 | }
146 |
147 | variable "install_bootloader" {
148 | type = bool
149 | description = "Install/re-install the bootloader"
150 | default = false
151 | }
152 |
--------------------------------------------------------------------------------
/terraform/flake-module.nix:
--------------------------------------------------------------------------------
1 | { inputs, ... }:
2 | {
3 | flake.nixosConfigurations.terraform-test = inputs.nixpkgs.lib.nixosSystem {
4 | system = "x86_64-linux";
5 | modules = [
6 | ../tests/modules/system-to-install.nix
7 | inputs.disko.nixosModules.disko
8 | (args: {
9 | # Example usage of special args from terraform
10 | networking.hostName = args.terraform.hostname or "nixos-anywhere";
11 |
12 | # Create testable files in /etc based on terraform special_args
13 | environment.etc = {
14 | "terraform-config.json" = {
15 | text = builtins.toJSON args.terraform or { };
16 | mode = "0644";
17 | };
18 | };
19 | })
20 | ];
21 | };
22 |
23 | perSystem = { pkgs, ... }: {
24 | devShells.terraform = pkgs.mkShell {
25 | buildInputs = with pkgs; [
26 | terraform-docs
27 | (opentofu.withPlugins (p: [
28 | p.digitalocean
29 | p.external
30 | p.hcloud
31 | p.local
32 | p.null
33 | p.tls
34 | ]))
35 | ];
36 |
37 | shellHook = ''
38 | echo "🚀 Terraform development environment"
39 | echo "Available tools:"
40 | echo " - terraform-docs"
41 | echo " - opentofu"
42 | echo ""
43 | echo "To run tests: cd terraform/tests && tofu test"
44 | echo "To update docs: cd terraform && ./update-docs.sh"
45 | '';
46 | };
47 | };
48 | }
49 |
--------------------------------------------------------------------------------
/terraform/install.md:
--------------------------------------------------------------------------------
1 | # Install
2 |
3 | Install NixOS with nixos-anywhere
4 |
5 | ## Example
6 |
7 | ```hcl
8 | locals {
9 | ipv4 = "192.0.2.1"
10 | }
11 |
12 | module "system-build" {
13 | source = "github.com/nix-community/nixos-anywhere//terraform/nix-build"
14 | # with flakes
15 | attribute = ".#nixosConfigurations.mymachine.config.system.build.toplevel"
16 | # without flakes
17 | # file can use (pkgs.nixos []) function from nixpkgs
18 | #file = "${path.module}/../.."
19 | #attribute = "config.system.build.toplevel"
20 | }
21 |
22 | module "disko" {
23 | source = "github.com/nix-community/nixos-anywhere//terraform/nix-build"
24 | # with flakes
25 | attribute = ".#nixosConfigurations.mymachine.config.system.build.diskoScript"
26 | # without flakes
27 | # file can use (pkgs.nixos []) function from nixpkgs
28 | #file = "${path.module}/../.."
29 | #attribute = "config.system.build.diskoScript"
30 | }
31 |
32 | module "install" {
33 | source = "github.com/nix-community/nixos-anywhere//terraform/install"
34 | nixos_system = module.system-build.result.out
35 | nixos_partitioner = module.disko.result.out
36 | target_host = local.ipv4
37 | }
38 | ```
39 |
40 |
41 |
42 | ## Requirements
43 |
44 | No requirements.
45 |
46 | ## Providers
47 |
48 | | Name | Version |
49 | | --------------------------------------------------- | ------- |
50 | | [null](#provider_null) | n/a |
51 |
52 | ## Modules
53 |
54 | No modules.
55 |
56 | ## Resources
57 |
58 | | Name | Type |
59 | | ------------------------------------------------------------------------------------------------------------------- | -------- |
60 | | [null_resource.nixos-remote](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
61 |
62 | ## Inputs
63 |
64 | | Name | Description | Type | Default | Required |
65 | | --------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | :------: |
66 | | [build\_on\_remote](#input_build_on_remote) | Build the closure on the remote machine instead of building it locally and copying it over | `bool` | `false` | no |
67 | | [debug\_logging](#input_debug_logging) | Enable debug logging | `bool` | `false` | no |
68 | | [disk\_encryption\_key\_scripts](#input_disk_encryption_key_scripts) | Each script will be executed locally. Output of each will be created at the given path to disko during installation. The keys will be not copied to the final system | list(object({
path = string
script = string
}))
| `[]` | no |
69 | | [extra\_environment](#input_extra_environment) | Extra environment variables to be set during installation. This can be useful to set extra variables for the extra\_files\_script or disk\_encryption\_key\_scripts | `map(string)` | `{}` | no |
70 | | [extra\_files\_script](#input_extra_files_script) | A script that should place files in the current directory that will be copied to the targets / directory | `string` | `null` | no |
71 | | [flake](#input_flake) | The flake to install the system from | `string` | `""` | no |
72 | | [instance\_id](#input_instance_id) | The instance id of the target\_host, used to track when to reinstall the machine | `string` | `null` | no |
73 | | [kexec\_tarball\_url](#input_kexec_tarball_url) | NixOS kexec installer tarball url | `string` | `null` | no |
74 | | [nixos\_facter\_path](#input_nixos_facter_path) | Path to which to write a `facter.json` generated by `nixos-facter`. This option cannot be set at the same time as `nixos_generate_config_path`. | `string` | `""` | no |
75 | | [nixos\_generate\_config\_path](#input_nixos_generate_config_path) | Path to which to write a `hardware-configuration.nix` generated by `nixos-generate-config`. This option cannot be set at the same time as `nixos_facter_path`. | `string` | `""` | no |
76 | | [nixos\_partitioner](#input_nixos_partitioner) | nixos partitioner and mount script | `string` | `""` | no |
77 | | [nixos\_system](#input_nixos_system) | The nixos system to deploy | `string` | `""` | no |
78 | | [no\_reboot](#input_no_reboot) | DEPRECATED: Use `phases` instead. Do not reboot after installation | `bool` | `false` | no |
79 | | [phases](#input_phases) | Phases to run. See `nixos-anywhere --help` for more information | `list(string)` | [
"kexec",
"disko",
"install",
"reboot"
]
| no |
80 | | [ssh\_private\_key](#input_ssh_private_key) | Content of private key used to connect to the target\_host | `string` | `""` | no |
81 | | [stop\_after\_disko](#input_stop_after_disko) | DEPRECATED: Use `phases` instead. Exit after disko formatting | `bool` | `false` | no |
82 | | [target\_host](#input_target_host) | DNS host to deploy to | `string` | n/a | yes |
83 | | [target\_pass](#input_target_pass) | Password used to connect to the target\_host | `string` | `null` | no |
84 | | [target\_port](#input_target_port) | SSH port used to connect to the target\_host | `number` | `22` | no |
85 | | [target\_user](#input_target_user) | SSH user used to connect to the target\_host | `string` | `"root"` | no |
86 |
87 | ## Outputs
88 |
89 | No outputs.
90 |
91 |
92 |
--------------------------------------------------------------------------------
/terraform/install/main.tf:
--------------------------------------------------------------------------------
1 | locals {
2 | disk_encryption_key_scripts = [for k in var.disk_encryption_key_scripts : "\"${k.path}\" \"${k.script}\""]
3 | removed_phases = setunion(var.stop_after_disko ? ["install"] : [], (var.no_reboot ? ["reboot"] : []))
4 | phases = setsubtract(var.phases, local.removed_phases)
5 | arguments = jsonencode({
6 | ssh_private_key = var.ssh_private_key
7 | debug_logging = var.debug_logging
8 | kexec_tarball_url = var.kexec_tarball_url
9 | nixos_partitioner = var.nixos_partitioner
10 | nixos_system = var.nixos_system
11 | target_user = var.target_user
12 | target_host = var.target_host
13 | target_port = var.target_port
14 | target_pass = var.target_pass
15 | extra_files_script = var.extra_files_script
16 | build_on_remote = var.build_on_remote
17 | flake = var.flake
18 | phases = join(",", local.phases)
19 | nixos_generate_config_path = var.nixos_generate_config_path
20 | nixos_facter_path = var.nixos_facter_path
21 | })
22 | }
23 |
24 | resource "null_resource" "nixos-remote" {
25 | triggers = {
26 | instance_id = var.instance_id
27 | }
28 | provisioner "local-exec" {
29 | environment = merge({
30 | ARGUMENTS = local.arguments
31 | }, var.extra_environment)
32 | command = "${path.module}/run-nixos-anywhere.sh ${join(" ", local.disk_encryption_key_scripts)}"
33 | quiet = var.debug_logging
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/terraform/install/providers.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | null = { source = "hashicorp/null" }
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/terraform/install/run-nixos-anywhere.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 |
4 | SCRIPT_DIR="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
5 |
6 | declare -A input
7 |
8 | while IFS= read -r -d '' key && IFS= read -r -d '' value; do
9 | input[$key]=$value
10 | done < <(jq -j 'to_entries[] | (.key, "\u0000", .value, "\u0000")' <<<"${ARGUMENTS}")
11 |
12 | args=()
13 |
14 | if [[ ${input[debug_logging]} == "true" ]]; then
15 | set -x
16 | declare -p input
17 | args+=("--debug")
18 | fi
19 | if [[ ${input[kexec_tarball_url]} != "null" ]]; then
20 | args+=("--kexec" "${input[kexec_tarball_url]}")
21 | fi
22 | if [[ ${input[build_on_remote]} == "true" ]]; then
23 | args+=("--build-on-remote")
24 | fi
25 | if [[ -n ${input[flake]} ]]; then
26 | args+=("--flake" "${input[flake]}")
27 | else
28 | args+=("--store-paths" "${input[nixos_partitioner]}" "${input[nixos_system]}")
29 | fi
30 | if [[ -n ${input[nixos_generate_config_path]} ]]; then
31 | if [[ -n ${input[nixos_facter_path]} ]]; then
32 | echo "cannot set both variables 'nixos_generate_config_path' and 'nixos_facter_path'!" >&2
33 | exit 1
34 | fi
35 | args+=("--generate-hardware-config" "nixos-generate-config" "${input[nixos_generate_config_path]}")
36 | elif [[ -n ${input[nixos_facter_path]} ]]; then
37 | args+=("--generate-hardware-config" "nixos-facter" "${input[nixos_facter_path]}")
38 | fi
39 | args+=(--phases "${input[phases]}")
40 | if [[ ${input[ssh_private_key]} != null ]]; then
41 | export SSH_PRIVATE_KEY="${input[ssh_private_key]}"
42 | fi
43 | if [[ ${input[target_pass]} != null ]]; then
44 | export SSHPASS=${input[target_pass]}
45 | args+=("--env-password")
46 | fi
47 |
48 | tmpdir=$(mktemp -d)
49 | cleanup() {
50 | rm -rf "${tmpdir}"
51 | }
52 | trap cleanup EXIT
53 |
54 | if [[ ${input[extra_files_script]} != "null" ]]; then
55 | if [[ ! -f ${input[extra_files_script]} ]]; then
56 | echo "extra_files_script '${input[extra_files_script]}' does not exist"
57 | exit 1
58 | fi
59 | if [[ ! -x ${input[extra_files_script]} ]]; then
60 | echo "extra_files_script '${input[extra_files_script]}' is not executable"
61 | exit 1
62 | fi
63 | extra_files_script=$(realpath "${input[extra_files_script]}")
64 | mkdir "${tmpdir}/extra-files"
65 | pushd "${tmpdir}/extra-files"
66 | $extra_files_script
67 | popd
68 | args+=("--extra-files" "${tmpdir}/extra-files")
69 | fi
70 |
71 | args+=("-p" "${input[target_port]}")
72 | args+=("${input[target_user]}@${input[target_host]}")
73 |
74 | keyIdx=0
75 | while [[ $# -gt 0 ]]; do
76 | if [[ ! -f $2 ]]; then
77 | echo "Script file '$2' does not exist"
78 | exit 1
79 | fi
80 | if [[ ! -x $2 ]]; then
81 | echo "Script file '$2' is not executable"
82 | exit 1
83 | fi
84 | mkdir -p "${tmpdir}/keys"
85 | "$2" >"${tmpdir}/keys/$keyIdx"
86 | args+=("--disk-encryption-keys" "$1" "${tmpdir}/keys/$keyIdx")
87 | shift
88 | shift
89 | keyIdx=$((keyIdx + 1))
90 | done
91 |
92 | nix run --extra-experimental-features 'nix-command flakes' "path:${SCRIPT_DIR}/../..#nixos-anywhere" -- "${args[@]}"
93 |
--------------------------------------------------------------------------------
/terraform/install/variables.tf:
--------------------------------------------------------------------------------
1 | variable "kexec_tarball_url" {
2 | type = string
3 | description = "NixOS kexec installer tarball url"
4 | default = null
5 | }
6 |
7 | # To make this re-usable we maybe should accept a store path here?
8 | variable "nixos_partitioner" {
9 | type = string
10 | description = "nixos partitioner and mount script"
11 | default = ""
12 | }
13 |
14 | # To make this re-usable we maybe should accept a store path here?
15 | variable "nixos_system" {
16 | type = string
17 | description = "The nixos system to deploy"
18 | default = ""
19 | }
20 |
21 | variable "target_host" {
22 | type = string
23 | description = "DNS host to deploy to"
24 | }
25 |
26 | variable "target_user" {
27 | type = string
28 | description = "SSH user used to connect to the target_host"
29 | default = "root"
30 | }
31 |
32 | variable "target_port" {
33 | type = number
34 | description = "SSH port used to connect to the target_host"
35 | default = 22
36 | }
37 |
38 | variable "target_pass" {
39 | type = string
40 | description = "Password used to connect to the target_host"
41 | default = null
42 | }
43 |
44 | variable "ssh_private_key" {
45 | type = string
46 | description = "Content of private key used to connect to the target_host"
47 | default = ""
48 | }
49 |
50 | variable "instance_id" {
51 | type = string
52 | description = "The instance id of the target_host, used to track when to reinstall the machine"
53 | default = null
54 | }
55 |
56 | variable "debug_logging" {
57 | type = bool
58 | description = "Enable debug logging"
59 | default = false
60 | }
61 |
62 | variable "extra_files_script" {
63 | type = string
64 | description = "A script that should place files in the current directory that will be copied to the targets / directory"
65 | default = null
66 | }
67 |
68 | variable "disk_encryption_key_scripts" {
69 | type = list(object({
70 | path = string
71 | script = string
72 | }))
73 | description = "Each script will be executed locally. Output of each will be created at the given path to disko during installation. The keys will be not copied to the final system"
74 | default = []
75 | }
76 |
77 | variable "extra_environment" {
78 | type = map(string)
79 | description = "Extra environment variables to be set during installation. This can be useful to set extra variables for the extra_files_script or disk_encryption_key_scripts"
80 | default = {}
81 | }
82 |
83 | variable "stop_after_disko" {
84 | type = bool
85 | description = "DEPRECATED: Use `phases` instead. Exit after disko formatting"
86 | default = false
87 | }
88 |
89 | variable "no_reboot" {
90 | type = bool
91 | description = "DEPRECATED: Use `phases` instead. Do not reboot after installation"
92 | default = false
93 | }
94 |
95 | variable "phases" {
96 | type = list(string)
97 | description = "Phases to run. See `nixos-anywhere --help` for more information"
98 | default = ["kexec", "disko", "install", "reboot"]
99 | }
100 |
101 | variable "build_on_remote" {
102 | type = bool
103 | description = "Build the closure on the remote machine instead of building it locally and copying it over"
104 | default = false
105 | }
106 |
107 | variable "flake" {
108 | type = string
109 | description = "The flake to install the system from"
110 | default = ""
111 | }
112 |
113 | variable "nixos_generate_config_path" {
114 | type = string
115 | description = "Path to which to write a `hardware-configuration.nix` generated by `nixos-generate-config`. This option cannot be set at the same time as `nixos_facter_path`."
116 | default = ""
117 | }
118 |
119 | variable "nixos_facter_path" {
120 | type = string
121 | description = "Path to which to write a `facter.json` generated by `nixos-facter`. This option cannot be set at the same time as `nixos_generate_config_path`."
122 | default = ""
123 | }
124 |
--------------------------------------------------------------------------------
/terraform/nix-build.md:
--------------------------------------------------------------------------------
1 | # Nix-build
2 |
3 | Small helper module to run do build a flake attribute or attribute from a nix
4 | file.
5 |
6 | ## Example
7 |
8 | - See [install](install.md) or [nixos-rebuild](nixos-rebuild.md)
9 |
10 |
11 |
12 | ## Requirements
13 |
14 | No requirements.
15 |
16 | ## Providers
17 |
18 | | Name | Version |
19 | | --------------------------------------------------------------- | ------- |
20 | | [external](#provider_external) | n/a |
21 |
22 | ## Modules
23 |
24 | No modules.
25 |
26 | ## Resources
27 |
28 | | Name | Type |
29 | | --------------------------------------------------------------------------------------------------------------------------- | ----------- |
30 | | [external_external.nix-build](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/external) | data source |
31 |
32 | ## Inputs
33 |
34 | | Name | Description | Type | Default | Required |
35 | | ------------------------------------------------------------------------- | --------------------------------------------------- | ------------- | ------- | :------: |
36 | | [attribute](#input_attribute) | the attribute to build, can also be a flake | `string` | n/a | yes |
37 | | [debug\_logging](#input_debug_logging) | Enable debug logging | `bool` | `false` | no |
38 | | [file](#input_file) | the nix file to evaluate, if not run in flake mode | `string` | `null` | no |
39 | | [nix\_options](#input_nix_options) | the options of nix | `map(string)` | `{}` | no |
40 | | [special\_args](#input_special_args) | A map exposed as NixOS's `specialArgs` thru a file. | `any` | `{}` | no |
41 |
42 | ## Outputs
43 |
44 | | Name | Description |
45 | | ----------------------------------------------------- | ----------- |
46 | | [result](#output_result) | n/a |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/terraform/nix-build/main.tf:
--------------------------------------------------------------------------------
1 | locals {
2 | nix_options = jsonencode({
3 | options = { for k, v in var.nix_options : k => v }
4 | })
5 | }
6 | data "external" "nix-build" {
7 | program = ["${path.module}/nix-build.sh"]
8 | query = {
9 | attribute = var.attribute
10 | file = var.file
11 | nix_options = local.nix_options
12 | debug_logging = var.debug_logging
13 | special_args = jsonencode(var.special_args)
14 | }
15 | }
16 | output "result" {
17 | value = data.external.nix-build.result
18 | }
19 |
--------------------------------------------------------------------------------
/terraform/nix-build/nix-build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -efu
3 |
4 | declare file attribute nix_options special_args debug_logging
5 | eval "$(jq -r '@sh "attribute=\(.attribute) file=\(.file) nix_options=\(.nix_options) special_args=\(.special_args) debug_logging=\(.debug_logging)"')"
6 | if [ "${debug_logging}" = "true" ]; then
7 | set -x
8 | fi
9 | if [ "${nix_options}" != '{"options":{}}' ]; then
10 | options=$(echo "${nix_options}" | jq -r '.options | to_entries | map("--option \(.key) \(.value)") | join(" ")')
11 | else
12 | options=""
13 | fi
14 | if [[ ${special_args-} == "{}" ]]; then
15 | # no special arguments, proceed as normal
16 | if [[ -n ${file-} ]] && [[ -e ${file-} ]]; then
17 | # shellcheck disable=SC2086
18 | out=$(nix build --no-link --json $options -f "$file" "$attribute")
19 | else
20 | # shellcheck disable=SC2086
21 | out=$(nix build --no-link --json ${options} "$attribute")
22 | fi
23 | else
24 | if [[ ${file-} != 'null' ]]; then
25 | echo "special_args are currently only supported when using flakes!" >&2
26 | exit 1
27 | fi
28 | # pass the args in a pure fashion by extending the original config
29 | rest="$(echo "${attribute}" | cut -d "#" -f 2)"
30 | # e.g. config_path=nixosConfigurations.aarch64-linux.myconfig
31 | config_path="${rest%.config.*}"
32 | # e.g. config_attribute=config.system.build.toplevel
33 | config_attribute="config.${rest#*.config.}"
34 |
35 | # grab flake nar from error message
36 | flake_rel="$(echo "${attribute}" | cut -d "#" -f 1)"
37 |
38 | # Use nix flake prefetch to get the flake into the store, then use path:// URL with narHash
39 | prefetch_result="$(nix flake prefetch "${flake_rel}" --json)"
40 | store_path="$(echo "${prefetch_result}" | jq -r '.storePath')"
41 | nar_hash="$(echo "${prefetch_result}" | jq -r '.hash')"
42 | flake_url="path:${store_path}?narHash=${nar_hash}"
43 |
44 | # substitute variables into the template
45 | nix_expr="(builtins.getFlake ''${flake_url}'').${config_path}.extendModules { specialArgs = builtins.fromJSON ''${special_args}''; }"
46 | # inject `special_args` into nixos config's `specialArgs`
47 |
48 | # shellcheck disable=SC2086
49 | out=$(nix build --no-link --json ${options} --expr "${nix_expr}" "${config_attribute}")
50 | fi
51 | printf '%s' "$out" | jq -c '.[].outputs'
52 |
--------------------------------------------------------------------------------
/terraform/nix-build/variables.tf:
--------------------------------------------------------------------------------
1 | variable "attribute" {
2 | type = string
3 | description = "the attribute to build, can also be a flake"
4 | }
5 |
6 | variable "file" {
7 | type = string
8 | description = "the nix file to evaluate, if not run in flake mode"
9 | default = null
10 | }
11 |
12 | variable "nix_options" {
13 | type = map(string)
14 | description = "the options of nix"
15 | default = {}
16 | }
17 |
18 | variable "special_args" {
19 | type = any
20 | default = {}
21 | description = "A map exposed as NixOS's `specialArgs` thru a file."
22 | }
23 |
24 | variable "debug_logging" {
25 | type = bool
26 | default = false
27 | description = "Enable debug logging"
28 | }
29 |
--------------------------------------------------------------------------------
/terraform/nixos-rebuild.md:
--------------------------------------------------------------------------------
1 | # Nixos-rebuild
2 |
3 | Update NixOS machine with nixos-rebuild on a remote machine
4 |
5 | ## Example
6 |
7 | ```hcl
8 | locals {
9 | ipv4 = "192.0.2.1"
10 | }
11 |
12 | module "system-build" {
13 | source = "github.com/nix-community/nixos-anywhere//terraform/nix-build"
14 | # with flakes
15 | attribute = ".#nixosConfigurations.mymachine.config.system.build.toplevel"
16 | # without flakes
17 | # file can use (pkgs.nixos []) function from nixpkgs
18 | #file = "${path.module}/../.."
19 | #attribute = "config.system.build.toplevel"
20 | }
21 |
22 | module "deploy" {
23 | source = "github.com/nix-community/nixos-anywhere//terraform/nixos-rebuild"
24 | nixos_system = module.system-build.result.out
25 | target_host = local.ipv4
26 | }
27 | ```
28 |
29 |
30 |
31 | ## Requirements
32 |
33 | No requirements.
34 |
35 | ## Providers
36 |
37 | | Name | Version |
38 | | --------------------------------------------------- | ------- |
39 | | [null](#provider_null) | n/a |
40 |
41 | ## Modules
42 |
43 | No modules.
44 |
45 | ## Resources
46 |
47 | | Name | Type |
48 | | -------------------------------------------------------------------------------------------------------------------- | -------- |
49 | | [null_resource.nixos-rebuild](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
50 |
51 | ## Inputs
52 |
53 | | Name | Description | Type | Default | Required |
54 | | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -------- | -------- | :------: |
55 | | [ignore\_systemd\_errors](#input_ignore_systemd_errors) | Ignore systemd errors happening during deploy | `bool` | `false` | no |
56 | | [install\_bootloader](#input_install_bootloader) | Install/re-install the bootloader | `bool` | `false` | no |
57 | | [nixos\_system](#input_nixos_system) | The nixos system to deploy | `string` | n/a | yes |
58 | | [ssh\_private\_key](#input_ssh_private_key) | Content of private key used to connect to the target\_host. If set to - no key is passed to openssh and ssh will use its own configuration | `string` | `"-"` | no |
59 | | [target\_host](#input_target_host) | DNS host to deploy to | `string` | n/a | yes |
60 | | [target\_port](#input_target_port) | SSH port used to connect to the target\_host | `number` | `22` | no |
61 | | [target\_user](#input_target_user) | User to deploy as | `string` | `"root"` | no |
62 |
63 | ## Outputs
64 |
65 | No outputs.
66 |
67 |
68 |
--------------------------------------------------------------------------------
/terraform/nixos-rebuild/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -uex -o pipefail
4 |
5 | if [ "$#" -ne 6 ]; then
6 | echo "USAGE: $0 NIXOS_SYSTEM TARGET_USER TARGET_HOST TARGET_PORT IGNORE_SYSTEMD_ERRORS INSTALL_BOOTLOADER" >&2
7 | exit 1
8 | fi
9 |
10 | NIXOS_SYSTEM=$1
11 | TARGET_USER=$2
12 | TARGET_HOST=$3
13 | TARGET_PORT=$4
14 | IGNORE_SYSTEMD_ERRORS=$5
15 | INSTALL_BOOTLOADER=$6
16 |
17 | shift 6
18 |
19 | TARGET="${TARGET_USER}@${TARGET_HOST}"
20 |
21 | workDir=$(mktemp -d)
22 | trap 'rm -rf "$workDir"' EXIT
23 |
24 | sshOpts=(-p "${TARGET_PORT}")
25 | sshOpts+=(-o UserKnownHostsFile=/dev/null)
26 | sshOpts+=(-o StrictHostKeyChecking=no)
27 |
28 | set +x
29 | if [[ -n ${SSH_KEY+x} && ${SSH_KEY} != "-" ]]; then
30 | sshPrivateKeyFile="$workDir/ssh_key"
31 | # Create the file with 0700 - umask calculation: 777 - 700 = 077
32 | (
33 | umask 077
34 | echo "$SSH_KEY" >"$sshPrivateKeyFile"
35 | )
36 | unset SSH_AUTH_SOCK # don't use system agent if key was supplied
37 | sshOpts+=(-o "IdentityFile=${sshPrivateKeyFile}")
38 | fi
39 | set -x
40 |
41 | try=1
42 | until NIX_SSHOPTS="${sshOpts[*]}" nix copy -s --experimental-features nix-command --to "ssh://$TARGET" "$NIXOS_SYSTEM"; do
43 | if [[ $try -gt 10 ]]; then
44 | echo "retries exhausted" >&2
45 | exit 1
46 | fi
47 | sleep 10
48 | try=$((try + 1))
49 | done
50 |
51 | if [[ $INSTALL_BOOTLOADER == "true" ]]; then
52 | extra_env='NIXOS_INSTALL_BOOTLOADER=1'
53 | else
54 | extra_env=''
55 | fi
56 |
57 | switchCommand="nix-env -p /nix/var/nix/profiles/system --set $(printf "%q" "$NIXOS_SYSTEM"); $extra_env /nix/var/nix/profiles/system/bin/switch-to-configuration switch"
58 |
59 | if [[ $TARGET_USER != "root" ]]; then
60 | switchCommand="sudo bash -c '$switchCommand'"
61 | fi
62 | deploy_status=0
63 | # shellcheck disable=SC2029
64 | ssh "${sshOpts[@]}" "$TARGET" "$switchCommand" || deploy_status="$?"
65 | if [[ $IGNORE_SYSTEMD_ERRORS == "true" && $deploy_status == "4" ]]; then
66 | exit 0
67 | fi
68 | exit "$deploy_status"
69 |
--------------------------------------------------------------------------------
/terraform/nixos-rebuild/main.tf:
--------------------------------------------------------------------------------
1 | resource "null_resource" "nixos-rebuild" {
2 | triggers = {
3 | store_path = var.nixos_system
4 | }
5 | provisioner "local-exec" {
6 | environment = {
7 | SSH_KEY = var.ssh_private_key
8 | }
9 | command = "${path.module}/deploy.sh ${var.nixos_system} ${var.target_user} ${var.target_host} ${var.target_port} ${var.ignore_systemd_errors} ${var.install_bootloader}"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/terraform/nixos-rebuild/variables.tf:
--------------------------------------------------------------------------------
1 | variable "nixos_system" {
2 | type = string
3 | description = "The nixos system to deploy"
4 | }
5 |
6 | variable "target_host" {
7 | type = string
8 | description = "DNS host to deploy to"
9 | }
10 |
11 | variable "target_user" {
12 | type = string
13 | default = "root"
14 | description = "User to deploy as"
15 | }
16 |
17 | variable "target_port" {
18 | type = number
19 | description = "SSH port used to connect to the target_host"
20 | default = 22
21 | }
22 |
23 | variable "ssh_private_key" {
24 | type = string
25 | description = "Content of private key used to connect to the target_host. If set to - no key is passed to openssh and ssh will use its own configuration"
26 | default = "-"
27 | }
28 |
29 | variable "ignore_systemd_errors" {
30 | type = bool
31 | description = "Ignore systemd errors happening during deploy"
32 | default = false
33 | }
34 |
35 | variable "install_bootloader" {
36 | type = bool
37 | description = "Install/re-install the bootloader"
38 | default = false
39 | }
40 |
--------------------------------------------------------------------------------
/terraform/tests/README.md:
--------------------------------------------------------------------------------
1 | # NixOS Anywhere Terraform Tests
2 |
3 | This directory contains tests for the `nixos-anywhere` Terraform modules using
4 | OpenTofu's built-in testing framework.
5 |
6 | ## Test Structure
7 |
8 | ```
9 | terraform/tests/
10 | ├── main.tf # Core configuration for validation tests
11 | ├── nixos-anywhere.tftest.hcl # Basic validation tests (no external deps)
12 | ├── nix-build-special-args.tftest.hcl # Nix build module tests with special_args
13 | ├── hcloud-deployment.tftest.hcl # Hetzner Cloud integration tests
14 | ├── digitalocean-deployment.tftest.hcl # DigitalOcean integration tests
15 | ├── hcloud/ # Hetzner Cloud deployment configuration
16 | │ ├── main.tf
17 | ├── digitalocean/ # DigitalOcean deployment configuration
18 | ├── main.tf
19 | ```
20 |
21 | ### For Cloud Provider Tests (Optional)
22 |
23 | - Hetzner Cloud account with API token
24 | - DigitalOcean account with API token
25 |
26 | ## Running Tests
27 |
28 | ### Basic Validation Tests (No External Dependencies)
29 |
30 | ```bash
31 | # Enter development environment
32 | nix develop .#terraform
33 |
34 | # Initialize and run validation tests
35 | cd terraform/tests
36 | tofu init
37 | tofu test
38 |
39 | # Run Specific Test File
40 | tofu test -filter=nixos-anywhere.tftest.hcl
41 | ```
42 |
43 | **Current Status:** Tests available for nixos-anywhere module and nix-build
44 | module
45 |
46 | ### Cloud Provider Integration Tests
47 |
48 | #### Hetzner Cloud
49 |
50 | ```bash
51 | # Set your Hetzner Cloud token
52 | export TF_VAR_hcloud_token="your-64-character-hcloud-token"
53 |
54 | # Run Hetzner Cloud tests
55 | tofu test -filter hcloud-deployment.tftest.hcl
56 | ```
57 |
58 | #### DigitalOcean
59 |
60 | ```bash
61 | # Set your DigitalOcean token
62 | export TF_VAR_digitalocean_token="your-digitalocean-api-token"
63 |
64 | # Run DigitalOcean tests
65 | tofu test -filter digitalocean-deployment.tftest.hcl
66 | ```
67 |
68 | **Note:** These tests will fail if no valid token is provided.
69 |
70 | ## Test Categories
71 |
72 | ### 1. Validation Tests (`nixos-anywhere.tftest.hcl`)
73 |
74 | - **Purpose:** Validate configuration structure and variables
75 | - **Dependencies:** None (nixos-anywhere module only)
76 | - **Duration:** 30-60 seconds
77 | - **Cost:** Free
78 |
79 | ### 2. Nix Build Special Args Tests (`nix-build-special-args.tftest.hcl`)
80 |
81 | - **Purpose:** Test nix-build module with special_args functionality
82 | - **Dependencies:** None (nix-build module only)
83 | - **Duration:** 30-60 seconds
84 | - **Cost:** Free
85 |
86 | ### 3. Hetzner Cloud Tests (`hcloud-deployment.tftest.hcl`)
87 |
88 | - **Purpose:** Test complete deployment workflow
89 | - **Dependencies:** Valid Hetzner Cloud token
90 | - **Duration:** 5 minutes (apply tests)
91 | - **Cost:** ~€ 0.006 per hour
92 |
93 | ### 4. DigitalOcean Tests (`digitalocean-deployment.tftest.hcl`)
94 |
95 | - **Purpose:** Test complete deployment workflow
96 | - **Dependencies:** Valid DigitalOcean API token
97 | - **Duration:** 5 minutes (apply tests)
98 | - **Cost:** ~$0,02679 per hour
99 |
100 | ## Usage Examples
101 |
102 | ### Environment Variables
103 |
104 | ```bash
105 | export TF_VAR_hcloud_token="your-hcloud-token"
106 | export TF_VAR_digitalocean_token="your-digitalocean-token"
107 | export TF_VAR_test_name_prefix="my-test"
108 | ```
109 |
110 | ### Variable File (Optional)
111 |
112 | Create `terraform.tfvars`:
113 |
114 | ```hcl
115 | hcloud_token = "your-hcloud-token-here"
116 | digitalocean_token = "your-digitalocean-token-here"
117 | test_name_prefix = "tftest-nixos-anywhere"
118 | ```
119 |
120 | ## Cleanup
121 |
122 | OpenTofu test automatically cleans up resources. Manual cleanup if needed:
123 |
124 | ### Hetzner Cloud
125 |
126 | ```bash
127 | # List test resources
128 | hcloud server list | grep tftest-nixos-anywhere
129 | hcloud ssh-key list | grep tftest-nixos-anywhere
130 |
131 | # Force cleanup
132 | hcloud server delete
133 | hcloud ssh-key delete
134 | ```
135 |
136 | ### DigitalOcean
137 |
138 | ```bash
139 | # List test resources
140 | doctl compute droplet list | grep tftest-nixos-anywhere
141 | doctl compute ssh-key list | grep tftest-nixos-anywhere
142 |
143 | # Force cleanup
144 | doctl compute droplet delete
145 | doctl compute ssh-key delete
146 | ```
147 |
148 | ## Development
149 |
150 | ### Test Best Practices
151 |
152 | - Use `command = plan` for validation tests
153 | - Use `command = apply` sparingly for integration tests
154 | - Include proper assertions with clear error messages
155 | - Make tests conditional based on available credentials
156 |
--------------------------------------------------------------------------------
/terraform/tests/digitalocean-deployment.tftest.hcl:
--------------------------------------------------------------------------------
1 | # Terraform Test Configuration for nixos-anywhere DigitalOcean deployment
2 | # Run with: tofu test -var="digitalocean_token=your-token-here"
3 | # These tests require a valid DigitalOcean API token
4 |
5 | variables {
6 | test_name_prefix = "tftest-nixos-anywhere"
7 | }
8 |
9 | run "test_digitalocean_deployment_apply" {
10 | command = apply
11 |
12 | module {
13 | source = "./digitalocean"
14 | }
15 |
16 | variables {
17 | nixos_system_attr = "github:nix-community/nixos-anywhere-examples#nixosConfigurations.digitalocean.config.system.build.toplevel"
18 | nixos_partitioner_attr = "github:nix-community/nixos-anywhere-examples#nixosConfigurations.digitalocean.config.system.build.diskoNoDeps"
19 | debug_logging = true
20 | }
21 |
22 | assert {
23 | condition = output.nixos_anywhere_result != null
24 | error_message = "nixos-anywhere deployment should produce a result"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/terraform/tests/digitalocean/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | digitalocean = {
4 | source = "digitalocean/digitalocean"
5 | version = "~> 2.34"
6 | }
7 | tls = {
8 | source = "hashicorp/tls"
9 | version = "~> 4.0"
10 | }
11 | local = {
12 | source = "hashicorp/local"
13 | version = "~> 2.4"
14 | }
15 | }
16 | }
17 |
18 | provider "digitalocean" {
19 | token = var.digitalocean_token
20 | }
21 |
22 | variable "digitalocean_token" {
23 | description = "DigitalOcean API token"
24 | type = string
25 | sensitive = true
26 | }
27 |
28 | variable "test_name_prefix" {
29 | description = "Prefix for test resource names"
30 | type = string
31 | default = "tftest-nixos-anywhere"
32 | }
33 |
34 | variable "nixos_system_attr" {
35 | description = "NixOS system attribute to deploy"
36 | type = string
37 | }
38 |
39 | variable "nixos_partitioner_attr" {
40 | description = "NixOS partitioner attribute"
41 | type = string
42 | }
43 |
44 | variable "debug_logging" {
45 | description = "Enable debug logging"
46 | type = bool
47 | default = false
48 | }
49 |
50 | # Generate SSH key pair
51 | resource "tls_private_key" "test_key" {
52 | algorithm = "ED25519"
53 | }
54 |
55 | # Save private key to file
56 | resource "local_file" "private_key" {
57 | content = tls_private_key.test_key.private_key_openssh
58 | filename = "${path.root}/test_key"
59 | file_permission = "0600"
60 | }
61 |
62 | # Save public key to file
63 | resource "local_file" "public_key" {
64 | content = tls_private_key.test_key.public_key_openssh
65 | filename = "${path.root}/test_key.pub"
66 | }
67 |
68 | # Create DigitalOcean SSH key
69 | resource "digitalocean_ssh_key" "test_key" {
70 | name = "${var.test_name_prefix}-deployment-key"
71 | public_key = tls_private_key.test_key.public_key_openssh
72 | }
73 |
74 | # Create test droplet
75 | # Note: Using s-2vcpu-2gb (minimum 2GB RAM required for nixos-anywhere kexec)
76 | # DigitalOcean uses /dev/vda for disk devices (handled by digitalocean config)
77 | resource "digitalocean_droplet" "test_server" {
78 | name = "${var.test_name_prefix}-server"
79 | image = "ubuntu-22-04-x64"
80 | size = "s-2vcpu-2gb"
81 | region = "nyc3"
82 | ssh_keys = [digitalocean_ssh_key.test_key.id]
83 |
84 | tags = [
85 | "nixos-anywhere-test",
86 | replace(replace(replace(timestamp(), ":", "-"), "T", "-"), "Z", "")
87 | ]
88 | }
89 |
90 | # nixos-anywhere all-in-one module
91 | # Uses digitalocean configuration from nixos-anywhere-examples which:
92 | # - Sets disk device to /dev/vda (DigitalOcean standard)
93 | # - Configures cloud-init for network setup
94 | # - Disables DHCP in favor of cloud-init provisioning
95 | module "nixos_anywhere" {
96 | source = "../../all-in-one"
97 |
98 | nixos_system_attr = var.nixos_system_attr
99 | nixos_partitioner_attr = var.nixos_partitioner_attr
100 | target_host = digitalocean_droplet.test_server.ipv4_address
101 | target_port = 22
102 | target_user = "root"
103 | debug_logging = var.debug_logging
104 | deployment_ssh_key = tls_private_key.test_key.private_key_openssh
105 | install_ssh_key = tls_private_key.test_key.private_key_openssh
106 |
107 | special_args = {
108 | extraPublicKeys = [tls_private_key.test_key.public_key_openssh]
109 | }
110 | }
111 |
112 | output "nixos_anywhere_result" {
113 | description = "nixos-anywhere module result"
114 | value = module.nixos_anywhere.result
115 | }
116 |
117 | output "droplet_ip" {
118 | description = "DigitalOcean droplet public IP address"
119 | value = digitalocean_droplet.test_server.ipv4_address
120 | }
121 |
122 | output "droplet_id" {
123 | description = "DigitalOcean droplet ID for cleanup"
124 | value = digitalocean_droplet.test_server.id
125 | }
126 |
127 | output "ssh_key_id" {
128 | description = "DigitalOcean SSH key ID for cleanup"
129 | value = digitalocean_ssh_key.test_key.id
130 | }
131 |
132 | output "ssh_connection_command" {
133 | description = "SSH command to connect to the deployed server"
134 | value = "ssh -i ${local_file.private_key.filename} root@${digitalocean_droplet.test_server.ipv4_address}"
135 | }
--------------------------------------------------------------------------------
/terraform/tests/hcloud-deployment.tftest.hcl:
--------------------------------------------------------------------------------
1 | # Terraform Test Configuration for nixos-anywhere Hetzner Cloud deployment
2 | # Run with: tofu test -var="hcloud_token=your-token-here"
3 | # These tests require a valid Hetzner Cloud API token
4 |
5 | variables {
6 | test_name_prefix = "tftest-nixos-anywhere"
7 | }
8 |
9 | run "test_hcloud_deployment_apply" {
10 | command = apply
11 |
12 | module {
13 | source = "./hcloud"
14 | }
15 |
16 | variables {
17 | nixos_system_attr = "github:nix-community/nixos-anywhere-examples#nixosConfigurations.hetzner-cloud.config.system.build.toplevel"
18 | nixos_partitioner_attr = "github:nix-community/nixos-anywhere-examples#nixosConfigurations.hetzner-cloud.config.system.build.diskoNoDeps"
19 | debug_logging = true
20 | }
21 |
22 | assert {
23 | condition = output.nixos_anywhere_result != null
24 | error_message = "nixos-anywhere deployment should produce a result"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/terraform/tests/hcloud/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | hcloud = {
4 | source = "hetznercloud/hcloud"
5 | version = "~> 1.45"
6 | }
7 | tls = {
8 | source = "hashicorp/tls"
9 | version = "~> 4.0"
10 | }
11 | local = {
12 | source = "hashicorp/local"
13 | version = "~> 2.4"
14 | }
15 | }
16 | }
17 |
18 | provider "hcloud" {
19 | token = var.hcloud_token
20 | }
21 |
22 | variable "hcloud_token" {
23 | description = "Hetzner Cloud API token (64 characters)"
24 | type = string
25 | sensitive = true
26 | }
27 |
28 | variable "test_name_prefix" {
29 | description = "Prefix for test resource names"
30 | type = string
31 | default = "tftest-nixos-anywhere"
32 | }
33 |
34 | variable "nixos_system_attr" {
35 | description = "NixOS system attribute to deploy"
36 | type = string
37 | }
38 |
39 | variable "nixos_partitioner_attr" {
40 | description = "NixOS partitioner attribute"
41 | type = string
42 | }
43 |
44 |
45 | variable "debug_logging" {
46 | description = "Enable debug logging"
47 | type = bool
48 | default = false
49 | }
50 |
51 | # Generate SSH key pair
52 | resource "tls_private_key" "test_key" {
53 | algorithm = "ED25519"
54 | }
55 |
56 | # Save private key to file
57 | resource "local_file" "private_key" {
58 | content = tls_private_key.test_key.private_key_openssh
59 | filename = "${path.root}/test_key"
60 | file_permission = "0600"
61 | }
62 |
63 | # Save public key to file
64 | resource "local_file" "public_key" {
65 | content = tls_private_key.test_key.public_key_openssh
66 | filename = "${path.root}/test_key.pub"
67 | }
68 |
69 | # Create Hetzner Cloud SSH key
70 | resource "hcloud_ssh_key" "test_key" {
71 | name = "${var.test_name_prefix}-deployment-key"
72 | public_key = tls_private_key.test_key.public_key_openssh
73 | }
74 |
75 | # Create test server
76 | resource "hcloud_server" "test_server" {
77 | name = "${var.test_name_prefix}-server"
78 | image = "ubuntu-22.04"
79 | server_type = "cx22"
80 | location = "hel1"
81 | ssh_keys = [hcloud_ssh_key.test_key.id]
82 |
83 | labels = {
84 | purpose = "nixos-anywhere-test"
85 | test_run = replace(replace(replace(timestamp(), ":", "-"), "T", "-"), "Z", "")
86 | }
87 | }
88 |
89 | # nixos-anywhere all-in-one module
90 | module "nixos_anywhere" {
91 | source = "../../all-in-one"
92 |
93 | nixos_system_attr = var.nixos_system_attr
94 | nixos_partitioner_attr = var.nixos_partitioner_attr
95 | target_host = hcloud_server.test_server.ipv4_address
96 | target_port = 22
97 | target_user = "root"
98 | debug_logging = var.debug_logging
99 | deployment_ssh_key = tls_private_key.test_key.private_key_openssh
100 | install_ssh_key = tls_private_key.test_key.private_key_openssh
101 |
102 | special_args = {
103 | extraPublicKeys = [tls_private_key.test_key.public_key_openssh]
104 | }
105 | }
106 |
107 | output "nixos_anywhere_result" {
108 | description = "nixos-anywhere module result"
109 | value = module.nixos_anywhere.result
110 | }
111 |
--------------------------------------------------------------------------------
/terraform/tests/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | external = {
4 | source = "hashicorp/external"
5 | version = "~> 2.3"
6 | }
7 | null = {
8 | source = "hashicorp/null"
9 | version = "~> 3.2"
10 | }
11 | }
12 | }
13 |
14 | variable "nixos_system_attr" {
15 | description = "NixOS system attribute to deploy"
16 | type = string
17 | default = ""
18 | }
19 |
20 | variable "nixos_partitioner_attr" {
21 | description = "NixOS partitioner attribute"
22 | type = string
23 | default = ""
24 | }
25 |
26 | variable "target_host" {
27 | description = "Target host for deployment"
28 | type = string
29 | default = "test.example.com"
30 | }
31 |
32 | variable "target_port" {
33 | description = "Target SSH port"
34 | type = number
35 | default = 22
36 | }
37 |
38 | variable "target_user" {
39 | description = "Target SSH user"
40 | type = string
41 | default = "root"
42 | }
43 |
44 | variable "debug_logging" {
45 | description = "Enable debug logging"
46 | type = bool
47 | default = false
48 | }
49 |
50 | variable "phases" {
51 | description = "Deployment phases to run"
52 | type = set(string)
53 | default = ["kexec", "disko", "install"]
54 | }
55 |
56 | variable "build_on_remote" {
57 | description = "Build on remote machine"
58 | type = bool
59 | default = false
60 | }
61 |
62 | variable "install_ssh_key" {
63 | description = "SSH private key for installation"
64 | type = string
65 | default = ""
66 | sensitive = true
67 | }
68 |
69 | # nixos-anywhere all-in-one module
70 | module "nixos_anywhere" {
71 | source = "../all-in-one"
72 |
73 | nixos_system_attr = var.nixos_system_attr
74 | nixos_partitioner_attr = var.nixos_partitioner_attr
75 | target_host = var.target_host
76 | target_port = var.target_port
77 | target_user = var.target_user
78 | debug_logging = var.debug_logging
79 | phases = var.phases
80 | build_on_remote = var.build_on_remote
81 | install_ssh_key = var.install_ssh_key
82 | }
83 |
84 | output "nixos_anywhere_result" {
85 | description = "nixos-anywhere module result"
86 | value = module.nixos_anywhere.result
87 | }
88 |
--------------------------------------------------------------------------------
/terraform/tests/nix-build-special-args.tftest.hcl:
--------------------------------------------------------------------------------
1 | # Terraform Test Configuration for nix-build module with special_args
2 | # Tests the fix for nested flakes in git repositories
3 | # Run with: tofu test
4 |
5 | variables {
6 | test_name_prefix = "tftest-nix-build-special-args"
7 | }
8 |
9 | # Test: nix-build module with special_args using terraform-test configuration
10 | run "validate_nix_build_special_args" {
11 | command = plan
12 |
13 | module {
14 | source = "../nix-build"
15 | }
16 |
17 | variables {
18 | attribute = ".#nixosConfigurations.terraform-test.config.system.build.toplevel"
19 | special_args = {
20 | terraform = {
21 | hostname = "special-args-test-host"
22 | ip_address = "192.168.1.100"
23 | environment = "testing"
24 | deployment_id = "test-001"
25 | }
26 | }
27 | }
28 |
29 | assert {
30 | condition = var.special_args.terraform.hostname == "special-args-test-host"
31 | error_message = "special_args should preserve hostname value"
32 | }
33 |
34 | assert {
35 | condition = var.special_args.terraform.environment == "testing"
36 | error_message = "special_args should preserve environment value"
37 | }
38 |
39 | assert {
40 | condition = var.attribute == ".#nixosConfigurations.terraform-test.config.system.build.toplevel"
41 | error_message = "should use terraform-test configuration"
42 | }
43 | }
--------------------------------------------------------------------------------
/terraform/tests/nixos-anywhere.tftest.hcl:
--------------------------------------------------------------------------------
1 | # Terraform Test Configuration for nixos-anywhere all-in-one module
2 | # Run with: tofu test
3 |
4 | variables {
5 | test_name_prefix = "tftest-nixos-anywhere"
6 | }
7 |
8 | # Test: Basic variable validation
9 | run "validate_basic_config" {
10 | command = plan
11 |
12 | variables {
13 | nixos_system_attr = ".#nixosConfigurations.terraform-test.config.system.build.toplevel"
14 | nixos_partitioner_attr = ".#nixosConfigurations.terraform-test.config.system.build.diskoNoDeps"
15 | target_host = "1.2.3.4"
16 | debug_logging = true
17 | }
18 |
19 | assert {
20 | condition = var.nixos_system_attr != ""
21 | error_message = "NixOS system attribute must be specified"
22 | }
23 |
24 | assert {
25 | condition = var.nixos_partitioner_attr != ""
26 | error_message = "NixOS partitioner attribute must be specified"
27 | }
28 |
29 | assert {
30 | condition = var.target_host != ""
31 | error_message = "Target host must be specified"
32 | }
33 | }
34 |
35 | # Test: Variable validation with custom values
36 | run "validate_variables" {
37 | command = plan
38 |
39 | variables {
40 | nixos_system_attr = ".#nixosConfigurations.terraform-test.config.system.build.toplevel"
41 | nixos_partitioner_attr = ".#nixosConfigurations.terraform-test.config.system.build.diskoNoDeps"
42 | target_host = "test.example.com"
43 | target_port = 2222
44 | target_user = "deploy"
45 | debug_logging = true
46 | phases = ["kexec", "disko", "install"]
47 | build_on_remote = true
48 | }
49 |
50 | assert {
51 | condition = var.target_port == 2222
52 | error_message = "Target port should be configurable"
53 | }
54 |
55 | assert {
56 | condition = var.target_user == "deploy"
57 | error_message = "Target user should be configurable"
58 | }
59 |
60 | assert {
61 | condition = contains(var.phases, "kexec")
62 | error_message = "Phases should include kexec"
63 | }
64 |
65 | assert {
66 | condition = var.build_on_remote == true
67 | error_message = "Build on remote should be configurable"
68 | }
69 |
70 | assert {
71 | condition = var.debug_logging == true
72 | error_message = "Debug logging should be configurable"
73 | }
74 | }
75 |
76 | # Test: Default values
77 | run "validate_defaults" {
78 | command = plan
79 |
80 | variables {
81 | nixos_system_attr = ".#nixosConfigurations.terraform-test.config.system.build.toplevel"
82 | nixos_partitioner_attr = ".#nixosConfigurations.terraform-test.config.system.build.diskoNoDeps"
83 | target_host = "192.168.1.100"
84 | }
85 |
86 | assert {
87 | condition = var.target_port == 22
88 | error_message = "Default target port should be 22"
89 | }
90 |
91 | assert {
92 | condition = var.target_user == "root"
93 | error_message = "Default target user should be root"
94 | }
95 |
96 | assert {
97 | condition = var.debug_logging == false
98 | error_message = "Default debug logging should be false"
99 | }
100 |
101 | assert {
102 | condition = var.build_on_remote == false
103 | error_message = "Default build_on_remote should be false"
104 | }
105 |
106 | assert {
107 | condition = contains(var.phases, "kexec")
108 | error_message = "Default phases should include kexec"
109 | }
110 |
111 | assert {
112 | condition = contains(var.phases, "disko")
113 | error_message = "Default phases should include disko"
114 | }
115 |
116 | assert {
117 | condition = contains(var.phases, "install")
118 | error_message = "Default phases should include install"
119 | }
120 | }
--------------------------------------------------------------------------------
/terraform/tests/terraform.tfvars.example:
--------------------------------------------------------------------------------
1 | # Terraform variables for nixos-anywhere test suite
2 | # Copy this file to terraform.tfvars and fill in your values
3 |
4 | # Required: Hetzner Cloud API token
5 | # Get this from https://console.hetzner.cloud/
6 | hcloud_token = "your-hcloud-token-here"
7 |
8 | # Required: DigitalOcean API token
9 | # Get this from https://cloud.digitalocean.com/account/api/tokens
10 | digitalocean_token = "your-digitalocean-token-here"
11 |
12 | # Optional: Prefix for test resource names
13 | # This helps identify and clean up test resources
14 | test_name_prefix = "tftest-nixos-anywhere"
--------------------------------------------------------------------------------
/terraform/update-docs.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -euo pipefail
4 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5 | cd "$SCRIPT_DIR"
6 | files=()
7 | git ls-files "${SCRIPT_DIR}"/*/ | xargs -n1 dirname | sort -u | while read -r i; do
8 | # ignore test directory
9 | if [[ $i == "tests"* ]]; then
10 | continue
11 | fi
12 | echo "Processing module: $i"
13 | module_name=$(basename "$i")
14 | markdown_file="${SCRIPT_DIR}/${module_name}.md"
15 | terraform-docs --config "${SCRIPT_DIR}/.terraform-docs.yml" markdown table --output-file "${markdown_file}" --output-mode inject "${module_name}"
16 | files+=("${markdown_file}")
17 | done
18 | cd ..
19 | nix fmt -- --no-cache
20 |
--------------------------------------------------------------------------------
/tests/flake-module.nix:
--------------------------------------------------------------------------------
1 | { withSystem, inputs, ... }:
2 |
3 | {
4 | flake.checks.x86_64-linux = withSystem "x86_64-linux" ({ pkgs, system, inputs', config, ... }:
5 | let
6 | system-to-install = pkgs.nixos [
7 | ./modules/system-to-install.nix
8 | inputs.disko.nixosModules.disko
9 | ];
10 | testInputsUnstable = {
11 | inherit pkgs;
12 | inherit (inputs.disko.nixosModules) disko;
13 | nixos-anywhere = config.packages.nixos-anywhere;
14 | kexec-installer = "${inputs'.nixos-images.packages.kexec-installer-nixos-unstable-noninteractive}/nixos-kexec-installer-noninteractive-${system}.tar.gz";
15 | inherit system-to-install;
16 | };
17 | testInputsStable = testInputsUnstable // {
18 | kexec-installer = "${inputs'.nixos-images.packages.kexec-installer-nixos-stable-noninteractive}/nixos-kexec-installer-noninteractive-${system}.tar.gz";
19 | };
20 | linuxTestInputs = testInputsUnstable // {
21 | nix-vm-test = inputs.nix-vm-test;
22 | };
23 | in
24 | {
25 | from-nixos = import ./from-nixos.nix testInputsUnstable;
26 | from-nixos-stable = import ./from-nixos.nix testInputsStable;
27 | from-nixos-with-sudo = import ./from-nixos-with-sudo.nix testInputsUnstable;
28 | from-nixos-with-sudo-stable = import ./from-nixos-with-sudo.nix testInputsStable;
29 | from-nixos-with-generated-config = import ./from-nixos-generate-config.nix testInputsUnstable;
30 | from-nixos-build-on-remote = import ./from-nixos-build-on-remote.nix testInputsUnstable;
31 | from-nixos-separated-phases = import ./from-nixos-separated-phases.nix testInputsUnstable;
32 | ubuntu-kexec-test = import ./linux-kexec-test.nix (linuxTestInputs // {
33 | distribution = "ubuntu";
34 | version = "24_04";
35 | });
36 | fedora-kexec-test = import ./linux-kexec-test.nix (linuxTestInputs // {
37 | distribution = "fedora";
38 | version = "40";
39 | });
40 | debian-kexec-test = import ./linux-kexec-test.nix (linuxTestInputs // {
41 | distribution = "debian";
42 | version = "12";
43 | });
44 | });
45 | }
46 |
--------------------------------------------------------------------------------
/tests/from-nixos-build-on-remote.nix:
--------------------------------------------------------------------------------
1 | (import ./lib/test-base.nix) (
2 | { pkgs, ... }:
3 | {
4 | name = "from-nixos-build-on-remote";
5 | nodes = {
6 | installer = ./modules/installer.nix;
7 | installed = {
8 | services.openssh.enable = true;
9 | virtualisation.memorySize = 1500;
10 |
11 | users.users.root.openssh.authorizedKeys.keyFiles = [ ./modules/ssh-keys/ssh.pub ];
12 | };
13 | };
14 | testScript = ''
15 | def create_test_machine(
16 | oldmachine=None, **kwargs
17 | ): # taken from
18 | start_command = [
19 | "${pkgs.qemu_test}/bin/qemu-kvm",
20 | "-cpu",
21 | "max",
22 | "-m",
23 | "1024",
24 | "-virtfs",
25 | "local,path=/nix/store,security_model=none,mount_tag=nix-store",
26 | "-drive",
27 | f"file={oldmachine.state_dir}/installed.qcow2,id=drive1,if=none,index=1,werror=report",
28 | "-device",
29 | "virtio-blk-pci,drive=drive1",
30 | ]
31 | machine = create_machine(start_command=" ".join(start_command), **kwargs)
32 | driver.machines.append(machine)
33 | return machine
34 | start_all()
35 |
36 | installer.succeed("""
37 | nixos-anywhere \
38 | -i /root/.ssh/install_key \
39 | --debug \
40 | --build-on-remote \
41 | --kexec /etc/nixos-anywhere/kexec-installer \
42 | --store-paths /etc/nixos-anywhere/disko /etc/nixos-anywhere/system-to-install \
43 | root@installed >&2
44 | """)
45 | try:
46 | installed.shutdown()
47 | except BrokenPipeError:
48 | # qemu has already exited
49 | pass
50 | new_machine = create_test_machine(oldmachine=installed, name="after_install")
51 | new_machine.start()
52 | hostname = new_machine.succeed("hostname").strip()
53 | assert "nixos-anywhere" == hostname, f"'nixos-anywhere' != '{hostname}'"
54 | '';
55 | }
56 | )
57 |
--------------------------------------------------------------------------------
/tests/from-nixos-generate-config.nix:
--------------------------------------------------------------------------------
1 | (import ./lib/test-base.nix) {
2 | name = "from-nixos-generate-config";
3 | nodes = {
4 | installer = { pkgs, ... }: {
5 | imports = [
6 | ./modules/installer.nix
7 | ];
8 | environment.systemPackages = [ pkgs.jq ];
9 | };
10 | installed = {
11 | services.openssh.enable = true;
12 | virtualisation.memorySize = 1500;
13 |
14 | users.users.root.openssh.authorizedKeys.keyFiles = [ ./modules/ssh-keys/ssh.pub ];
15 | };
16 | };
17 | testScript = ''
18 | start_all()
19 | installer.fail("test -f /tmp/hw/config.nix")
20 | installer.succeed("echo super-secret > /tmp/disk-1.key")
21 | output = installer.succeed("""
22 | nixos-anywhere \
23 | -i /root/.ssh/install_key \
24 | --debug \
25 | --kexec /etc/nixos-anywhere/kexec-installer \
26 | --disk-encryption-keys /tmp/disk-1.key /tmp/disk-1.key \
27 | --disk-encryption-keys /tmp/disk-2.key <(echo another-secret) \
28 | --phases kexec,disko \
29 | --generate-hardware-config nixos-generate-config /tmp/hw/config.nix \
30 | --store-paths /etc/nixos-anywhere/disko /etc/nixos-anywhere/system-to-install \
31 | root@installed >&2
32 | echo "disk-1.key: '$(ssh -i /root/.ssh/install_key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
33 | root@installed cat /tmp/disk-1.key)'"
34 | echo "disk-2.key: '$(ssh -i /root/.ssh/install_key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
35 | root@installed cat /tmp/disk-2.key)'"
36 | """)
37 |
38 | installer.succeed("cat /tmp/hw/config.nix >&2")
39 | installer.succeed("nix-instantiate --parse /tmp/hw/config.nix")
40 |
41 | assert "disk-1.key: 'super-secret'" in output, f"output does not contain expected values: {output}"
42 | assert "disk-2.key: 'another-secret'" in output, f"output does not contain expected values: {output}"
43 |
44 | installer.fail("test -f /test/hw/config.json")
45 |
46 | output = installer.succeed("""
47 | nixos-anywhere \
48 | -i /root/.ssh/install_key \
49 | --debug \
50 | --kexec /etc/nixos-anywhere/kexec-installer \
51 | --disk-encryption-keys /tmp/disk-1.key /tmp/disk-1.key \
52 | --disk-encryption-keys /tmp/disk-2.key <(echo another-secret) \
53 | --phases kexec,disko \
54 | --generate-hardware-config nixos-facter /tmp/hw/config.json \
55 | --store-paths /etc/nixos-anywhere/disko /etc/nixos-anywhere/system-to-install \
56 | installed >&2
57 | echo "disk-1.key: '$(ssh -i /root/.ssh/install_key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
58 | root@installed cat /tmp/disk-1.key)'"
59 | echo "disk-2.key: '$(ssh -i /root/.ssh/install_key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
60 | root@installed cat /tmp/disk-2.key)'"
61 | """)
62 |
63 | installer.succeed("cat /tmp/hw/config.json >&2")
64 | installer.succeed("jq < /tmp/hw/config.json")
65 |
66 | assert "disk-1.key: 'super-secret'" in output, f"output does not contain expected values: {output}"
67 | assert "disk-2.key: 'another-secret'" in output, f"output does not contain expected values: {output}"
68 | '';
69 | }
70 |
--------------------------------------------------------------------------------
/tests/from-nixos-separated-phases.nix:
--------------------------------------------------------------------------------
1 | (import ./lib/test-base.nix) {
2 | name = "from-nixos-separated-phases";
3 | nodes = {
4 | installer = ./modules/installer.nix;
5 | installed = {
6 | services.openssh.enable = true;
7 | virtualisation.memorySize = 1500;
8 |
9 | users.users.nixos = {
10 | isNormalUser = true;
11 | openssh.authorizedKeys.keyFiles = [ ./modules/ssh-keys/ssh.pub ];
12 | extraGroups = [ "wheel" ];
13 | };
14 | security.sudo.enable = true;
15 | security.sudo.wheelNeedsPassword = false;
16 | };
17 | };
18 | testScript = ''
19 | start_all()
20 |
21 | with subtest("Kexec Phase"):
22 | installer.succeed("""
23 | nixos-anywhere \
24 | -i /root/.ssh/install_key \
25 | --debug \
26 | --kexec /etc/nixos-anywhere/kexec-installer \
27 | --phases kexec \
28 | --store-paths /etc/nixos-anywhere/disko /etc/nixos-anywhere/system-to-install \
29 | nixos@installed >&2
30 | """)
31 |
32 | with subtest("Disko Phase"):
33 | output = installer.succeed("""
34 | nixos-anywhere \
35 | -i /root/.ssh/install_key \
36 | --debug \
37 | --phases disko \
38 | --store-paths /etc/nixos-anywhere/disko /etc/nixos-anywhere/system-to-install \
39 | installed >&2
40 | """)
41 |
42 | with subtest("Install Phase"):
43 | installer.succeed("""
44 | nixos-anywhere \
45 | -i /root/.ssh/install_key \
46 | --debug \
47 | --phases install \
48 | --store-paths /etc/nixos-anywhere/disko /etc/nixos-anywhere/system-to-install \
49 | root@installed >&2
50 | """)
51 | '';
52 | }
53 |
--------------------------------------------------------------------------------
/tests/from-nixos-with-sudo.nix:
--------------------------------------------------------------------------------
1 | (import ./lib/test-base.nix) {
2 | name = "from-nixos-with-sudo";
3 | nodes = {
4 | installer = ./modules/installer.nix;
5 | installed = {
6 | services.openssh.enable = true;
7 | virtualisation.memorySize = 1500;
8 |
9 | users.users.nixos = {
10 | isNormalUser = true;
11 | openssh.authorizedKeys.keyFiles = [ ./modules/ssh-keys/ssh.pub ];
12 | extraGroups = [ "wheel" ];
13 | };
14 | security.sudo.enable = true;
15 | security.sudo.wheelNeedsPassword = false;
16 | };
17 | };
18 | testScript = ''
19 | start_all()
20 | installer.succeed("echo super-secret > /tmp/disk-1.key")
21 | output = installer.succeed("""
22 | nixos-anywhere \
23 | -i /root/.ssh/install_key \
24 | --debug \
25 | --kexec /etc/nixos-anywhere/kexec-installer \
26 | --phases kexec,disko \
27 | --disk-encryption-keys /tmp/disk-1.key /tmp/disk-1.key \
28 | --disk-encryption-keys /tmp/disk-2.key <(echo another-secret) \
29 | --store-paths /etc/nixos-anywhere/disko /etc/nixos-anywhere/system-to-install \
30 | nixos@installed >&2
31 | echo "disk-1.key: '$(ssh -i /root/.ssh/install_key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
32 | root@installed cat /tmp/disk-1.key)'"
33 | echo "disk-2.key: '$(ssh -i /root/.ssh/install_key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
34 | root@installed cat /tmp/disk-2.key)'"
35 | """)
36 |
37 | assert "disk-1.key: 'super-secret'" in output, f"output does not contain expected values: {output}"
38 | assert "disk-2.key: 'another-secret'" in output, f"output does not contain expected values: {output}"
39 | '';
40 | }
41 |
--------------------------------------------------------------------------------
/tests/from-nixos.nix:
--------------------------------------------------------------------------------
1 | (import ./lib/test-base.nix) (
2 | { pkgs, ... }:
3 | {
4 | name = "from-nixos";
5 | nodes = {
6 | installer = ./modules/installer.nix;
7 | installed = {
8 | services.openssh.enable = true;
9 | virtualisation.memorySize = 1500;
10 |
11 | users.users.root.openssh.authorizedKeys.keyFiles = [ ./modules/ssh-keys/ssh.pub ];
12 | };
13 | };
14 | testScript = ''
15 | def create_test_machine(
16 | oldmachine=None, **kwargs
17 | ): # taken from
18 | start_command = [
19 | "${pkgs.qemu_test}/bin/qemu-kvm",
20 | "-cpu",
21 | "max",
22 | "-m",
23 | "1024",
24 | "-virtfs",
25 | "local,path=/nix/store,security_model=none,mount_tag=nix-store",
26 | "-drive",
27 | f"file={oldmachine.state_dir}/installed.qcow2,id=drive1,if=none,index=1,werror=report",
28 | "-device",
29 | "virtio-blk-pci,drive=drive1",
30 | ]
31 | machine = create_machine(start_command=" ".join(start_command), **kwargs)
32 | driver.machines.append(machine)
33 | return machine
34 | start_all()
35 | installer.succeed("mkdir -p /tmp/extra-files/var/lib/secrets")
36 | installer.succeed("echo value > /tmp/extra-files/var/lib/secrets/key")
37 | installer.succeed("mkdir -p /tmp/extra-files/home/user/.ssh")
38 | installer.succeed("echo secretkey > /tmp/extra-files/home/user/.ssh/id_ed25519")
39 | installer.succeed("echo publickey > /tmp/extra-files/home/user/.ssh/id_ed25519.pub")
40 | installer.succeed("chmod 600 /tmp/extra-files/home/user/.ssh/id_ed25519")
41 | ssh_key_path = "/etc/ssh/ssh_host_ed25519_key.pub"
42 | ssh_key_output = installer.wait_until_succeeds(f"""
43 | ssh -i /root/.ssh/install_key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
44 | root@installed cat {ssh_key_path}
45 | """)
46 | installer.succeed("""
47 | nixos-anywhere \
48 | -i /root/.ssh/install_key \
49 | --debug \
50 | --kexec /etc/nixos-anywhere/kexec-installer \
51 | --extra-files /tmp/extra-files \
52 | --store-paths /etc/nixos-anywhere/disko /etc/nixos-anywhere/system-to-install \
53 | --chown /home/user 1000:100 \
54 | --copy-host-keys \
55 | root@installed >&2
56 | """)
57 | try:
58 | installed.shutdown()
59 | except BrokenPipeError:
60 | # qemu has already exited
61 | pass
62 | new_machine = create_test_machine(oldmachine=installed, name="after_install")
63 | new_machine.start()
64 | hostname = new_machine.succeed("hostname").strip()
65 | assert "nixos-anywhere" == hostname, f"'nixos-anywhere' != '{hostname}'"
66 | content = new_machine.succeed("cat /var/lib/secrets/key").strip()
67 | assert "value" == content, f"secret does not have expected value: {content}"
68 | ssh_key_content = new_machine.succeed(f"cat {ssh_key_path}").strip()
69 | assert ssh_key_content in ssh_key_output, "SSH host identity changed"
70 | priv_key_perms = new_machine.succeed("stat -c %a /home/user/.ssh/id_ed25519").strip()
71 | assert priv_key_perms == "600", f"unexpected permissions for private key: {priv_key_perms}"
72 | user_dir_ownership = new_machine.succeed("stat -c %u:%g /home/user").strip()
73 | assert user_dir_ownership == "1000:100", f"unexpected user home dir permissions: {user_dir_ownership}"
74 | '';
75 | }
76 | )
77 |
--------------------------------------------------------------------------------
/tests/lib/test-base.nix:
--------------------------------------------------------------------------------
1 | test:
2 | { pkgs
3 | , nixos-anywhere
4 | , disko
5 | , kexec-installer
6 | , system-to-install
7 | , ...
8 | }:
9 | let
10 | inherit (pkgs) lib;
11 | nixos-lib = import (pkgs.path + "/nixos/lib") { };
12 | in
13 | (nixos-lib.runTest {
14 | hostPkgs = pkgs;
15 | # speed-up evaluation
16 | defaults.documentation.enable = lib.mkDefault false;
17 | # to accept external dependencies such as disko
18 | node.specialArgs.inputs = { inherit nixos-anywhere disko kexec-installer system-to-install; };
19 | imports = [ test ];
20 | }).config.result
21 |
--------------------------------------------------------------------------------
/tests/linux-kexec-test.nix:
--------------------------------------------------------------------------------
1 | { pkgs, nixos-anywhere, kexec-installer, nix-vm-test, system-to-install, distribution, version, ... }:
2 |
3 | (nix-vm-test.lib.${pkgs.system}.${distribution}.${version} {
4 | sharedDirs = { };
5 |
6 | # Configure VM with 2GB memory
7 | machineConfigModule = { ... }: {
8 | nodes.vm.virtualisation.memorySize = 1500;
9 | };
10 |
11 | # The test script
12 | testScript = ''
13 | # Python imports
14 | import subprocess
15 | import tempfile
16 | import shutil
17 | import os
18 |
19 | # Wait for the system to be fully booted
20 | vm.wait_for_unit("multi-user.target")
21 |
22 | # Detect SSH service name (ssh on Ubuntu/Debian, sshd on Fedora/RHEL)
23 | ssh_service = "sshd" if "${distribution}" in ["fedora", "centos", "rhel"] else "ssh"
24 |
25 | # Unmask SSH service (which is masked by default in the test VM)
26 | vm.succeed(f"systemctl unmask {ssh_service}.service || true")
27 | vm.succeed(f"systemctl unmask {ssh_service}.socket || true")
28 |
29 | # Generate SSH host keys (required for SSH to start)
30 | vm.succeed("ssh-keygen -A")
31 |
32 | # Setup SSH with the existing keys
33 | vm.succeed("mkdir -p /root/.ssh")
34 | vm.succeed(
35 | "echo '${builtins.replaceStrings ["\n"] [""] (builtins.readFile ./modules/ssh-keys/ssh.pub)}' > /root/.ssh/authorized_keys"
36 | )
37 | vm.succeed("chmod 644 /root/.ssh/authorized_keys")
38 |
39 | # Setup SSH for connection from host
40 | vm.succeed(
41 | "sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config"
42 | )
43 |
44 | # Start SSH service
45 | vm.succeed(f"systemctl start {ssh_service}")
46 |
47 | # Forward SSH port using vm.forward_port method
48 | ssh_port = 2222
49 | vm.forward_port(host_port=ssh_port, guest_port=22)
50 |
51 | # Use temporary file for SSH key with automatic cleanup
52 | with tempfile.NamedTemporaryFile(mode='w', delete=True, suffix='_ssh_key') as temp_key:
53 | temp_key_path = temp_key.name
54 |
55 | # Copy SSH private key to temp file with correct permissions
56 | shutil.copy2("${./modules/ssh-keys/ssh}", temp_key_path)
57 | os.chmod(temp_key_path, 0o600)
58 |
59 | nixos_anywhere_cmd = [
60 | "${nixos-anywhere}/bin/nixos-anywhere",
61 | "-i", temp_key_path,
62 | "--ssh-port", str(ssh_port),
63 | "--post-kexec-ssh-port", "2222",
64 | "--phases", "kexec",
65 | "--kexec", "${kexec-installer}",
66 | "--store-paths", "${system-to-install.config.system.build.diskoScriptNoDeps}",
67 | "${system-to-install.config.system.build.toplevel}",
68 | "--debug",
69 | "root@localhost"
70 | ]
71 |
72 | result = subprocess.run(nixos_anywhere_cmd, check=False)
73 |
74 | if result.returncode != 0:
75 | print(f"nixos-anywhere failed with exit code {result.returncode}")
76 | vm.succeed("dmesg | tail -n 50")
77 | vm.succeed("journalctl -n 50")
78 | raise Exception(f"nixos-anywhere command failed with exit code {result.returncode}")
79 |
80 | # Test SSH connection to verify we're in NixOS kexec environment
81 | check_cmd = [
82 | "${pkgs.openssh}/bin/ssh", "-v",
83 | "-i", temp_key_path,
84 | "-p", "2222",
85 | "-o", "StrictHostKeyChecking=no",
86 | "-o", "UserKnownHostsFile=/dev/null",
87 | "root@localhost",
88 | "cat /etc/os-release"
89 | ]
90 |
91 | test_result = subprocess.run(check_cmd, check=True, stdout=subprocess.PIPE, text=True)
92 | assert "nixos" in test_result.stdout.lower(), f"Expected NixOS environment but got: {test_result.stdout}"
93 |
94 | # After kexec we no longer have the machine driver,
95 | # so we need to let the VM crash because the test driver backdoor gets confused by the terminal output.
96 | vm.crash()
97 | '';
98 | }).sandboxed
99 |
--------------------------------------------------------------------------------
/tests/modules/installer.nix:
--------------------------------------------------------------------------------
1 | { pkgs, inputs, ... }:
2 | {
3 | system.activationScripts.rsa-key = ''
4 | ${pkgs.coreutils}/bin/install -D -m600 ${./ssh-keys/ssh} /root/.ssh/install_key
5 | '';
6 |
7 | environment.systemPackages = [ inputs.nixos-anywhere ];
8 |
9 | environment.etc = {
10 | "nixos-anywhere/disko".source = inputs.system-to-install.config.system.build.diskoScriptNoDeps;
11 | "nixos-anywhere/system-to-install".source = inputs.system-to-install.config.system.build.toplevel;
12 | "nixos-anywhere/kexec-installer".source = inputs.kexec-installer;
13 | };
14 | }
15 |
--------------------------------------------------------------------------------
/tests/modules/ssh-keys/ssh:
--------------------------------------------------------------------------------
1 | -----BEGIN OPENSSH PRIVATE KEY-----
2 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
3 | QyNTUxOQAAACDM4kPg4V7DMYceuA8VIUdBvw30gM9qagERA8v1VGBBBQAAAJDpULAq6VCw
4 | KgAAAAtzc2gtZWQyNTUxOQAAACDM4kPg4V7DMYceuA8VIUdBvw30gM9qagERA8v1VGBBBQ
5 | AAAECpjfl5WMMIDvEyZJTeXzRNFzpDpj4fqdIXHZauKAlE5MziQ+DhXsMxhx64DxUhR0G/
6 | DfSAz2pqAREDy/VUYEEFAAAACWxhc3NAbW9ycwECAwQ=
7 | -----END OPENSSH PRIVATE KEY-----
8 |
--------------------------------------------------------------------------------
/tests/modules/ssh-keys/ssh.pub:
--------------------------------------------------------------------------------
1 | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMziQ+DhXsMxhx64DxUhR0G/DfSAz2pqAREDy/VUYEEF
2 |
--------------------------------------------------------------------------------
/tests/modules/system-to-install.nix:
--------------------------------------------------------------------------------
1 | { modulesPath, self, lib, ... }: {
2 | imports = [
3 | (modulesPath + "/testing/test-instrumentation.nix")
4 | (modulesPath + "/profiles/qemu-guest.nix")
5 | (modulesPath + "/profiles/minimal.nix")
6 | ];
7 | networking.hostName = lib.mkDefault "nixos-anywhere";
8 | documentation.enable = false;
9 | hardware.enableAllFirmware = false;
10 | networking.hostId = "8425e349"; # from profiles/base.nix, needed for zfs
11 | boot.zfs.devNodes = "/dev/disk/by-uuid"; # needed because /dev/disk/by-id is empty in qemu-vms
12 | disko.devices = {
13 | disk = {
14 | vda = {
15 | device = "/dev/vda";
16 | type = "disk";
17 | content = {
18 | type = "gpt";
19 | partitions = {
20 | boot = {
21 | size = "1M";
22 | type = "EF02";
23 | };
24 | ESP = {
25 | size = "100M";
26 | type = "EF00";
27 | content = {
28 | type = "filesystem";
29 | format = "vfat";
30 | mountpoint = "/boot";
31 | };
32 | };
33 | root = {
34 | size = "100%";
35 | content = {
36 | type = "filesystem";
37 | format = "ext4";
38 | mountpoint = "/";
39 | };
40 | };
41 | };
42 | };
43 | };
44 | };
45 | };
46 | }
47 |
--------------------------------------------------------------------------------
/treefmt/flake-module.nix:
--------------------------------------------------------------------------------
1 | { inputs, ... }: {
2 | imports = [
3 | inputs.treefmt-nix.flakeModule
4 | ];
5 | perSystem = { config, pkgs, ... }: {
6 | treefmt = {
7 | projectRootFile = "flake.nix";
8 | programs.mdsh.enable = true;
9 | programs.nixpkgs-fmt.enable = true;
10 | programs.shellcheck.enable = true;
11 | programs.shfmt.enable = true;
12 | programs.terraform.enable = true;
13 | programs.deno.enable = !pkgs.deno.meta.broken;
14 | settings.formatter.shellcheck.options = [ "-s" "bash" ];
15 | };
16 | formatter = config.treefmt.build.wrapper;
17 | };
18 | }
19 |
--------------------------------------------------------------------------------