The Nx plugin that turns this repo's Terraform into Nx projects with targets.
Nothing here is deployed. It is loaded by nx.json.
From a project.json beside .tf files:
| The directory has | It is a | Targets |
|---|---|---|
a backend block |
root | format, lint, terraform-init, terraform-plan, deploy, terraform-force-unlock, clean |
.tf files and no backend |
module | format, lint |
lint is the format check and TFLint. A root's also runs terraform validate
between the two, after init -backend=false, so it needs no credentials but
does need terraform and tflint on PATH. A module is validated through the
roots that call it.
clean removes the root's .terraform: the providers, the modules and which
backend it was last initialised against, most of a gigabyte a root. The next
init brings it back. bun run clean, from the root, cleans every root and
empties Nx's cache.
On a laptop every target that runs terraform sets TF_PLUGIN_CACHE_DIR to
~/.terraform.d/plugin-cache unless it is already set (src/plugin-cache.sh),
so each provider is downloaded once per machine, not once per root per
checkout. A runner keeps its own setup.
A root must carry exactly one account:<name> tag. It picks the SSO profile a
laptop uses; the accounts are ACCOUNTS in src/plugin.ts.
No plan takes the state lock. A plan writes nothing, and the lock it would hold is the one an apply needs: a plan run from a laptop once held a root's lock as a merge landed, and that merge's apply failed.
A module only one root calls lives under that root, in modules/, and is not
a project. One shared between roots is a project, and source = "../x"
becomes a dependency, so a change to it marks every root calling it affected.
From a terraform/backend.jinja: a root AFT renders. It has no
project.json, because it sits in a folder mirrored into the repo AFT reads
and the file would be mirrored with it. These get format, lint (the
format check and a terraform validate of the rendered stub), aft-plan, and
aft-accounts on the customizations.
packages/aft/README.md describes them and how the workflows use them.
Every root this repo applies is planned and applied by these targets:
pr.yml runs terraform-plan on a pull request and deploy.yml runs deploy on a
merge or dispatch, and no workflow calls terraform itself.
On a laptop a target runs Terraform directly, with the account's SSO profile.
On a runner, where the job has already assumed a role and nobody can answer a
prompt, terraform-plan and deploy go through src/ci.sh, which
prints the output as it happens and writes the plan's result, with what would
change, to the job summary.
No root states a Terraform version. .terraform-version does, once: CI
installs from it, and every target that runs Terraform first checks the
terraform on PATH against it (src/terraform-version.sh) and stops if it is
another minor release.
| File | What it does |
|---|---|
src/plugin.ts |
The inference |
src/ci.sh |
init and plan or apply on a runner |
src/aft-render.sh |
Builds an AFT root with its .jinja files rendered |
src/aft-vars.jq |
AFT's SSM parameters to the variables its templates take |
src/aft-stub-vars.json |
Stand-in values for those, so validate needs no credentials |
src/aft-as-admin.sh |
Runs a command as AFT's admin role |
src/aft-affected-accounts.sh |
The accounts a change reaches |
src/aft-deploy.sh |
Invokes AFT's customizations and follows the builds, printing their logs |
src/aft-follow.sh |
Follows a pipeline AFT starts by itself, and prints its build's log |
src/aft-codebuild.sh |
Following one CodeBuild build with its log, shared by the two above |
src/terraform-version.sh |
Holds a target to the Terraform release in .terraform-version |
bun nx run nx-terraform:test # bun test: the inference, and the scripts against stand-in aws, terraform and nx
bun nx run nx-terraform:lint # bun lint and shellcheck
bun nx run nx-terraform:typecheck
A change under src/ marks every AFT root affected, so a pull request that
touches it plans all of them. That run is the only test the tooling gets
against real state.