terraform
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.
Best use case
terraform is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
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.
Teams using terraform should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/terraform/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How terraform Compares
| Feature / Agent | terraform | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
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.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Terraform
## Overview
Use this skill to keep Terraform work small-state, reviewable, and brownfield-safe.
This repo should treat Terraform as four separate concerns:
1. **Place** Terraform in the right part of the repo.
2. **Provision** new infrastructure with clear root-module boundaries.
3. **Adopt** existing infrastructure without taking unsafe shortcuts.
4. **Operate** Terraform through validated plans, CI, and policy checks.
For this repo family, default to **brownfield embedding** inside an existing product repo unless the user explicitly wants a dedicated infrastructure repository.
## Quick Reference
| 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” |
## Operating Lanes
### Lane 1: Repo and State Design
- Keep Terraform in a clearly scoped infrastructure area.
- Prefer `infra/terraform/` for brownfield repos.
- Keep reusable modules separate from live environment roots.
- Keep each root module small enough that reviewers can understand a plan.
### Lane 2: Greenfield Provisioning
- Start with a small root module and a reusable module boundary only where reuse is real.
- Pin provider and module versions.
- Check in `.terraform.lock.hcl`.
### Lane 3: Brownfield Adoption
- Inventory what already exists.
- Export or import existing resources into Terraform.
- Normalize the generated or imported configuration.
- Refactor with `moved` blocks instead of destructive rename/recreate cycles.
### Lane 4: Day-2 Operations
- Run `fmt`, `validate`, saved `plan`, and policy checks before `apply`.
- Prefer CI-mediated `plan` and `apply`, especially for shared environments.
- Upgrade versions intentionally and review lockfile drift.
<workflow>
## Workflow
### Step 1: Choose Repo Placement
Use the smallest layout that preserves clear ownership.
- **Brownfield application repo:** put Terraform under `infra/terraform/`.
- **Dedicated infra repo:** keep the same internal split of `modules/` and live environment roots.
- **Service-specific IaC:** place service roots under `infra/terraform/environments/<env>/<service>/`.
Read [references/layout.md](references/layout.md) before creating directories.
### Step 2: Define Root-Module Boundaries
A root module is a state boundary. Treat it as an operational boundary too.
- Split by application, shared platform service, or blast-radius boundary.
- Prefer separate roots for shared networking, project/bootstrap, and app-service stacks.
- Keep unrelated systems out of the same state even if they deploy together.
### Step 3: Establish Backend and Auth
For GCP, default to:
- **Local development:** ADC
- **Privileged local work:** service account impersonation
- **CI on Google Cloud:** attached service account
- **CI outside Google Cloud:** Workload Identity Federation
- **Remote state:** GCS backend per root/environment
Read [references/gcp.md](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.
### Step 3.5: Handle Sensitive Values Early
Treat Terraform state files, saved plan files, and plan JSON as sensitive artifacts.
- Keep state and plan artifacts out of Git.
- Prefer Secret Manager or another external secret source over plaintext secrets in `.tfvars`.
- Use `sensitive = true` for inputs and outputs that must be redacted.
- Use `ephemeral = true` or write-only arguments when the provider/resource supports them and the value should stay out of state and plan files entirely.
### Step 4: Pick the Environment Model
Use separate directories and separate state for `dev`, `stage`, and `prod`.
Use Terraform CLI workspaces only when all of the following are true:
- the configuration is the same shape in every instance
- credentials and approvals are the same
- the backend is shared intentionally
- the instances are peers, not separate systems
If any of those conditions are false, do not use CLI workspaces as the primary environment model.
### Step 5: Implement and Validate
Before proposing an `apply`, run the low-risk checks first:
1. `terraform fmt`
2. `terraform init`
3. `terraform validate`
4. `terraform plan -out=tfplan`
5. `terraform show -json tfplan > tfplan.json` when policy tooling or machine review is needed
6. `terraform test` when the module or root justifies it
Use [references/testing.md](references/testing.md) for the validation pipeline.
### Step 6: Brownfield Adoption Path
When a system already exists:
1. Decide the target root-module boundary before importing anything.
2. Export existing GCP resources if that accelerates discovery.
3. Add `import` blocks or targeted imports for the selected boundary.
4. Generate configuration when helpful, but treat it as a scaffold.
5. Refactor into the desired module shape.
6. Use `moved` blocks to preserve state history during renames or splits.
Use [references/brownfield.md](references/brownfield.md) for the exact flow.
### Step 7: Connect Service-Specific Patterns
After the Terraform structure is sound, pull in service-specific references:
- Cloud Run examples: [../cloud-run/references/terraform.md](../cloud-run/references/terraform.md)
- GKE examples: [../gke/references/terraform.md](../gke/references/terraform.md)
</workflow>
<guardrails>
## Guardrails
- **Do not use CLI workspaces for system decomposition** or for environments with separate credentials or approvals.
- **Do not put unrelated services in one state** just because a single PR touches them.
- **Do not hand-edit Terraform state** unless it is a documented break-glass recovery.
- **Do not normalize brownfield infrastructure by deleting and recreating it** when import and refactor paths exist.
- **Do not treat exported or generated HCL as production-ready** until it is cleaned up and reviewed.
- **Do not rely on service account keys by default on GCP** when ADC, impersonation, or Workload Identity Federation are available.
- **Do not treat remote state as non-sensitive**. State files and saved plans can contain secrets, tokens, and generated credentials.
- **Do not skip `.terraform.lock.hcl` in root modules** that will be shared or reviewed.
- **Do not hide destructive changes inside apply-only workflows**. Save and review the plan first.
- **Do not introduce Terragrunt as the default architecture** unless the repo already standardized on it. Plain Terraform is the baseline.
- **Do not let helper scripts become hidden infrastructure dependencies**. Prefer provider resources and documented modules first.
- **Do not leave stateful resources without lifecycle protection**. Use `prevent_destroy` and provider-specific deletion protection where the platform supports them.
</guardrails>
<validation>
## Validation Checkpoint
Before claiming a Terraform change is ready, verify:
- [ ] the repo layout keeps Terraform out of application runtime folders
- [ ] each root module has a clear ownership and state boundary
- [ ] environment separation uses directories/state, or workspace use is explicitly justified
- [ ] backend and authentication strategy are documented
- [ ] sensitive inputs, state, and plan artifacts are handled as secrets
- [ ] provider aliases or `google-beta` configuration are explicit when multi-project, multi-region, or beta-only resources are involved
- [ ] provider and module versions are pinned intentionally
- [ ] `.terraform.lock.hcl` is committed when the root is meant to be versioned
- [ ] `terraform fmt`, `terraform validate`, and a saved `terraform plan -out=...` were run
- [ ] stateful resources use lifecycle protection or an explicitly documented exception
- [ ] brownfield imports, generated config, and `moved` blocks are documented when applicable
- [ ] policy validation and module tests were considered for shared or sensitive infrastructure
</validation>
<example>
## Example
Brownfield application repo layout:
```text
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:
```text
api-service/
├── backend.tf
├── main.tf
├── providers.tf
├── terraform.tf
├── terraform.tfvars
├── variables.tf
├── outputs.tf
└── README.md
```
GCP root skeleton:
```hcl
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>
---
## References Index
- **[Layout and Workspaces](references/layout.md)**
- Brownfield repo placement, root-module boundaries, directory conventions, and when not to use CLI workspaces.
- **[GCP Terraform Patterns](references/gcp.md)**
- ADC, impersonation, WIF, GCS backends, API enablement, module pinning, and policy validation on Google Cloud.
- **[Brownfield Adoption](references/brownfield.md)**
- Export, import blocks, generated configuration, and refactoring with `moved` blocks.
- **[Testing and Delivery](references/testing.md)**
- `fmt`, `validate`, saved plans, `terraform test`, CI, and policy checks.
## Official References
- <https://docs.cloud.google.com/docs/terraform/best-practices/general-style-structure>
- <https://docs.cloud.google.com/docs/terraform/best-practices/root-modules>
- <https://docs.cloud.google.com/docs/terraform/best-practices/operations>
- <https://docs.cloud.google.com/docs/terraform/best-practices/testing>
- <https://docs.cloud.google.com/docs/terraform/authentication>
- <https://docs.cloud.google.com/docs/terraform/resource-management/export>
- <https://docs.cloud.google.com/docs/terraform/resource-management/import>
- <https://docs.cloud.google.com/docs/terraform/policy-validation/quickstart>
- <https://developer.hashicorp.com/terraform/language/style>
- <https://developer.hashicorp.com/terraform/language/state/workspaces>
- <https://developer.hashicorp.com/terraform/language/import>
- <https://developer.hashicorp.com/terraform/language/modules/develop/refactoring>Related Skills
flow-memory-keeper
Use at task, phase, flow, sync, archive, finish, revise, or failure checkpoints to keep Flow specs clean, capture learnings and failures, elevate durable patterns, and refine this skill with project-specific nuances
vue
Use when editing Vue projects, .vue files, vue.config.js, Vue 3 components, Composition API, <script setup>, SFC state, deployment workflows, or Vue CI configuration.
vite
Use when editing Vite projects, vite.config.ts, vite.config.js, Vite plugins, HMR, asset bundling, frontend build settings, deployment config, or Litestar/Vite integration.
uvicorn
Use when deploying ASGI apps with uvicorn, editing uvicorn CLI commands, Config or Server usage, workers, reload, event loop selection, SSL, lifespan, logging, or development server behavior.
tracer
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.
testing
Use when writing or refactoring tests, editing test_*.py, *.test.ts, *.spec.ts, conftest.py, vitest.config.ts, pytest fixtures, mocks, coverage, async tests, anyio, or test failure debugging.
tanstack
Use when editing TanStack code, @tanstack imports, useQuery, createRouter, React Query, TanStack Router, Table, Form, Store, file-based routing, data fetching, or SPA state management.
tailwind
Use when styling with Tailwind CSS, editing tailwind.config.ts, tailwind.config.js, @tailwind directives, utility classes, responsive layouts, @apply, cn(), @theme config, dark mode, or forms.
svelte
Use when editing Svelte components, .svelte files, svelte.config.js, Svelte 5 runes, $state, $derived, SvelteKit, component state, or migrating away from Svelte 4 patterns.
sqlserver
Use when writing T-SQL, editing SQL Server .sql files, using sqlcmd, SQL Server connection strings, stored procedures, execution plans, indexes, Always On, JSON, security, or connector code.
sqlalchemy
Use when editing SQLAlchemy code, sqlalchemy imports, mapped_column, DeclarativeBase, ORM models, relationships, select() queries, async sessions, engines, events, or migrations.
sphinx
Use when editing Sphinx docs, conf.py, .rst files, docs/source, autodoc, Read the Docs builds, Shibuya or Immaterial themes, Wasm extensions, VHS terminal recordings, or Sphinx CI.