Terraform IaC Agent Rules
Project Context
You are writing Terraform infrastructure-as-code for provisioning and managing cloud resources. All infrastructure changes are version-controlled, peer-reviewed, and applied through CI pipelines.
Code Style
- Run `terraform fmt` on all files before committing — use 2-space indentation and consistent attribute ordering.
- Use `snake_case` for all resource names, variables, outputs, and locals: `aws_s3_bucket.application_assets`, not `aws_s3_bucket.bucket1`.
- Group related resources into logically named files: `networking.tf`, `compute.tf`, `storage.tf`, `iam.tf`, `variables.tf`, `outputs.tf`.
- Use `locals` for computed values, repeated expressions, and shared tag maps to keep resource blocks readable.
- Add inline comments for non-obvious decisions, workarounds, and provider-specific quirks.
- Run `terraform validate` in CI to catch syntax errors and provider constraint violations before planning.
Project Structure
- Separate reusable modules from environment-specific root configurations.
- Store reusable modules in `modules/`: `modules/vpc/`, `modules/ecs-service/`, `modules/rds-cluster/`.
- Store environment configurations in `envs/`: `envs/dev/`, `envs/staging/`, `envs/production/`.
- Each environment directory contains `main.tf` (module calls), `variables.tf`, `terraform.tfvars`, and `outputs.tf`.
- Keep root modules thin: primary responsibility is composing module calls and provider configuration.
- Define provider configuration in `providers.tf`. Pin provider versions in `required_providers` with `~>` constraints.
Resources & Data Sources
- Use `data` sources to reference existing infrastructure (VPCs, AMIs, Route53 zones) rather than hardcoding IDs.
- Use `for_each` over `count` for creating multiple resources — `for_each` produces stable resource addresses that don't shift on deletion.
- Use `dynamic` blocks to generate repeated nested blocks from maps or lists.
- Use `moved` blocks when refactoring resource addresses to avoid destructive recreations.
- Use `depends_on` only as a last resort when Terraform cannot infer implicit dependencies from attribute references.
- Use `lifecycle.prevent_destroy = true` for stateful resources in production; `ignore_changes` only for externally managed attributes.
- Tag all cloud resources consistently: `environment`, `project`, `team`, `managed_by = "terraform"`.
Variables & Outputs
- Define all variables in `variables.tf` with `description`, `type`, and `validation` blocks.
- Use complex types with explicit element types: `list(string)`, `map(object({ ... }))` — avoid `any`.
- Add `validation` blocks for constrained variables: regex patterns, allowed value sets, numeric ranges.
- Mark sensitive variables with `sensitive = true` to prevent them appearing in plan output and state diffs.
- Define outputs for all values consumed by other modules, pipelines, or environments: IDs, ARNs, DNS names, endpoints.
- Never expose secrets as outputs unless marked `sensitive = true` and strictly necessary.
State Management
- Store state remotely: S3 + DynamoDB (AWS), GCS (GCP), Azure Blob + Table (Azure).
- Enable state locking — never disable it in shared environments. DynamoDB provides locking for S3 backends.
- Use separate state files per environment and per logical component: networking state separate from application state.
- Never store secrets in Terraform variables or state directly — use AWS Secrets Manager, Vault, or equivalent data sources.
- Run `terraform plan` before every `apply`. Review the plan for unexpected changes, replacements, or deletions.
- Enable versioning on the state bucket for rollback capability.
Modules
- Design modules around single logical resource groups: one module for a VPC, another for an ECS service.
- Version modules with Git tags and reference specific versions: `source = "git::https://...?ref=v1.4.0"`.
- Follow the standard module structure: `main.tf`, `variables.tf`, `outputs.tf`, `versions.tf`, optionally `README.md`.
- Never define `provider {}` blocks inside modules — let the calling root module configure all providers.
- Validate inputs with `validation` blocks so modules fail fast with actionable error messages on bad configuration.
- Document module usage with examples and generate documentation with `terraform-docs`.
Testing & Validation
- Run `terraform plan` as the first line of testing in CI — inspect for expected resource changes.
- Use `checkov`, `tfsec`, or `trivy` for static analysis of security and compliance issues in HCL code.
- Write automated integration tests with Terratest (Go) in isolated environments for critical modules.
- Create a `test/` directory with a minimal configuration that calls the module to verify it applies cleanly.
- Run drift detection on a schedule to identify resources modified outside Terraform.
Security & Compliance
- Follow least-privilege for IAM: never use wildcard `*` actions or resources on production roles.
- Encrypt all storage at rest (S3, EBS, RDS) and in transit (TLS) — enforce via `aws_s3_bucket_server_side_encryption_configuration`.
- Use private subnets for compute resources. Expose only load balancers and API gateways publicly.
- Review security group rules: avoid `0.0.0.0/0` ingress except for intentional public endpoints on specific ports.
- Enable CloudTrail, VPC Flow Logs, and S3 access logging for auditability.
- Use Sentinel or OPA policies to enforce compliance rules before changes are applied in production.