Terraform Change
Use this when changing Terraform under
terraform/or cluster bootstrap Terraform underclusters/<site>/bootstrap/. Background lives in Architecture/Terraform; the apply model (Atlantis autoplan → review →atlantis apply→ automerge) is on Architecture/GitOps.Rule
The binary is OpenTofu (
tofu), notterraform—mise.tomlinstalls both, but Atlantis applies withATLANTIS_DEFAULT_TF_DISTRIBUTION=opentofuand CI runstofu. Terraform applies run through Atlantis on the PR. Do not runtofu applyagainst remote state locally; it can race Atlantis, lock state, and create drift.
Find the root module
Each Terraform root has its own state and backend. Common roots:
terraform/network/unifi/follyterraform/network/unifi/offsiteterraform/network/cloudflareterraform/network/tailscaleterraform/gcp/organizationterraform/gcp/projects/<name>terraform/argoterraform/google-workspaceterraform/pkiclusters/folly/bootstrapclusters/offsite/bootstrap
Local validation
Preferred — validates every root the same way CI does (
mise tasks lslists alltf:*tasks):mise run tf:init mise run tf:validateTo scope to just the root you changed:
tofu -chdir=<root> init -backend=false tofu -chdir=<root> validateFormat before review:
mise run tf:fmtLocal plans are inspection only. Set
TF_DIRto the root's path from the repo root:TF_DIR=terraform/network/unifi/folly mise run tf:plan
PR flow
Open a PR with the
.tfchange.Atlantis autoplans the changed root modules.
Review the Atlantis plan comment.
Comment
atlantis applyonly after the plan is reviewed and expected.A successful Atlantis apply automerges according to the repo workflow.
If validation fails
For backend errors during local validation, retry with
mise run tf:init(ortofu init -backend=falsein the root).For provider/schema errors, run from the exact root that owns the changed files (
tofu -chdir=<root> validate).For plans that include unexpected replacement or deletion, stop and inspect state/import history before applying.
Notes
The
terraform/network/roots keep historical GCS state prefixes that do not always match the current directory name — read thebackendblock rather than inferring the prefix from the path.terraform/pkirequires OpenTofu specifically: it uses theopentofu/tlsprovider fork formax_path_length, unavailable on the Terraform registry.Network facts come from the cluster topology single source of truth (
clusters/<site>/config/cluster-topology.json) where a root already has atopology.tf; see Architecture/Terraform.
Linked references 6
Step-by-step: Runbooks/Terraform Change. Root/module layout: Architecture/Terraform.
Everything here ships through git. Terraform applies through Atlantis on the PR (Runbooks/Terraform Change); Kubernetes applies on merge to main through Flux (Architecture/GitOps). Nothing below is a kubectl apply or a local tofu apply.
Use this for read-only inspection of the live UniFi network before changing Terraform desired state. Desired state still lives under terraform/network/unifi/; changes apply through Atlantis. See Runbooks/Terraform Change.
Author the desired change in Terraform and use Runbooks/Terraform Change.
Runbooks/Terraform Change — Atlantis-first OpenTofu workflow and local validation