--- name: terraform description: >- Guide for writing production-quality Terraform and Terragrunt infrastructure code following HashiCorp and community best practices. Triggers on "terraform", "terragrunt", "tf", "hcl", "infrastructure as code", "iac", "aws terraform", "gcp terraform", "azure terraform", "terraform module", "terraform state", "terraform plan", "terraform apply", "terraform init", "terraform workspace", "terraform backend", "terraform provider", "terraform resource", "terraform data", "terraform variable", "terraform output", "terraform locals", "terraform for_each", "terraform count", "terraform dynamic", "tfvars", "terraform validate", "terraform fmt", "tflint", "terraform import", "terraform destroy", "remote state", "state locking", "terraform cloud", "opentofu". PROACTIVE: MUST invoke when writing ANY .tf or .hcl file. allowed-tools: Read, Write, Edit, Bash, Glob, Grep --- # ABOUTME: Terraform and Terragrunt skill for production-quality infrastructure code # ABOUTME: Enforces HashiCorp best practices, DRY patterns, and security-first design # Terraform & Terragrunt Skill ## Quick Reference | Principle | Rule | |-----------|------| | DRY | Use modules and Terragrunt to eliminate repetition | | Immutability | Prefer replacement over modification | | Security | No secrets in state; use data sources for sensitive values | | Naming | `__` format | | State | Remote backend always; never local for shared infra | | Modules | Input validation, sensible defaults, documented outputs | ## 🛑 FILE OPERATION CHECKPOINT (BLOCKING) **Before EVERY `Write` or `Edit` tool call on a `.tf` or `.hcl` file:** ``` ╔══════════════════════════════════════════════════════════════════╗ ║ 🛑 STOP - TERRAFORM SKILL CHECK ║ ║ ║ ║ You are about to modify a Terraform/Terragrunt file. ║ ║ ║ ║ QUESTION: Is /terraform skill currently active? ║ ║ ║ ║ If YES → Proceed with the edit ║ ║ If NO → STOP! Invoke /terraform FIRST, then edit ║ ║ ║ ║ This check applies to: ║ ║ ✗ Write tool with file_path ending in .tf ║ ║ ✗ Edit tool with file_path ending in .tf ║ ║ ✗ Write/Edit with file_path ending in .hcl ║ ║ ✗ Files named terragrunt.hcl ║ ║ ✗ ANY Terraform file, regardless of conversation topic ║ ║ ║ ║ Examples that REQUIRE this skill: ║ ║ - "add a new resource" (edits main.tf) ║ ║ - "update the variables" (edits variables.tf) ║ ║ - "configure the backend" (edits terragrunt.hcl) ║ ╚══════════════════════════════════════════════════════════════════╝ ``` **Why this matters:** Terraform code with hardcoded secrets or missing validations creates security risks. The skill ensures remote state and proper variable handling. ## 🔄 RESUMED SESSION CHECKPOINT **When a session is resumed from context compaction, verify Terraform development state:** ``` ┌─────────────────────────────────────────────────────────────┐ │ SESSION RESUMED - TERRAFORM SKILL VERIFICATION │ │ │ │ Before continuing Terraform implementation: │ │ │ │ 1. Was I in the middle of writing Terraform/Terragrunt? │ │ → Check summary for ".tf", "module", "terragrunt" │ │ │ │ 2. Did I follow all Terraform skill guidelines? │ │ → No hardcoded secrets │ │ → Remote state backend configured │ │ → Variables have descriptions and validations │ │ → ABOUTME headers on new files │ │ │ │ 3. Check code quality before continuing: │ │ → Run: terraform fmt -check -recursive │ │ → Run: terraform validate │ │ → Run: tflint (if available) │ │ │ │ If implementation was in progress: │ │ → Review the partial code for completeness │ │ → Ensure all resources have proper naming │ │ → Verify no sensitive data in outputs │ │ → Re-invoke /terraform if skill context was lost │ └─────────────────────────────────────────────────────────────┘ ``` ## When to Use Terraform **Use Terraform for:** - Cloud infrastructure provisioning (AWS, GCP, Azure, etc.) - Multi-cloud and hybrid deployments - Infrastructure that requires versioning and audit trails - Reproducible environments (dev, staging, prod) - Kubernetes cluster provisioning (not workloads) **Use Terragrunt for:** - Managing multiple environments with DRY configurations - Orchestrating module dependencies - Managing remote state configuration - Running Terraform across multiple modules **Do NOT use Terraform for:** - Application deployment (use Kubernetes, Docker, or CI/CD) - Configuration management (use Ansible, Chef, Puppet) - Secret management storage (use Vault, AWS Secrets Manager) ## Project Structure ### Standard Module Structure ``` module/ ├── main.tf # Primary resources ├── variables.tf # Input variable declarations ├── outputs.tf # Output value declarations ├── versions.tf # Provider and terraform version constraints ├── locals.tf # Local values (optional) ├── data.tf # Data sources (optional) └── README.md # Module documentation ``` ### Terragrunt Project Structure ``` infrastructure/ ├── terragrunt.hcl # Root configuration ├── modules/ # Reusable modules │ ├── vpc/ │ ├── eks/ │ └── rds/ ├── environments/ │ ├── common.hcl # Shared variables │ ├── dev/ │ │ ├── terragrunt.hcl # Environment config │ │ ├── vpc/ │ │ │ └── terragrunt.hcl │ │ └── eks/ │ │ └── terragrunt.hcl │ ├── staging/ │ │ └── ... │ └── prod/ │ └── ... └── README.md ``` ## Core Patterns ### Provider Configuration ```hcl # versions.tf terraform { required_version = ">= 1.6.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } } provider "aws" { region = var.aws_region default_tags { tags = { Environment = var.environment ManagedBy = "terraform" Project = var.project_name } } } ``` ### Variable Definitions with Validation ```hcl # variables.tf variable "environment" { description = "Deployment environment (dev, staging, prod)" type = string validation { condition = contains(["dev", "staging", "prod"], var.environment) error_message = "Environment must be one of: dev, staging, prod." } } variable "instance_type" { description = "EC2 instance type for the application servers" type = string default = "t3.medium" validation { condition = can(regex("^t3\\.", var.instance_type)) error_message = "Instance type must be from the t3 family." } } variable "vpc_cidr" { description = "CIDR block for the VPC" type = string validation { condition = can(cidrhost(var.vpc_cidr, 0)) error_message = "VPC CIDR must be a valid CIDR block." } } ``` ### Resource Naming Convention ```hcl # Use locals for consistent naming locals { name_prefix = "${var.project_name}-${var.environment}" common_tags = { Project = var.project_name Environment = var.environment ManagedBy = "terraform" } } resource "aws_vpc" "main" { cidr_block = var.vpc_cidr enable_dns_hostnames = true enable_dns_support = true tags = merge(local.common_tags, { Name = "${local.name_prefix}-vpc" }) } ``` ### Output Definitions ```hcl # outputs.tf output "vpc_id" { description = "ID of the created VPC" value = aws_vpc.main.id } output "private_subnet_ids" { description = "List of private subnet IDs" value = aws_subnet.private[*].id } # NEVER expose sensitive values without marking them output "database_password" { description = "Database master password" value = random_password.db.result sensitive = true } ``` ## Terragrunt Patterns ### Root Configuration ```hcl # terragrunt.hcl (root) locals { account_vars = read_terragrunt_config(find_in_parent_folders("account.hcl")) region_vars = read_terragrunt_config(find_in_parent_folders("region.hcl")) env_vars = read_terragrunt_config(find_in_parent_folders("env.hcl")) account_id = local.account_vars.locals.account_id aws_region = local.region_vars.locals.aws_region environment = local.env_vars.locals.environment } generate "provider" { path = "provider.tf" if_exists = "overwrite_terragrunt" contents = <