---
name: terraform-iac-pro
description: "Terraform modules, state management, remote backends, multi-env workspaces, drift detection. Production Terraform 1.x: module composition, state with S3 + DynamoDB locking (or Terraform Cloud), workspaces for env separation, drift detection, terraform import for existing infra, and the OpenTofu fork story."
license: MIT
---

# Terraform IaC Pro

A working companion for Terraform 1.x (and OpenTofu) practitioners who manage real infrastructure across multiple environments. It focuses on the parts that bite teams in production: state, locking, module boundaries, and catching drift before it becomes an incident. Aimed at platform and backend engineers who already write HCL but want sharper patterns and fewer 2 a.m. surprises.

## When to use it

- You're standing up a remote backend and need S3 + DynamoDB locking (or migrating to Terraform Cloud) without corrupting existing state.
- You inherited click-ops infrastructure and need to `terraform import` it under management.
- Your `plan` shows unexpected changes and you need to tell real drift from provider noise.
- You're splitting a monolithic root module into reusable child modules with clean `variables.tf` / `outputs.tf` contracts.
- You're separating dev/staging/prod and deciding between workspaces, separate state files, or directory-per-env layouts.

## How it works

The skill reasons about your `.tf` files, `terraform.tfstate` metadata, and CLI workflow directly. It knows the real commands and their sharp edges: `terraform init -backend-config=...`, `plan -out=tfplan`, `apply tfplan`, `state mv` / `state rm` / `state list`, `import`, `taint`/`untaint` (and the newer `-replace=` flag), `refresh`, and `workspace new|select`.

For backends it generates the `backend "s3"` block with `dynamodb_table` for locking, `encrypt = true`, and key naming that won't collide across environments. For module composition it enforces explicit input variables, typed outputs, and version-pinned `source` references. For drift it walks `plan` output to flag resources changed outside Terraform and suggests whether to import, ignore via `lifecycle { ignore_changes }`, or reconcile.

## Example

```bash
curl -fsSL --create-dirs -o ~/.claude/skills/terraform-iac-pro/SKILL.md https://claudeskil.com/skills/terraform-iac-pro/SKILL.md

# Bootstrap a locked remote backend
terraform init -backend-config="bucket=acme-tfstate" \
  -backend-config="dynamodb_table=tf-locks" \
  -backend-config="key=prod/network.tfstate"

# Adopt an existing VPC instead of recreating it
terraform import aws_vpc.main vpc-0abc123
terraform plan   # should now show no changes

# Promote a change safely
terraform workspace select prod
terraform plan -out=prod.tfplan && terraform apply prod.tfplan
```

## Tips & gotchas

- Never `terraform apply -refresh-only` blindly to "fix" drift — it writes observed reality into state and can mask a destroy.
- `count` vs `for_each`: switching an existing resource from `count` to `for_each` reindexes addresses and forces replacement. Use `terraform state mv` to preserve them.
- Bootstrap the S3/DynamoDB backend resources in a *separate* root with local state; you can't store a backend's own state in the backend it creates.
- Pin `required_providers` versions. An unpinned provider upgrade is the most common source of "phantom" plan diffs.

## FAQ

**Does it work with OpenTofu?** Yes. The HCL, state format, and commands are compatible; swap `terraform` for `tofu`. The skill notes where 1.6+ features (like state encryption in OpenTofu) diverge.

**Workspaces or separate state files per env?** Separate state (directories or backend keys) for prod isolation; workspaces are fine for ephemeral or low-blast-radius environments. The skill recommends per-case.

**Can it fix a locked state?** It guides `force-unlock <LOCK_ID>` only after confirming no apply is genuinely running.
