What is Terraform / OpenTofu?

This page explains Terraform enough to motivate and explain terranix easily. You may already be using Terraform and Nix separately and wonder exactly how one combines the two. Or either one of the two may be new to you.

You can skip this section if all of these words make perfect sense:

  • HCL
  • provider
  • tfstate

Terraform is a declarative infrastructure-as-code tool.

OpenTofu is the open-source fork of Terraform.

Instead of writing imperative scripts that get executed to create or configure infrastructure resources, like you would with Ansible, you describe the resources you want to exist and their configuration, but not the steps to get there. Using the provider of a given service or platform, the accumulated tfstate, the Terraform plan and apply commands, resources are created, modified, or deleted to reflect the description.

You have a text file under version control that closely reflects the state of:

  • hardware
  • virtual machines
  • VPC networks and policies
  • GitHub account policies
  • DNS records
  • etc.

Terraform vs. OpenTofu

The Open Source fork of Terraform is OpenTofu.

OpenTofu was announced in Sept. 2023 after Terraform switched to a Business Source License (BSL).

Both are available in nixpkgs and are compatible with terranix, but Terraform requires allowing unfree software using e.g. nixpkgs.config.allowUnfree = true; so it's slightly easier to create working examples with OpenTofu.

HCL vs. Terraform JSON

Terraform comes with two syntaxes, HCL and Terraform JSON.

HCL compiles to Terraform JSON.

The main role of terranix is to compile Nix to Terraform JSON.

Here is a complete resource definition for a Hetzner VPS one could store to myserver.tf:

# Control the API token with a variable to hide it from version control
variable "hcloud_token" {}

# Configure the Hetzner Cloud Provider
provider "hcloud" {
  token = "${var.hcloud_token}"
}

resource "hcloud_server" "myserver" {
  image       = "debian-12"
  name        = "myserver.example.org"
  server_type = "cx22"
  datacenter  = "nbg1-dc3"
  ssh_keys    = [ hcloud_ssh_key.my_key.id ]
  public_net {
    ipv4_enabled = true
    ipv6_enabled = true
  }
}

resource "hcloud_ssh_key" "my_key" {
  name       = "my-ssh-key"
  public_key = file("~/.ssh/id_ed25519.pub")
}

The same definition using Terraform JSON looks like the following.

One could store this to myserver.tf.json:

{
  "variable": {
    "hcloud_token": {}
  },
  "provider": {
    "hcloud": {
      "token": "${var.hcloud_token}"
    }
  },
  "resource": {
    "hcloud_server": {
      "myserver": {
        "image": "debian-12",
        "name": "myserver.example.org",
        "server_type": "cx22",
        "datacenter": "nbg1-dc3",
        "ssh_keys": ["${hcloud_ssh_key.my_key.id}"],
        "public_net": {
          "ipv4_enabled": true,
          "ipv6_enabled": true
        }
      }
    },
    "hcloud_ssh_key": {
      "my_key": {
        "name": "my-ssh-key",
        "public_key": "${file(\"~/.ssh/id_ed25519.pub\")}"
      }
    }
  }
}

What is myserver.tf / myserver.tf.json?

myserver.tf or myserver.tf.json is the file that contains the setup descriptions to be realized behind one or multiple APIs. The majority of your work will be to create and maintain these files.

Besides a declarative description of your resource, Terraform also requires:

  • the terraform (or opentofu) CLI for running plan or apply commands
  • the provider software to be installed for each relevant service or platform
  • an API token for each relevant service or platform
  • a tfstate

What are providers?

Providers are plugins that enables Terraform to interact with external services and platforms, like cloud or SaaS providers, through their APIs, translating Terraform's resource definitions into API calls.

A huge list of providers is available on Terraform.io.

Here is an example of how you would define multiple providers of the same kind

provider.aws = [
  { region = "us-east-1"; }
  { region = "eu-central-1"; alias = "eu"; }
];

What is Terraform state?

Terraform state, or tfstate, keeps track of the resources that Terraform has created and is currently managing.

Terraform is not capable of seeing the state behind APIs, because APIs never share all information. This is why Terraform creates a state file on every run to provide information for the next run.

This means a part of managing resources with Terraform is to store the tfstate and keep it in sync.

It is not always clear what ends up in this state file, so always handle secrets and tfstate with care!