skills/terraform/SKILL.md
Use when creating, adopting, refactoring, or operating Terraform, *.tf files, .terraform.lock.hcl, terragrunt.hcl, root modules, backends, state, workspaces, imports, CI plan/apply, tests, or policy checks.
npx skillsauth add cofin/flow terraformInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Use this skill to keep Terraform work small-state, reviewable, and brownfield-safe.
This repo should treat Terraform as four separate concerns:
For this repo family, default to brownfield embedding inside an existing product repo unless the user explicitly wants a dedicated infrastructure repository.
| Decision | Default | Avoid |
|---|---|---|
| Brownfield placement | infra/terraform/ inside the existing repo | Mixing Terraform into src/ or app runtime folders |
| Environment model | Separate directories and separate state per env/root | Using CLI workspaces for dev / stage / prod |
| State size | One root module per deployable unit or boundary | Giant multi-service roots |
| GCP backend | GCS remote state | Local state for shared environments |
| Local auth | ADC | Long-lived service account keys |
| CI auth | Attached service account on GCP, otherwise WIF | Downloaded JSON keys when avoidable |
| Sensitive values | Treat state and plan artifacts as sensitive; prefer Secret Manager plus sensitive / ephemeral patterns when supported | Hardcoding secrets or committing plan/state artifacts |
| Brownfield adoption | Export/import, review, then refactor with moved blocks | Hand-editing state to “make it fit” |
infra/terraform/ for brownfield repos..terraform.lock.hcl.moved blocks instead of destructive rename/recreate cycles.fmt, validate, saved plan, and policy checks before apply.plan and apply, especially for shared environments.Use the smallest layout that preserves clear ownership.
infra/terraform/.modules/ and live environment roots.infra/terraform/environments/<env>/<service>/.Read references/layout.md before creating directories.
A root module is a state boundary. Treat it as an operational boundary too.
For GCP, default to:
Read references/gcp.md before writing provider or backend configuration.
If the root spans multiple projects, regions, or beta-only resources, define explicit provider aliases instead of overloading one default google provider configuration.
Treat Terraform state files, saved plan files, and plan JSON as sensitive artifacts.
.tfvars.sensitive = true for inputs and outputs that must be redacted.ephemeral = true or write-only arguments when the provider/resource supports them and the value should stay out of state and plan files entirely.Use separate directories and separate state for dev, stage, and prod.
Use Terraform CLI workspaces only when all of the following are true:
If any of those conditions are false, do not use CLI workspaces as the primary environment model.
Before proposing an apply, run the low-risk checks first:
terraform fmtterraform initterraform validateterraform plan -out=tfplanterraform show -json tfplan > tfplan.json when policy tooling or machine review is neededterraform test when the module or root justifies itUse references/testing.md for the validation pipeline.
When a system already exists:
import blocks or targeted imports for the selected boundary.moved blocks to preserve state history during renames or splits.Use references/brownfield.md for the exact flow.
After the Terraform structure is sound, pull in service-specific references:
.terraform.lock.hcl in root modules that will be shared or reviewed.prevent_destroy and provider-specific deletion protection where the platform supports them.Before claiming a Terraform change is ready, verify:
google-beta configuration are explicit when multi-project, multi-region, or beta-only resources are involved.terraform.lock.hcl is committed when the root is meant to be versionedterraform fmt, terraform validate, and a saved terraform plan -out=... were runmoved blocks are documented when applicableBrownfield application repo layout:
repo/
├── src/
├── tests/
├── .agents/
└── infra/
└── terraform/
├── modules/
│ ├── project-services/
│ ├── network/
│ └── cloud-run-service/
└── environments/
├── dev/
│ ├── shared-network/
│ └── api-service/
├── stage/
│ ├── shared-network/
│ └── api-service/
└── prod/
├── shared-network/
└── api-service/
Minimal root files:
api-service/
├── backend.tf
├── main.tf
├── providers.tf
├── terraform.tf
├── terraform.tfvars
├── variables.tf
├── outputs.tf
└── README.md
GCP root skeleton:
terraform {
required_version = ">= 1.10.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 6.0" # Pin to a current minor series intentionally.
}
}
backend "gcs" {}
}
provider "google" {
project = var.project_id
region = var.region
}
module "service" {
source = "../../modules/cloud-run-service"
project_id = var.project_id
region = var.region
name = var.name
}
</example>
moved blocks.fmt, validate, saved plans, terraform test, CI, and policy checks.development
Use when tracing execution paths, mapping dependencies, understanding unfamiliar code, following data flow, investigating end-to-end behavior, debugging call chains, or deciding which files to read next.
development
Use when reviewing authentication, authorization, user input, secrets, API keys, database queries, file uploads, session management, external API calls, OWASP risks, or data handling attack surface.
testing
Use when analyzing tradeoffs, comparing approaches, weighing options, assessing risks, stress-testing conclusions, identifying blind spots, or applying multiple viewpoints to a decision.
development
Use when reviewing hot paths, slow code, database queries, N+1 risks, memory usage, loops, I/O, caching strategy, concurrency, latency-sensitive paths, or resource efficiency.