Use OpenTofu rather than Terraform
Status: Accepted Date: 2026-08-21 Deciders: Smana (Platform Owner) Related Design: N/A — records a choice predating the design workflow Related: ADR-0011 — the same BUSL relicensing trigger, applied to Vault
Context
This platform’s infrastructure below Kubernetes — network, OpenBao, and both
EKS bootstrap stages — is written in HCL and applied through
terramate script run deploy (see ADR-0012
for what that split from Crossplane covers). The tool that actually executes
that HCL is OpenTofu, not HashiCorp Terraform: mise.toml pins
opentofu = "1.12.6" and carries no terraform entry at all.
The trigger is the one ADR-0011 already records for Vault: in 2023 HashiCorp relicensed its tools under the Business Source License (BUSL). Foundations already states the Terraform-specific consequence plainly: “OpenTofu is the open-source fork of Terraform, created after HashiCorp moved Terraform off an OSI-approved license. It is now a Linux Foundation project with community governance, and it stayed compatible with existing Terraform configuration and providers.” This record exists to state that choice formally and name its actual cost, rather than repeat ADR-0011’s reasoning for a different tool.
Every stack’s versions.tf still opens with a plain terraform { ... }
block and an unmodified required_providers map — nothing about the HCL
itself changed to move the toolchain; only the binary and its pin did.
Decision Drivers
- Licence continuity, the same driver as ADR-0011: a reference platform anyone can clone and run should not depend on a component whose commercial terms one vendor can change underneath it.
- No configuration rewrite. Every stack already declares a plain
terraform { ... }block; whatever replaced Terraform had to keep executing that block unmodified, not force new HCL. - Provider availability. The specific providers this repository’s stacks already pin had to resolve from wherever the replacement tool pulls providers, or the migration cost stops being close to zero.
- Orchestration continuity. Terramate (
opentofu/workflows.tm.hcl,opentofu/config.tm.hcl) sequences and invokes the provisioner; the replacement should not require rewriting that orchestration layer.
Considered Options
Option 1: OpenTofu
Adopt OpenTofu as the pinned provisioner binary, with the existing stacks, providers and Terramate orchestration otherwise unchanged.
Pros:
- Every stack’s
terraform { ... }block andrequired_providersmap runs unmodified —mise.toml’sopentofu = "1.12.6"is the only place the tool name changed. - The full provider set this repository’s stacks declare resolves without
substitution:
gavinbunney/kubectl,hashicorp/aws,hashicorp/cloudinit,hashicorp/helm,hashicorp/http,hashicorp/random,hashicorp/time,hashicorp/tls,hashicorp/vault,integrations/github,tailscale/tailscale. - Terramate’s scripts (
opentofu/workflows.tm.hcl) callglobal.provisioner, set to"tofu"inopentofu/config.tm.hcl— orchestration needed a one-line global change, not a rewrite. - Linux Foundation governance, the same neutral-governance argument ADR-0011 makes for OpenBao.
Cons:
- Younger project than Terraform, with a correspondingly smaller (if actively growing) contributor base and body of third-party writeups.
- A separate registry from Terraform’s — provider publication there depends on each provider’s own maintainer, not on OpenTofu’s compatibility layer (see Consequences).
Option 2: Stay on Terraform under the BUSL
Keep HashiCorp Terraform, accepting the relicensing rather than migrating.
Pros:
- No migration at all — the existing HCL, state and CI already target Terraform.
- Larger ecosystem, official HashiCorp support available if ever needed.
Cons:
- Runs the exact licence risk this decision exists to avoid, on the tool that provisions every stage below Kubernetes — the same argument ADR-0011 makes for Vault, one layer further down the stack.
- Undercuts the point of a reference platform: someone cloning this repo to learn from or run it themselves would inherit a BUSL dependency at the very bottom of it.
Option 3: Pulumi or CDK for Terraform
Rewrite the stacks in a general-purpose language instead of HCL, sidestepping the Terraform/OpenTofu choice entirely.
Pros:
- Leaves the HCL licence question behind rather than choosing between two licence terms for the same configuration language.
- Real programming-language constructs (loops, functions, types) in place of HCL’s more limited expression language.
Cons:
- Every existing stack —
network,openbao/cluster,openbao/management,eks/init,eks/configure— would need a full rewrite, not a binary swap. - A new state model and CI toolchain on top of that rewrite, with nothing equivalent to Terramate’s stack ordering, drift detection or opt-in gating already in place.
- Does not actually remove the dependency this decision is about: both Pulumi’s Terraform bridge and CDK for Terraform still resolve and shell out to the same provider plugins, so the question of which licence those provider binaries ship under does not disappear, it just moves.
Decision Outcome
Chosen option: “Option 1 — OpenTofu”
Rationale: The trigger and the reasoning shape are the same as ADR-0011: the licence risk is real, and removing it costs close to nothing because OpenTofu stayed compatible with the exact configuration and providers already written. Option 2 keeps the licence exposure this decision exists to remove, on the tool that sits below every other stage in the platform. Option 3 removes that exposure too, but only by paying for a full rewrite Option 1 does not require, and even then it does not remove the underlying dependency on Terraform-shaped provider binaries — it moves the question rather than answering it. Option 1 is the only path that resolves the licence question for the cost of a tool-name change.
Consequences
Positive
- Every
versions.tfand itsrequired_providersblock runs unmodified; the only artifact that named the tool differently wasmise.toml. - The confirmed provider set —
gavinbunney/kubectl,hashicorp/aws,hashicorp/cloudinit,hashicorp/helm,hashicorp/http,hashicorp/random,hashicorp/time,hashicorp/tls,hashicorp/vault,integrations/github,tailscale/tailscale— resolves without substitution. - Terramate orchestration required no rewrite:
opentofu/workflows.tm.hcl’sinit,preview,deploy,drift detect,drift reconcileanddestroyscripts all invokeglobal.provisioner, and onlyopentofu/config.tm.hcl’s singleprovisioner = "tofu"line names the actual binary. - The same neutral-governance benefit ADR-0011 records for OpenBao: no single vendor controls the commercial terms this platform’s IaC tool runs under.
Negative
- A HashiCorp-published provider still configures the OpenBao stack.
opentofu/openbao/management/versions.tfpinsvault = { source = "hashicorp/vault", version = "~> 5.0" }— the same OpenTofu adoption chosen to leave HashiCorp’s terms behind still depends on a provider HashiCorp publishes, to configure OpenBao, the fork this platform adopted for the same licence reason (see ADR-0011). This record does not state what licence that provider currently ships under — confirming an external project’s current licence terms is outside what this repository can verify about itself; see References for where to check directly.- Mitigation: none currently, and the exposure is scoped:
opentofu/openbao/cluster/versions.tf, the stack that provisions the actual Raft EC2 fleet, declares novaultprovider at all — onlymanagement, which layers namespaces, PKI and AppRole auth on top, needs it.
- Mitigation: none currently, and the exposure is scoped:
- Provider availability is not automatically identical to Terraform’s.
OpenTofu’s registry is a separate index from HashiCorp’s; this
repository’s current provider set all resolves there today, but a
provider whose maintainer does not explicitly publish for OpenTofu is not
guaranteed to appear there, especially for anything less widely used than
hashicorp/awsorhashicorp/helm.- Mitigation: none automated; a future stack reaching for an uncommon provider is a check to make before assuming OpenTofu compatibility, not after.
Neutral
- Every stack’s
required_versionconstraint (~> 1.4inopenbao/management,~> 1.5elsewhere,>= 1.5ineks/configure) predates this decision and was never edited — those ranges are satisfied by the pinnedopentofu = "1.12.6"inmise.tomlwithout any per-stack change. - The CLI binary name itself changed (
tofuinstead ofterraform);opentofu/config.tm.hcl’sglobal.provisioneris the one place that names it, so every Terramate script keeps callingglobal.provisionerregardless of which binary it resolves to.
Implementation Notes
Adoption touched exactly two kinds of file: mise.toml, which pins the
opentofu tool version rather than a terraform one, and
opentofu/config.tm.hcl, whose global.provisioner = "tofu" is the value
every script in opentofu/workflows.tm.hcl invokes. No stack’s versions.tf
or resource HCL needed to change, and Terramate’s own stack-ordering,
drift-detection and opt-in-gating behaviour — documented on
Foundations — is
unaffected by any of this: it orchestrates whatever global.provisioner
names, not Terraform specifically.
The hashicorp/vault provider dependency in
opentofu/openbao/management/versions.tf predates this decision — it
configures OpenBao regardless of which tool executes it — and is not
something adopting OpenTofu introduced or could remove on its own;
ADR-0011 covers why OpenBao is configured
through that provider at all.
References
- Foundations — “Why OpenTofu”: the Linux Foundation governance and compatibility-with-existing-configuration claims this record’s Context and Positive sections draw from
- ADR-0011 — the same BUSL relicensing trigger, applied to Vault
- ADR-0012 — the boundary between OpenTofu and Crossplane assumed in this record’s Context
mise.toml— theopentofutool pin and the absence of aterraformentryopentofu/config.tm.hcl—global.provisioner = "tofu"opentofu/workflows.tm.hcl— theinit,preview,deploy,drift detect,drift reconcileanddestroyscripts that invokeglobal.provisioneropentofu/openbao/management/versions.tf— thehashicorp/vaultprovider pin this record’s Negative section namesopentofu/openbao/cluster/versions.tf— confirms thevaultprovider is absent from the cluster stack, scoping the dependency tomanagementonly- OpenTofu Registry — where current provider publication for OpenTofu can be checked directly
hashicorp/vaultprovider on the Terraform Registry — where that provider’s current licence terms can be checked directly; this record does not assert them