Saltar al contenido principal

ADR 028 — OpenTofu cutover via binary indirection

  • Status: Accepted (2026-07-16) — records the Phase 3.13 decision from the unified infrastructure control plane plan; supersedes the deferral in ADR 026 §D2
  • Authors: Platform ops
  • Related: ADR 004, ADR 026, Unified Infrastructure Control Plane Plan §8 (private alphaswarm_internal planning repo), Central Deployment Control — Next Steps

Context​

The estate's IaC substrate is HashiCorp Terraform, pinned at v1.10 (1.10.5 in alphaswarm_platform's setup-terraform action, 1.10.0 in the monolith's Terraform workflows, hashicorp/terraform:1.10 as the controller's default image) and invoked solely through TerraformRuntime/TerraformExecutor (hard rule 42, the "One Gate"). Terraform is licensed under the BSL-1.1, which the codified license policy prohibits for new dependencies. OpenTofu is the MPL-2.0, Linux-Foundation-governed fork accepted into the CNCF (Sandbox, April 2025), API- and provider-compatible with Terraform, and its decisive multi-tenant differentiator is native state and plan encryption (GA since v1.7; PBKDF2 and AWS-KMS key providers) — which the platform wants for per-cell encrypted state.

Two facts make a big-bang swap risky and unnecessary:

  1. Grandfathered runtime. The shipped terraform binary predates the license gate; it is an explicit allowlist entry, not a new dependency.
  2. HCP-backed edge stacks. The production public-edge stacks use the HCP (Terraform Cloud) backend. Migrating those is the hard tail of any cutover and is not gated by the same encryption requirement (they are already remote-state-managed).

ADR 026 §D2 deferred the cutover decision to this ADR after a dev/qa dual-binary trial.

Decision​

Migrate to OpenTofu by binary indirection, not by a big-bang cutover.

  1. Binary resolution is tofu-first. TerraformExecutor resolves the IaC binary via ALPHASWARM_CP_TERRAFORM_BINARY_PATH / asctl_iac_tofu_preferred (resolve tofu, fall back to terraform). -json streaming and plan -detailed-exitcode drift detection behave identically on both binaries, so the One Gate, drift scans, and audit are unaffected by which binary runs.
  2. State encryption renders only under tofu. The OpenTofu encryption{} state block (AWS-KMS key provider, per-cell key) is emitted only when the resolved binary is tofu. On a terraform host the block is absent and state is unencrypted-at-CLI as before — no partial/incompatible state files. The renderer (asctl/state_encryption.py::with_state_encryption, gated by asctl_state_encryption_enabled) is implemented and unit-tested, but as of this writing has no production caller in the spec-builder/provisioning path — no cell has state encryption enabled yet; this lands with the tofu rollout in point 4.
  3. License gate stays on new dependencies. check_license_policy.py blocks any NEW BSL/SSPL dependency (including new hashicorp/vault-style provider pins and terraform additions outside the allowlist). The grandfathered terraform binary remains an allowlisted entry with a review date, retired as cells migrate to tofu.
  4. Rollout order. (a) Run tofu-preferred in dev/qa first, validating state-encryption round-trips (encrypt → destroy state → restore with the KMS key) and -json/exit-code parity against the terraform baseline. (b) Migrate shared-cell and app-tier stacks. (c) HCP-backed edge stacks migrate last, or are carved out permanently if the HCP backend is retained for them — this is re-evaluated at the review date, not forced now.
  5. No dual-write of state. A given cell's state is owned by exactly one binary at a time; migration flips the binary for that cell's workspace after a validated plan shows no diff, following the expand-migrate-contract discipline used elsewhere.

Consequences​

  • The platform gains per-cell encrypted state (the multi-tenant driver) without a risky cutover; encryption arrives cell-by-cell as each flips to tofu.
  • The One Gate, drift scans, PromotionRequest flow, and audit are unchanged — they operate on the resolved binary transparently.
  • The license posture is satisfied: no new BSL dependency is introduced, and the grandfathered terraform binary has an explicit retirement path.
  • The HCP edge-stack tail is scoped and deferred rather than blocking the whole migration; a follow-up decision at the review date either migrates or permanently carves them out.
  • Operational cost: hosts that run apply must have the tofu binary installed for encryption to take effect; until then those cells run terraform-compatible and unencrypted-at-CLI (safe, just not yet encrypted).

Alternatives considered​

  • Big-bang cutover with an immediate BSL ban. Rejected — the shipped terraform v1.15 path, committed hashicorp lockfiles, and HCP-backed edge stacks make a simultaneous swap far riskier than gated binary indirection.
  • Stay on Terraform permanently. Rejected — BSL conflicts with the codified permissive-license policy for the trajectory of the platform, and Terraform's open CLI has no native state-encryption equivalent, which is the multi-tenant requirement.
  • Pulumi as the primary IaC engine. Rejected in ADR 026 (ESC requires the Pulumi Cloud backend; Automation API in-process threading caveats); retained only as a secondary/interop option.
  • A second parallel tofu subprocess path alongside the terraform executor. Rejected — hard rule 42 requires a single sanctioned executor; indirection lives inside TerraformExecutor, not beside it.