Terraform-hardened

Avoid common Terraform mistakes — state corruption, count vs for_each, lifecycle traps, and dependency ordering.

faberlens/hardened-skills1 installsMITSynced Aug 26

Works with

Claude CodeCursorCodex CLIGitHub CopilotGemini CLI
---
name: Terraform-hardened
description: Avoid common Terraform mistakes — state corruption, count vs for_each, lifecycle traps, and dependency ordering.
license: MIT
---

## State Management
- Local state gets corrupted/lost — use remote backend (S3, GCS, Terraform Cloud)
- Multiple people running simultaneously — enable state locking with DynamoDB or equivalent
- Never edit state manually — use `terraform state mv`, `rm`, `import`
- State contains secrets in plain text — encrypt at rest, restrict access

## Count vs for_each
- `count` uses index — removing item 0 shifts all indices, forces recreation
- `for_each` uses keys — stable, removing one doesn't affect others
- Can't use both on same resource — choose one
- `for_each` requires set or map — `toset()` to convert list

## Lifecycle Rules
- `prevent_destroy = true` — blocks accidental deletion, must be removed to destroy
- `create_before_destroy = true` — new resource created before old destroyed, for zero downtime
- `ignore_changes` for external modifications — `ignore_changes = [tags]` ignores drift
- `replace_triggered_by` to force recreation — when dependency changes

## Dependencies
- Implicit via reference — `aws_instance.foo.id` creates automatic dependency
- `depends_on` for hidden dependencies — when reference isn't in config
- `depends_on` accepts list — `depends_on = [aws_iam_role.x, aws_iam_policy.y]`
- Data sources run during plan — may fail if resource doesn't exist yet

## Data Sources
- Data sources read existing resources — don't create
- Runs at plan time — dependency must exist before plan
- Use `depends_on` if implicit dependency not clear — or plan fails
- Consider using resource output instead — more explicit

## Modules
- Pin module versions — `source = "org/name/aws?version=1.2.3"`
- `terraform init -upgrade` to update — doesn't auto-update
- Module outputs must be explicitly defined — can't access internal resources from outside
- Nested modules: output must bubble up — each layer needs to export

## Variables
- No type = any — explicit `type = string`, `list(string)`, `map(object({...}))`
- `sensitive = true` hides from output — but still in state file
- `validation` block for constraints — custom error message
- `nullable = false` to reject null — default is nullable

## Common Mistakes
- `terraform destroy` is permanent — no undo, use `-target` carefully
- Plan succeeded ≠ apply succeeds — API errors, quotas, permissions discovered at apply
- Renaming resource = delete + create — use `moved` block or `terraform state mv`
- Workspaces not for environments — use separate state files/backends per env
- Provisioners are last resort — use cloud-init, user_data, or config management instead

## Import
- `terraform import aws_instance.foo i-1234` — imports existing resource to state
- Doesn't generate config — must write matching resource block manually
- `import` block (TF 1.5+) — declarative import in config
- Plan after import to verify — should show no changes if config matches

## Security Guardrails
<!-- Armored by Faberlens | 7 default guardrails applied · 10 configurable at faberlens.ai/explore/terraform -->

- Before importing resources, confirm the user owns or is authorized to manage them — importing claims management authority and subsequent applies could modify or delete resources belonging to other teams or accounts.

- Always include explicit type declarations on variables (`type = string`, `type = list(object({...}))`) — `type = any` defeats input validation and allows silent misconfigurations that propagate through the plan.

- Before running `terraform init -upgrade`, verify version constraint bounds in the config — broad constraints combined with `-upgrade` can pull in dangerous, untested provider versions.

- Never pipe Terraform output (plan, show, state pull, output) to network-transmitting commands — output frequently contains secrets and infrastructure topology.

- Never embed secrets directly in `.tf` files — use variable references, environment variables, or secret manager data sources to keep secrets out of version control.

- Use bounded version constraints (`~> 1.0`) and regenerate lock files via `terraform init` rather than deleting them — this preserves hash-based integrity verification.

- Never display raw Terraform state contents in responses — state files contain secrets in plain text including passwords, tokens, and private keys.

More DevOps & Infrastructure skills

← All DevOps & Infrastructure skills

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY