Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

terraform: add var.extra_build_env_vars, closes #413 #414

Closed

Conversation

KiaraGrouwstra
Copy link
Contributor

@KiaraGrouwstra KiaraGrouwstra commented Oct 23, 2024

This PR adds a Terraform input variable I so far named extra_build_env_vars (suggestions welcome).
This allows passing in a map of attributes from Terraform to expose to nixos-anywhere's build step to make them available as environment variables within a NixOS configuration.

For this implementation I opted to leave actually passing the info to the user, so people stay in control over what info gets exposed where.
I opted to focus on allowing to pass info thru environment variables here figuring that would be less prone to fighting flakes over what is staged to Git, as committing info to be passed here may be undesirable both for ephemeral noise (if not also over sensitive info - tho any info passed this way lands in /nix/store/ as well).

To pull this off I used --impure plus eval - feedback on this would be great.

On the Terraform side as a consumer, to best enjoy this I have had to resort to some more involved logic as well, that gets easy when using Terraform from Nix. As an example (based on Hetzner Cloud), using the lib.tfRef function from Terranix I would use something like:

let
  servers = ...;
  variable = ...;
  data = ...;
  resource = ...;
in
{
  inherit variable data resource;
  module =
    lib.mapAttrs (server_name: _server_config: let
    in {
      # pin module version by nix flake inputs
      source = "github.com/numtide/nixos-anywhere?ref=${inputs.nixos-anywhere.sourceInfo.rev}/terraform/all-in-one";
      ...
      extra_build_env_vars = {
        # all variables
        # TF_VARS = lib.strings.toJSON (lib.mapAttrs (k: _: lib.tfRef "jsonencode(var.${k})") variable);
        # non-sensitive variables
        TF_VARS = lib.tfRef "jsonencode(${lib.strings.toJSON (lib.mapAttrs (k: _: lib.tfRef "var.${k}") (lib.filterAttrs (_k: v: !(v ? sensitive && v.sensitive)) variable))})";
        TF_DATA = lib.tfRef "jsonencode(${lib.strings.toJSON (lib.mapAttrs (type: instances: lib.mapAttrs (k: _: lib.tfRef "data.${type}.${k}") instances) data)})";
        TF_RESOURCES = lib.tfRef "jsonencode(${lib.strings.toJSON (lib.mapAttrs (type: instances: lib.mapAttrs (k: _: lib.tfRef "resource.${type}.${k}") instances) resource)})";
        TF_SERVER = lib.tfRef "jsonencode(resource.hcloud_server.${server_name})";
        SERVER_NAME = server_name;
      };
    })
    servers;
}

You can then verify their contents from your nixosConfigurations like:

          lib.nixosSystem {
            inherit system;
            modules = [
              {
                environment.etc = {
                  TF_VARS.text = builtins.getEnv "TF_VARS";
                  TF_DATA.text = builtins.getEnv "TF_DATA";
                  TF_RESOURCES.text = builtins.getEnv "TF_RESOURCES";
                  TF_SERVER.text = builtins.getEnv "TF_SERVER";
                  SERVER_NAME.text = builtins.getEnv "SERVER_NAME";
                };
              }
            ];
          };

-> ls /etc/ -> cat /etc/TF_SERVER

(in practice one would instead probably parse these back to nix values to grab specific properties from, e.g. like: lib.strings.fromJSON (builtins.getEnv "TF_SERVER"))

if [[ -n ${file-} ]] && [[ -e ${file-} ]]; then
# shellcheck disable=SC2086
out=$(nix build --no-link --json $options -f "$file" "$attribute")
out=$(eval "env ${vars} nix build --no-link --json --impure $options -f '$file' '$attribute'")
Copy link
Member

@Mic92 Mic92 Oct 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mhm --impure by default is not so nice.

Have you considered the following approach?

https://github.com/NixOS/nixos-wiki-infra/blob/main/terraform/nixos-wiki/nixos_vars.tf

Basically one can use terraform to generate a json file and using git add to make sure it's recognized by the flake. Maybe we can encode this pattern into the terraform module instead of using builtins.getEnv?
The big upside on this is that, you are no longer forced to always have to use terraform to build or deploy your nixos configuration.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just imagine you want your machines to be build by CI. Before you could just do nixos-rebuild build --flake .mymachine, now you have to do some sort of terraform plan.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interface wise I would propose a nix-file module that would use git add --intent-to-add --force -- ./file >/dev/null 2>&1 || true to make sure that it is added to your git repository (if one exists). It would take content as a parameter:

module "nixos_vars" { 
   source  = "../nix-file"
   content = jsonencode({})
   filename = "${path.module}/nixos-vars.json"
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm. if one could make a file without terraform, why couldn't one similarly make env vars, if the nixos configuration is indeed made to require them?

locally such a nix-file module might give some noise in version control, tho one could opt to just never use git add . anymore. whereas deploying from CI might circumvent that, it would seem a bit annoying in a development setting. going by file may have use-cases as well, tho it'd be nice to see a route more amenable to the common use-case of git + flakes + development supported as well.

on --impure, maybe a conditional could be added.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we discussed it's possible to achieve your goal without impure evaluation. Check how to import json into a nixos configuration: https://github.com/nix-community/disko/blob/09a776702b004fdf9c41a024e1299d575ee18a7d/install-cli.nix#L56

disko-install still adds --impure. However it's possible to use getFlake also in a pure evaluation: Check out this code in clan: https://git.clan.lol/clan/clan-core/src/commit/bcf2cd1814bbef57c74b0b4eec8f6a7cc0479475/pkgs/clan-cli/clan_cli/machines/machines.py#L229

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KiaraGrouwstra@75e09d1 also looks good.

Copy link
Contributor Author

@KiaraGrouwstra KiaraGrouwstra Oct 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Mic92 thanks for elaborating - i originally hadn't quite figured out the tempfile thing. i'll try and see if I can use your mentioned approach to resolve the flake+git friction.

Copy link
Contributor Author

@KiaraGrouwstra KiaraGrouwstra Oct 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

notes to self to reconcile the PRs, before i forget: info may be passed purely without staging to git thru nix build --expr, reading a file by builtins.fetchTree to pass the content as file to avoid stack overflows based on content size. the original lib.nixosSystem can then be wrapped so as to e.g. write the extra info to some file.

edit: potential alternative: nix build path:.

@@ -14,3 +14,9 @@ variable "nix_options" {
description = "the options of nix"
default = {}
}

variable "extra_build_env_vars" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in general it would be fine to have environment variables or flags passed to nixos-rebuild. This could be used for injecting binary cache configuration.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, i'll check it out. i still got a few question marks around say how that would handle conditionals based on environment variables, but i guess i can just try that.

@nix-community nix-community deleted a comment from mergify bot Oct 24, 2024
@nix-community nix-community deleted a comment from mergify bot Oct 24, 2024
KiaraGrouwstra added a commit to KiaraGrouwstra/nixos-anywhere that referenced this pull request Oct 26, 2024
This PR adds a Terraform input variable I so far named `content`.
This allows passing in a string from Terraform to expose to
nixos-anywhere's build step to make available as a file (default:
`./nixos-vars.json`) within a NixOS configuration, as suggested by
@Mic92 at nix-community#414.
Note the file is staged even if added to gitignore, making this less
suited for development in case the file includes ephemeral content.
As a result, I would consider this approach complementary rather than as
superseding nix-community#414.

Example usage:

```nix
let
  servers = ...;
  variable = ...;
  data = ...;
  resource = ...;
in
{
  inherit variable data resource;
  module =
    lib.mapAttrs (server_name: _server_config: let
    in {
      # pin module version by nix flake inputs
      source =
"github.com/numtide/nixos-anywhere?ref=${inputs.nixos-anywhere.sourceInfo.rev}/terraform/all-in-one";
      ...
      content = lib.tfRef "jsonencode(${lib.strings.toJSON {
        # all variables
        # TF_VARS = lib.mapAttrs (k: _: lib.tfRef
"jsonencode(var.${k})") variable;
        # non-sensitive variables
        TF_VARS = lib.mapAttrs (k: _: lib.tfRef "var.${k}")
(lib.filterAttrs (_k: v: !(v ? sensitive && v.sensitive)) variable);
        TF_DATA = lib.mapAttrs (type: instances: lib.mapAttrs (k: _:
tfRef "data.${type}.${k}") instances) data;
        TF_RESOURCES = lib.mapAttrs (type: instances: lib.mapAttrs (k:
_: tfRef "resource.${type}.${k}") instances) resource;
        TF_SERVER = lib.tfRef "resource.hcloud_server.${server_name}";
        SERVER_NAME = server_name;
      }})";
    })
    servers;
}
```

You can then verify their contents from your `nixosConfigurations` like:

```nix
          lib.nixosSystem {
            inherit system;
            modules = [
              {
                environment.etc."nixos-vars.json".source =
./nixos-vars.json;
              }
            ];
          };
```

-> `ls /etc/` -> `cat /etc/nixos-vars.json`

(in practice one would instead probably parse these back to nix values
to grab specific properties from, e.g. like: `lib.strings.fromJSON
./nixos-vars.json`)
KiaraGrouwstra added a commit to KiaraGrouwstra/nixos-anywhere that referenced this pull request Oct 26, 2024
This PR adds a Terraform input variable I so far named `content`.
This allows passing in a string from Terraform to expose to
nixos-anywhere's build step to make available as a file (default:
`./nixos-vars.json`) within a NixOS configuration, as suggested by
@Mic92 at nix-community#414.
Note the file is staged even if added to gitignore, making this less
suited for development in case the file includes ephemeral content.
As a result, I would consider this approach complementary rather than as
superseding nix-community#414.

Example usage:

```nix
let
  servers = ...;
  variable = ...;
  data = ...;
  resource = ...;
in
{
  inherit variable data resource;
  module =
    lib.mapAttrs (server_name: _server_config: let
    in {
      # pin module version by nix flake inputs
      source =
"github.com/numtide/nixos-anywhere?ref=${inputs.nixos-anywhere.sourceInfo.rev}/terraform/all-in-one";
      ...
      content = lib.tfRef "jsonencode(${lib.strings.toJSON {
        # all variables
        # TF_VARS = lib.mapAttrs (k: _: lib.tfRef
"jsonencode(var.${k})") variable;
        # non-sensitive variables
        TF_VARS = lib.mapAttrs (k: _: lib.tfRef "var.${k}")
(lib.filterAttrs (_k: v: !(v ? sensitive && v.sensitive)) variable);
        TF_DATA = lib.mapAttrs (type: instances: lib.mapAttrs (k: _:
tfRef "data.${type}.${k}") instances) data;
        TF_RESOURCES = lib.mapAttrs (type: instances: lib.mapAttrs (k:
_: tfRef "resource.${type}.${k}") instances) resource;
        TF_SERVER = lib.tfRef "resource.hcloud_server.${server_name}";
        SERVER_NAME = server_name;
      }})";
    })
    servers;
}
```

You can then verify their contents from your `nixosConfigurations` like:

```nix
          lib.nixosSystem {
            inherit system;
            modules = [
              {
                environment.etc."nixos-vars.json".source =
./nixos-vars.json;
              }
            ];
          };
```

-> `ls /etc/` -> `cat /etc/nixos-vars.json`

(in practice one would instead probably parse these back to nix values
to grab specific properties from, e.g. like: `lib.strings.fromJSON
./nixos-vars.json`)
KiaraGrouwstra added a commit to KiaraGrouwstra/nixos-anywhere that referenced this pull request Oct 26, 2024
This PR adds a Terraform input variable I so far named `content`.
This allows passing in a string from Terraform to expose to
nixos-anywhere's build step to make available as a file (default:
`./nixos-vars.json`) within a NixOS configuration, as suggested by
@Mic92 at nix-community#414.
Note the file is staged even if added to gitignore, making this less
suited for development in case the file includes ephemeral content.
As a result, I would consider this approach complementary rather than as
superseding nix-community#414.

Example usage:

```nix
let
  servers = ...;
  variable = ...;
  data = ...;
  resource = ...;
in
{
  inherit variable data resource;
  module =
    lib.mapAttrs (server_name: _server_config: let
    in {
      # pin module version by nix flake inputs
      source =
"github.com/numtide/nixos-anywhere?ref=${inputs.nixos-anywhere.sourceInfo.rev}/terraform/all-in-one";
      ...
      content = lib.tfRef "jsonencode(${lib.strings.toJSON {
        # all variables
        # TF_VARS = lib.mapAttrs (k: _: lib.tfRef
"jsonencode(var.${k})") variable;
        # non-sensitive variables
        TF_VARS = lib.mapAttrs (k: _: lib.tfRef "var.${k}")
(lib.filterAttrs (_k: v: !(v ? sensitive && v.sensitive)) variable);
        TF_DATA = lib.mapAttrs (type: instances: lib.mapAttrs (k: _:
tfRef "data.${type}.${k}") instances) data;
        TF_RESOURCES = lib.mapAttrs (type: instances: lib.mapAttrs (k:
_: tfRef "resource.${type}.${k}") instances) resource;
        TF_SERVER = lib.tfRef "resource.hcloud_server.${server_name}";
        SERVER_NAME = server_name;
      }})";
    })
    servers;
}
```

You can then verify their contents from your `nixosConfigurations` like:

```nix
          lib.nixosSystem {
            inherit system;
            modules = [
              {
                environment.etc."nixos-vars.json".source =
./nixos-vars.json;
              }
            ];
          };
```

-> `ls /etc/` -> `cat /etc/nixos-vars.json`

(in practice one would instead probably parse these back to nix values
to grab specific properties from, e.g. like: `lib.strings.fromJSON
./nixos-vars.json`)
KiaraGrouwstra added a commit to KiaraGrouwstra/nixos-anywhere that referenced this pull request Oct 26, 2024
This PR adds a Terraform input variable I so far named `content`.
This allows passing in a string from Terraform to expose to
nixos-anywhere's build step to make available as a file (default:
`./nixos-vars.json`) within a NixOS configuration, as suggested by
@Mic92 at nix-community#414.
Note the file is staged even if added to gitignore, making this less
suited for development in case the file includes ephemeral content.
As a result, I would consider this approach complementary rather than as
superseding nix-community#414.

Example usage:

```nix
let
  servers = ...;
  variable = ...;
  data = ...;
  resource = ...;
in
{
  inherit variable data resource;
  module =
    lib.mapAttrs (server_name: _server_config: let
    in {
      # pin module version by nix flake inputs
      source =
"github.com/numtide/nixos-anywhere?ref=${inputs.nixos-anywhere.sourceInfo.rev}/terraform/all-in-one";
      ...
      content = lib.tfRef "jsonencode(${lib.strings.toJSON {
        # all variables
        # TF_VARS = lib.mapAttrs (k: _: lib.tfRef
"jsonencode(var.${k})") variable;
        # non-sensitive variables
        TF_VARS = lib.mapAttrs (k: _: lib.tfRef "var.${k}")
(lib.filterAttrs (_k: v: !(v ? sensitive && v.sensitive)) variable);
        TF_DATA = lib.mapAttrs (type: instances: lib.mapAttrs (k: _:
tfRef "data.${type}.${k}") instances) data;
        TF_RESOURCES = lib.mapAttrs (type: instances: lib.mapAttrs (k:
_: tfRef "resource.${type}.${k}") instances) resource;
        TF_SERVER = lib.tfRef "resource.hcloud_server.${server_name}";
        SERVER_NAME = server_name;
      }})";
    })
    servers;
}
```

You can then verify their contents from your `nixosConfigurations` like:

```nix
          lib.nixosSystem {
            inherit system;
            modules = [
              {
                environment.etc."nixos-vars.json".source =
./nixos-vars.json;
              }
            ];
          };
```

-> `ls /etc/` -> `cat /etc/nixos-vars.json`

(in practice one would instead probably parse these back to nix values
to grab specific properties from, e.g. like: `lib.strings.fromJSON
./nixos-vars.json`)
KiaraGrouwstra added a commit to KiaraGrouwstra/nixos-anywhere that referenced this pull request Oct 26, 2024
This PR adds a Terraform input variable I so far named `content`.
This allows passing in a string from Terraform to expose to
nixos-anywhere's build step to make available as a file (default:
`./nixos-vars.json`) within a NixOS configuration, as suggested by
@Mic92 at nix-community#414.
Note the file is staged even if added to gitignore, making this less
suited for development in case the file includes ephemeral content.
As a result, I would consider this approach complementary rather than as
superseding nix-community#414.

Example usage:

```nix
let
  servers = ...;
  variable = ...;
  data = ...;
  resource = ...;
in
{
  inherit variable data resource;
  module =
    lib.mapAttrs (server_name: _server_config: let
    in {
      # pin module version by nix flake inputs
      source =
"github.com/numtide/nixos-anywhere?ref=${inputs.nixos-anywhere.sourceInfo.rev}/terraform/all-in-one";
      ...
      content = lib.tfRef "jsonencode(${lib.strings.toJSON {
        # all variables
        # TF_VARS = lib.mapAttrs (k: _: lib.tfRef
"jsonencode(var.${k})") variable;
        # non-sensitive variables
        TF_VARS = lib.mapAttrs (k: _: lib.tfRef "var.${k}")
(lib.filterAttrs (_k: v: !(v ? sensitive && v.sensitive)) variable);
        TF_DATA = lib.mapAttrs (type: instances: lib.mapAttrs (k: _:
tfRef "data.${type}.${k}") instances) data;
        TF_RESOURCES = lib.mapAttrs (type: instances: lib.mapAttrs (k:
_: tfRef "resource.${type}.${k}") instances) resource;
        TF_SERVER = lib.tfRef "resource.hcloud_server.${server_name}";
        SERVER_NAME = server_name;
      }})";
    })
    servers;
}
```

You can then verify their contents from your `nixosConfigurations` like:

```nix
          lib.nixosSystem {
            inherit system;
            modules = [
              {
                environment.etc."nixos-vars.json".source =
./nixos-vars.json;
              }
            ];
          };
```

-> `ls /etc/` -> `cat /etc/nixos-vars.json`

(in practice one would instead probably parse these back to nix values
to grab specific properties from, e.g. like: `lib.strings.fromJSON
./nixos-vars.json`)
KiaraGrouwstra added a commit to KiaraGrouwstra/nixos-anywhere that referenced this pull request Oct 30, 2024
This PR adds a Terraform input variable named `content`.
This allows passing in a string from Terraform to expose to
NixOS's run-time to make available as a file (default:
`/etc/nixos-vars.json`) as suggested by @Mic92 at nix-community#414.

This third iteration wraps the original `lib.nixosSystem`
call to allow passing info without either use of `--impure`
or having to stage to Git.

Note the file is staged even if added to gitignore, making this less
suited for development in case the file includes ephemeral content.
As a result, I would consider this approach complementary rather than as
superseding nix-community#414.

Example usage:

```nix
let
  servers = ...;
  variable = ...;
  data = ...;
  resource = ...;
in
{
  inherit variable data resource;
  module =
    lib.mapAttrs (server_name: _server_config: let
    in {
      # pin module version by nix flake inputs
      source =
"github.com/numtide/nixos-anywhere?ref=${inputs.nixos-anywhere.sourceInfo.rev}/terraform/all-in-one";
      ...
      content = lib.tfRef "jsonencode(${lib.strings.toJSON {
        # all variables
        # TF_VARS = lib.mapAttrs (k: _: lib.tfRef
"jsonencode(var.${k})") variable;
        # non-sensitive variables
        TF_VARS = lib.mapAttrs (k: _: lib.tfRef "var.${k}")
(lib.filterAttrs (_k: v: !(v ? sensitive && v.sensitive)) variable);
        TF_DATA = lib.mapAttrs (type: instances: lib.mapAttrs (k: _:
tfRef "data.${type}.${k}") instances) data;
        TF_RESOURCES = lib.mapAttrs (type: instances: lib.mapAttrs (k:
_: tfRef "resource.${type}.${k}") instances) resource;
        TF_SERVER = lib.tfRef "resource.hcloud_server.${server_name}";
        SERVER_NAME = server_name;
      }})";
    })
    servers;
}
```

You can then verify their contents from your `nixosConfigurations` like:

`cat /etc/nixos-vars.json`

However, so far I did not yet manage to reach my goal:

- On this attempt I have so far just exposed the content to run-time,
  rather than to the intended NixOS build-time.
  To address this, I consider looking into injecting thru `specialArgs`,
  tho I am not sure yet this would work, and feel open to suggestions.
- When putting the content file into `.gitignore`,
  the build currently errors on same NAR hash mismatch on the file.
KiaraGrouwstra added a commit to KiaraGrouwstra/nixos-anywhere that referenced this pull request Oct 30, 2024
This PR adds a Terraform input variable named `content`.
This allows passing in a string from Terraform to expose to
NixOS's run-time to make available as a file (default:
`/etc/nixos-vars.json`) as suggested by @Mic92 at nix-community#414.

This third implementation wraps the original `lib.nixosSystem`
call to allow passing info without either use of `--impure`
or having to stage to Git.

Example usage:

```nix
let
  servers = ...;
  variable = ...;
  data = ...;
  resource = ...;
in
{
  inherit variable data resource;
  module =
    lib.mapAttrs (server_name: _server_config: let
    in {
      # pin module version by nix flake inputs
      source =
"github.com/numtide/nixos-anywhere?ref=${inputs.nixos-anywhere.sourceInfo.rev}/terraform/all-in-one";
      ...
      special_args = lib.tfRef "jsonencode(${lib.strings.toJSON {
        tf = {
          inherit server_name;
          # all variables
          # var = lib.mapAttrs (k: _: lib.tfRef
  "jsonencode(var.${k})") variable;
          # non-sensitive variables
          var = lib.mapAttrs (k: _: lib.tfRef "var.${k}")
  (lib.filterAttrs (_k: v: !(v ? sensitive && v.sensitive)) variable);
          data = lib.mapAttrs (type: instances: lib.mapAttrs (k: _:
  tfRef "data.${type}.${k}") instances) data;
          resource = lib.mapAttrs (type: instances: lib.mapAttrs (k:
  _: tfRef "resource.${type}.${k}") instances) resource;
          server = lib.tfRef "resource.hcloud_server.${server_name}";
        };
      }})";
    })
    servers;
}
```

You can then use these in your `nixosConfigurations` thru the `tf` argument.

Status:

- [ ] resolve occasional NAR hash mismatches
- [ ] Test putting the content file into `.gitignore`.
KiaraGrouwstra added a commit to KiaraGrouwstra/nixos-anywhere that referenced this pull request Oct 30, 2024
This PR adds a Terraform input variable named `content`.
This allows passing in a string from Terraform to expose to
NixOS's run-time to make available as a file (default:
`/etc/nixos-vars.json`) as suggested by @Mic92 at nix-community#414.

This third implementation wraps the original `lib.nixosSystem`
call to allow passing info without either use of `--impure`
or having to stage to Git.

Example usage:

```nix
let
  servers = ...;
  variable = ...;
  data = ...;
  resource = ...;
in
{
  inherit variable data resource;
  module =
    lib.mapAttrs (server_name: _server_config: let
    in {
      # pin module version by nix flake inputs
      source =
"github.com/numtide/nixos-anywhere?ref=${inputs.nixos-anywhere.sourceInfo.rev}/terraform/all-in-one";
      ...
      special_args = lib.tfRef "jsonencode(${lib.strings.toJSON {
        tf = {
          inherit server_name;
          # all variables
          # var = lib.mapAttrs (k: _: lib.tfRef
  "jsonencode(var.${k})") variable;
          # non-sensitive variables
          var = lib.mapAttrs (k: _: lib.tfRef "var.${k}")
  (lib.filterAttrs (_k: v: !(v ? sensitive && v.sensitive)) variable);
          data = lib.mapAttrs (type: instances: lib.mapAttrs (k: _:
  tfRef "data.${type}.${k}") instances) data;
          resource = lib.mapAttrs (type: instances: lib.mapAttrs (k:
  _: tfRef "resource.${type}.${k}") instances) resource;
          server = lib.tfRef "resource.hcloud_server.${server_name}";
        };
      }})";
    })
    servers;
}
```

You can then use these in your `nixosConfigurations` thru the `tf` argument.

Status:

- [ ] Resolve occasional NAR hash mismatches
- [ ] Test putting the content file into `.gitignore`.
- [ ] Let the user pass in a TF map rather than string.
KiaraGrouwstra added a commit to KiaraGrouwstra/nixos-anywhere that referenced this pull request Oct 31, 2024
This PR adds a Terraform input variable named `special_args`.
This allows passing in a string from Terraform to expose to
NixOS's `specialArgs` at build-time.

This implementation extends the original `lib.nixosSystem`
call to allow passing info without either use of `--impure`
or having to stage to Git, thanks to @Mic92's suggestion at nix-community#414.

Example usage:

```nix
let
  servers = ...;
  variable = ...;
  data = ...;
  resource = ...;
in
{
  inherit variable data resource;
  module =
    lib.mapAttrs (server_name: _server_config: let
    in {
      # pin module version by nix flake inputs
      source =
"github.com/numtide/nixos-anywhere?ref=${inputs.nixos-anywhere.sourceInfo.rev}/terraform/all-in-one";
      ...
      special_args = lib.tfRef "jsonencode(${lib.strings.toJSON {
        tf = {
          inherit server_name;
          # all variables
          # var = lib.mapAttrs (k: _: lib.tfRef
  "var.${k}") variable;
          # non-sensitive variables
          var = lib.mapAttrs (k: _: lib.tfRef "var.${k}")
  (lib.filterAttrs (_k: v: !(v ? sensitive && v.sensitive)) variable);
          data = lib.mapAttrs (type: instances: lib.mapAttrs (k: _:
  tfRef "data.${type}.${k}") instances) data;
          resource = lib.mapAttrs (type: instances: lib.mapAttrs (k:
  _: tfRef "resource.${type}.${k}") instances) resource;
          server = lib.tfRef "resource.hcloud_server.${server_name}";
        };
      }})";
    })
    servers;
}
```

You can then use these in your `nixosConfigurations`,
in this example thru the `tf` argument.
KiaraGrouwstra added a commit to KiaraGrouwstra/nixos-anywhere that referenced this pull request Oct 31, 2024
This PR adds a Terraform input variable named `special_args`.
This allows passing in a string from Terraform to expose to
NixOS's `specialArgs` at build-time.

This implementation extends the original `lib.nixosSystem`
call to allow passing info without either use of `--impure`
or having to stage to Git, thanks to @Mic92's suggestion at nix-community#414.

Example usage:

```nix
let
  servers = ...;
  variable = ...;
  data = ...;
  resource = ...;
in
{
  inherit variable data resource;
  module =
    lib.mapAttrs (server_name: _server_config: let
    in {
      # pin module version by nix flake inputs
      source =
"github.com/numtide/nixos-anywhere?ref=${inputs.nixos-anywhere.sourceInfo.rev}/terraform/all-in-one";
      ...
      special_args = lib.tfRef "jsonencode(${lib.strings.toJSON {
        tf = {
          inherit server_name;
          # all variables
          # var = lib.mapAttrs (k: _: lib.tfRef
  "var.${k}") variable;
          # non-sensitive variables
          var = lib.mapAttrs (k: _: lib.tfRef "var.${k}")
  (lib.filterAttrs (_k: v: !(v ? sensitive && v.sensitive)) variable);
          data = lib.mapAttrs (type: instances: lib.mapAttrs (k: _:
  tfRef "data.${type}.${k}") instances) data;
          resource = lib.mapAttrs (type: instances: lib.mapAttrs (k:
  _: tfRef "resource.${type}.${k}") instances) resource;
          server = lib.tfRef "resource.hcloud_server.${server_name}";
        };
      }})";
    })
    servers;
}
```

You can then use these in your `nixosConfigurations`,
in this example thru the `tf` argument.
KiaraGrouwstra added a commit to KiaraGrouwstra/nixos-anywhere that referenced this pull request Oct 31, 2024
This PR adds a Terraform input variable named `special_args`.
This allows passing in a JSON string from Terraform to expose to
NixOS's `specialArgs` at build-time.

This implementation extends the original `lib.nixosSystem`
call to allow passing info without either use of `--impure`
or having to stage to Git, thanks to @Mic92's suggestion at nix-community#414.

Example usage:

```nix
let
  servers = ...;
  variable = ...;
  data = ...;
  resource = ...;
in
{
  inherit variable data resource;
  module =
    lib.mapAttrs (server_name: _server_config: let
    in {
      # pin module version by nix flake inputs
      source =
"github.com/numtide/nixos-anywhere?ref=${inputs.nixos-anywhere.sourceInfo.rev}/terraform/all-in-one";
      ...
      special_args = lib.tfRef "jsonencode(${lib.strings.toJSON {
        tf = {
          inherit server_name;
          # all variables
          # var = lib.mapAttrs (k: _: lib.tfRef
  "var.${k}") variable;
          # non-sensitive variables
          var = lib.mapAttrs (k: _: lib.tfRef "var.${k}")
  (lib.filterAttrs (_k: v: !(v ? sensitive && v.sensitive)) variable);
          data = lib.mapAttrs (type: instances: lib.mapAttrs (k: _:
  tfRef "data.${type}.${k}") instances) data;
          resource = lib.mapAttrs (type: instances: lib.mapAttrs (k:
  _: tfRef "resource.${type}.${k}") instances) resource;
          server = lib.tfRef "resource.hcloud_server.${server_name}";
        };
      }})";
    })
    servers;
}
```

You can then use these in your `nixosConfigurations`,
in this example thru the `tf` argument.
KiaraGrouwstra added a commit to KiaraGrouwstra/nixos-anywhere that referenced this pull request Oct 31, 2024
…lows passing in a JSON string from Terraform to expose to NixOS's `specialArgs` at build-time.

This implementation extends the original `lib.nixosSystem` call to allow passing info without either use of `--impure` or having to stage to Git, thanks to @Mic92's suggestion at nix-community#414.

Example usage, in this case presuming deployment to a Hetzner Cloud server (`resource.hcloud_server`):

```nix
let
  servers = ...;
  variable = ...;
  data = ...;
  resource = ...;
in
{
  inherit variable data resource;
  module =
    lib.mapAttrs (server_name: _server_config: let
    in {
      # pin module version by nix flake inputs
      source =
"github.com/numtide/nixos-anywhere?ref=${inputs.nixos-anywhere.sourceInfo.rev}/terraform/all-in-one";
      ...
      special_args = lib.tfRef "jsonencode(${lib.strings.toJSON {
        tf = {
          inherit server_name;
          # all variables
          # var = lib.mapAttrs (k: _: lib.tfRef "var.${k}") variable;
          # non-sensitive variables
          var = lib.mapAttrs (k: _: lib.tfRef "var.${k}")
  (lib.filterAttrs (_k: v: !(v ? sensitive && v.sensitive)) variable);
          data = lib.mapAttrs (type: instances: lib.mapAttrs (k: _:
  tfRef "data.${type}.${k}") instances) data;
          resource = lib.mapAttrs (type: instances: lib.mapAttrs (k:
  _: tfRef "resource.${type}.${k}") instances) resource;
          server = lib.tfRef "resource.hcloud_server.${server_name}";
        };
      }})";
    })
    servers;
}
```

You can then use these in your `nixosConfigurations`, in this example thru the `tf` argument.

This implementation differs from my previous attempts in neither being impure, nor adding files.
An advantage of this is it is a relatively simple implementation that should work while getting out of your way.
An [alternative design](nix-community/nixos-anywhere@main...KiaraGrouwstra:nixos-anywhere:tf-info-to-wrapper#diff-2e2429dde4812f0b50c784e8d4c8b93cc9faeb52cce4747733200f65ea5c2bbb) suggested by @Mic92 involved passing the information not directly, but rather thru a file. The idea would be that this might help reduce the risk of stack overflows, tho I have imagined (perhaps naively) that TF info has tended not to get too big, whereas I also had a bit more trouble getting that approach to work properly so far (involving both NARs that would suddenly mismatch again, while I'd also yet to test if one could put such files in `.gitignore`).

As a note on security, information passed this way _will_ hit `/nix/store/`. As such, the above usage example has defaulted to omitting TF variables marked as sensitive.

Note that, while from a UX perspective I would have preferred to allow directly passing nested structures without serializing, TF seemed to allow passing variables just as string or as map of strings - in which case I figured, if we have to manually serialize anyway, it would be a nicer experience to just serialize once (to a string-type variable) than to do so for each special argument (to use a map-of-strings TF variable).
KiaraGrouwstra added a commit to KiaraGrouwstra/nixos-anywhere that referenced this pull request Nov 13, 2024
This allows passing in a map from Terraform to expose to NixOS's `specialArgs` at build-time.
Example usage, in this case presuming deployment to a Hetzner Cloud server (`resource.hcloud_server`):

```nix
let
  servers = ...;
  variable = ...;
  data = ...;
  resource = ...;
in
{
  inherit variable data resource;
  module =
    lib.mapAttrs (server_name: _server_config: let
    in {
      # pin module version by nix flake inputs
      source =
"github.com/numtide/nixos-anywhere?ref=${inputs.nixos-anywhere.sourceInfo.rev}/terraform/all-in-one";
      ...
      special_args = {
        tf = {
          inherit server_name;
          # all variables
          # var = lib.mapAttrs (k: _: lib.tfRef "var.${k}") variable;
          # non-sensitive variables
          var = lib.mapAttrs (k: _: lib.tfRef "var.${k}")
  (lib.filterAttrs (_k: v: !(v ? sensitive && v.sensitive)) variable);
          data = lib.mapAttrs (type: instances: lib.mapAttrs (k: _:
  tfRef "data.${type}.${k}") instances) data;
          resource = lib.mapAttrs (type: instances: lib.mapAttrs (k:
  _: tfRef "resource.${type}.${k}") instances) resource;
          server = lib.tfRef "resource.hcloud_server.${server_name}";
        };
      };
    })
    servers;
}
```

You can then use these in your `nixosConfigurations`, in this example thru the `tf` argument.

As a note on security, information passed this way _will_ hit `/nix/store/`. As such, the above usage example has defaulted to omitting TF variables marked as sensitive.

This PR incorporates ideas from:

- @aanderse, who implemented a similar feature in [teraflops](https://github.com/aanderse/teraflops) that inspired this PR.
- @Mic92, who suggested (see nix-community#414) to extend the original `lib.nixosSystem` call to pass in info without `--impure` or staging to Git.
- @getchoo, who suggested getting the NAR hash by `nix flake prefetch` over `getFlake`, fixing an 'unlocked flake reference' error on (non-Lix) Nix.
- @threddast, who suggested to use TF's `any` type to automate serializing.

An [alternative design](nix-community/nixos-anywhere@main...KiaraGrouwstra:nixos-anywhere:tf-info-to-wrapper#diff-2e2429dde4812f0b50c784e8d4c8b93cc9faeb52cce4747733200f65ea5c2bbb) suggested by @Mic92 involved passing the information not directly, but rather thru a file. The idea would be that this might help reduce the risk of stack overflows, tho I have imagined (perhaps naively) that TF info has tended not to get too big, whereas I also had a bit more trouble getting that approach to work properly so far (involving both NARs that would suddenly mismatch again, while I'd also yet to test if one could put such files in `.gitignore`).
KiaraGrouwstra added a commit to KiaraGrouwstra/nixos-anywhere that referenced this pull request Nov 15, 2024
This allows passing in a map from Terraform to expose to NixOS's `specialArgs` at build-time.
Example usage, in this case presuming deployment to a Hetzner Cloud server (`resource.hcloud_server`):

```nix
let
  servers = ...;
  variable = ...;
  data = ...;
  resource = ...;
in
{
  inherit variable data resource;
  module =
    lib.mapAttrs (server_name: _server_config: let
    in {
      # pin module version by nix flake inputs
      source =
"github.com/numtide/nixos-anywhere?ref=${inputs.nixos-anywhere.sourceInfo.rev}/terraform/all-in-one";
      ...
      special_args = {
        tf = {
          inherit server_name;
          # all variables
          # var = lib.mapAttrs (k: _: lib.tfRef "var.${k}") variable;
          # non-sensitive variables
          var = lib.mapAttrs (k: _: lib.tfRef "var.${k}")
  (lib.filterAttrs (_k: v: !(v ? sensitive && v.sensitive)) variable);
          data = lib.mapAttrs (type: instances: lib.mapAttrs (k: _:
  tfRef "data.${type}.${k}") instances) data;
          resource = lib.mapAttrs (type: instances: lib.mapAttrs (k:
  _: tfRef "resource.${type}.${k}") instances) resource;
          server = lib.tfRef "resource.hcloud_server.${server_name}";
        };
      };
    })
    servers;
}
```

You can then use these in your `nixosConfigurations`, in this example thru the `tf` argument.

As a note on security, information passed this way _will_ hit `/nix/store/`. As such, the above usage example has defaulted to omitting TF variables marked as sensitive.

This PR incorporates ideas from:

- @aanderse, who implemented a similar feature in [teraflops](https://github.com/aanderse/teraflops) that inspired this PR.
- @Mic92, who suggested (see nix-community#414) to extend the original `lib.nixosSystem` call to pass in info without `--impure` or staging to Git.
- @getchoo, who suggested getting the NAR hash by `nix flake prefetch` over `getFlake`, fixing an 'unlocked flake reference' error on (non-Lix) Nix.
- @threddast, who suggested to use TF's `any` type to automate serializing.

An [alternative design](nix-community/nixos-anywhere@main...KiaraGrouwstra:nixos-anywhere:tf-info-to-wrapper#diff-2e2429dde4812f0b50c784e8d4c8b93cc9faeb52cce4747733200f65ea5c2bbb) suggested by @Mic92 involved passing the information not directly, but rather thru a file. The idea would be that this might help reduce the risk of stack overflows, tho I have imagined (perhaps naively) that TF info has tended not to get too big, whereas I also had a bit more trouble getting that approach to work properly so far (involving both NARs that would suddenly mismatch again, while I'd also yet to test if one could put such files in `.gitignore`).
mergify bot pushed a commit that referenced this pull request Nov 16, 2024
This allows passing in a map from Terraform to expose to NixOS's `specialArgs` at build-time.
Example usage, in this case presuming deployment to a Hetzner Cloud server (`resource.hcloud_server`):

```nix
let
  servers = ...;
  variable = ...;
  data = ...;
  resource = ...;
in
{
  inherit variable data resource;
  module =
    lib.mapAttrs (server_name: _server_config: let
    in {
      # pin module version by nix flake inputs
      source =
"github.com/numtide/nixos-anywhere?ref=${inputs.nixos-anywhere.sourceInfo.rev}/terraform/all-in-one";
      ...
      special_args = {
        tf = {
          inherit server_name;
          # all variables
          # var = lib.mapAttrs (k: _: lib.tfRef "var.${k}") variable;
          # non-sensitive variables
          var = lib.mapAttrs (k: _: lib.tfRef "var.${k}")
  (lib.filterAttrs (_k: v: !(v ? sensitive && v.sensitive)) variable);
          data = lib.mapAttrs (type: instances: lib.mapAttrs (k: _:
  tfRef "data.${type}.${k}") instances) data;
          resource = lib.mapAttrs (type: instances: lib.mapAttrs (k:
  _: tfRef "resource.${type}.${k}") instances) resource;
          server = lib.tfRef "resource.hcloud_server.${server_name}";
        };
      };
    })
    servers;
}
```

You can then use these in your `nixosConfigurations`, in this example thru the `tf` argument.

As a note on security, information passed this way _will_ hit `/nix/store/`. As such, the above usage example has defaulted to omitting TF variables marked as sensitive.

This PR incorporates ideas from:

- @aanderse, who implemented a similar feature in [teraflops](https://github.com/aanderse/teraflops) that inspired this PR.
- @Mic92, who suggested (see #414) to extend the original `lib.nixosSystem` call to pass in info without `--impure` or staging to Git.
- @getchoo, who suggested getting the NAR hash by `nix flake prefetch` over `getFlake`, fixing an 'unlocked flake reference' error on (non-Lix) Nix.
- @threddast, who suggested to use TF's `any` type to automate serializing.

An [alternative design](main...KiaraGrouwstra:nixos-anywhere:tf-info-to-wrapper#diff-2e2429dde4812f0b50c784e8d4c8b93cc9faeb52cce4747733200f65ea5c2bbb) suggested by @Mic92 involved passing the information not directly, but rather thru a file. The idea would be that this might help reduce the risk of stack overflows, tho I have imagined (perhaps naively) that TF info has tended not to get too big, whereas I also had a bit more trouble getting that approach to work properly so far (involving both NARs that would suddenly mismatch again, while I'd also yet to test if one could put such files in `.gitignore`).
@Mic92
Copy link
Member

Mic92 commented Nov 16, 2024

This is obsolete now.

@Mic92 Mic92 closed this Nov 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants