feat(terraform): add shared Terraform plan/apply workflows [NOJIRA] - #341
Merged
Merged
Conversation
Replaces the three divergent Terraform pipelines (terraform, terraform-core, terraform-db-access) with two reusable workflows. terraform-discover.yml computes the affected stacks from the diff instead of a hand-maintained job list. A stack is any directory containing backend.tf, so it is at once the unit of state, the CI job, the lock scope and the blast radius: a ClickHouse change can init, plan and apply while Route53 state is drifted. A change owned by no stack (modules/**) fans out to plan-all, apply-none. terraform-stack.yml plans or applies one stack. Apply downloads the artifact uploaded by the plan and runs `terraform apply tfplan` rather than re-planning, so what merges is what was reviewed. On a failed apply it re-plans in place, opens one issue per stack assigned to the merger, comments the link on the originating PR, and banners subsequent plans of that stack until the next successful apply closes it. Locking is per stack: plans cancel per ref, applies queue and are never cancelled mid-apply. Terraform version comes from .terraform-version in the stack, so stacks upgrade one at a time. OIDC roles are resolved per account with a fallback to static keys for the migration. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…OJIRA] Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every other example in this doc uses @main, but SRE-462's acceptance criteria specifically require callers of the terraform workflows to pin a tag, since this workflow is under active migration across three repos and an in-place change on main would land on every caller at once.
gh05tdog
approved these changes
Sep 10, 2026
gh05tdog
marked this pull request as ready for review
September 10, 2026 10:03
gh05tdog
requested review from
chandrashekar-dhar
and removed request for
a team
September 10, 2026 10:03
This was referenced Sep 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Three Terraform repos run three divergent pipelines — 1,820 lines of CI between them:
terraformterraform-coreterraform-db-accessThe problem isn't the duplication, it's that correctness fixes have to be applied N times.
terraform apply -auto-approve— re-plan on merge, the pattern behind the earlier Cloudflare outage — is live in 5 places across the three repos. monta-app/terraform#156 fixes 2 of them and defers the ClickHouse jobs by its own admission. Same story for the missing state lock, and for the "Argument list too long" plan-comment bug, which is fixed in exactly one of the three copies.What this adds
Two reusable workflows (461 lines) that the three repos call with ~40 lines each.
terraform-discover.yml— emits the JSON matrix of stacks affected by the event. A stack is any directory containingbackend.tf, which makes that directory simultaneously the unit of state, the CI job, the lock scope and the blast radius. Discovery walks the diff and maps each changed file to its nearest ancestor stack, so adding a stack needs no workflow edit — this replacesterraform-core's hand-writtenplan-X/apply-Xjob pairs and the hand-maintainedpaths-filterlists.The practical goal: a ClickHouse change can init, plan and apply while Route53 state is drifted.
terraform-stack.yml— plans or applies exactly one stack.terraform apply tfplan. It never re-plans, so what merges is what was reviewed (generalises monta-app/terraform#156 to every stack instead of two)..terraform-versionin the stack dir, falling back to the repo root — so stacks upgrade one at a time instead of a four-repo big bang.terraform-failedassigned to whoever merged, comments the link on the originating PR, and banners later plans of that stack until the next successful apply closes the issue. Recovery is always a forward apply; there is deliberately no rollback path.Caller example, required permissions and the
merge_groupguidance are indocs/workflow-guide.md.Verification
actionlintclean on both workflows and on the caller example extracted from the docs.terraform-coretree — 8 cases: single stack, two stacks across two accounts,modules/**fan-out (11 stacks), markdown-only →[], deep nested file → owning stack, duplicate paths → deduped, force-all → 11, stack with no account mapping → hard fail.Known issue to fix before
terraform-db-accessadopts thisFan-out is too wide. A change to a path owned by no stack currently selects every stack in the repo.
terraform-db-accesskeeps_shared-users-*.tfin the environment root, outside any stack — today its own workflow fans out to the DBs in that environment only. Under this PR it would fan out to all 56, which is harmless for plan and wrong for apply.Fix is to scope fan-out to the changed file's nearest ancestor directory (~10 lines in the discover script). It also stops a
modules/cloudflare/**change planning Grafana in theterraformrepo. Not blockingterraform-core, blocking for db-access.Adoption notes
terraform-failedlabel.terraform providers lock -platform=linux_arm64 -platform=darwin_arm64.terraform-db-accesscurrently has 3 of 56.tf-vars-jsonis a migration bridge forterraform's sevenTF_VAR_*secrets. The destination is stacks reading their own secrets viadata "aws_secretsmanager_secret_version"so CI holds nothing but the AWS role.@mainbeforeterraformandterraform-db-accessadopt it — a bad commit here would otherwise break apply in three repos at once, and the failure mode is a half-applied production stack.Suggested first adopter is
terraform-core: 11 stacks already havebackend.tf, it deletes ~693 lines, and it's the lowest-stakes way to find out if this design is wrong.🤖 Generated with Claude Code