From flow
Guides small-state, reviewable Terraform practices for brownfield repos: placement in infra/terraform/, provisioning root modules, adopting existing infra, CI plan/apply operations.
npx claudepluginhub cofin/flow --plugin flowThis skill uses the workspace's default tool permissions.
Use this skill to keep Terraform work small-state, reviewable, and brownfield-safe.
Diagnoses Terraform/OpenTofu failure modes like identity churn, secret exposure, blast radius, CI drift, state corruption when writing, reviewing, or debugging modules, tests, CI, scans, state ops.
Provides Terraform best practices for module design, remote state management, security, testing, and multi-environment deployments in scalable IaC.
Provisions and manages cloud infrastructure using Terraform with HCL modules, remote state backends, workspaces, and plan/apply workflows. For new IaC setups, CloudFormation migrations, multi-env management, and team collaboration.
Share bugs, ideas, or general feedback.
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
}
moved blocks.fmt, validate, saved plans, terraform test, CI, and policy checks.