# Terraform Patterns

> When to activate: Terraform, HCL, infrastructure as code, IaC, tf, tfvars, module, state, provider, workspace

- Skill: `mattakushi432/terraform-patterns` (Agent Skill)
- Install (CLI): `npx skillmds@latest add mattakushi432/terraform-patterns`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mattakushi432/terraform-patterns/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: Mattakushi432 (https://skillmd.com/u/mattakushi432)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mattakushi432/terraform-patterns

---

# Terraform Patterns

## Module Structure

```
modules/
  vpc/
    main.tf
    variables.tf
    outputs.tf
    versions.tf
environments/
  prod/
    main.tf
    terraform.tfvars
  staging/
    main.tf
    terraform.tfvars
```

## versions.tf — Pin providers

```hcl
terraform {
  required_version = ">= 1.7"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
  backend "s3" {
    bucket         = "mycompany-tfstate"
    key            = "prod/app/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-lock"
  }
}
```

## Variables with Validation

```hcl
variable "environment" {
  type        = string
  description = "Deployment environment"
  validation {
    condition     = contains(["prod", "staging", "dev"], var.environment)
    error_message = "environment must be prod, staging, or dev"
  }
}

variable "instance_count" {
  type    = number
  default = 2
  validation {
    condition     = var.instance_count >= 1 && var.instance_count <= 10
    error_message = "instance_count must be between 1 and 10"
  }
}
```

## Loops with for_each

```hcl
variable "buckets" {
  type = map(object({
    versioning = bool
    region     = string
  }))
  default = {
    assets  = { versioning = false, region = "us-east-1" }
    backups = { versioning = true, region = "us-west-2" }
  }
}

resource "aws_s3_bucket" "this" {
  for_each = var.buckets
  bucket   = "${var.environment}-${each.key}"
}

resource "aws_s3_bucket_versioning" "this" {
  for_each = { for k, v in var.buckets : k => v if v.versioning }
  bucket   = aws_s3_bucket.this[each.key].id
  versioning_configuration {
    status = "Enabled"
  }
}
```

## Conditional Resources

```hcl
resource "aws_cloudwatch_log_group" "this" {
  count             = var.enable_logging ? 1 : 0
  name              = "/app/${var.environment}"
  retention_in_days = var.environment == "prod" ? 90 : 7
}
```

## Locals for Computed Values

```hcl
locals {
  common_tags = {
    Environment = var.environment
    ManagedBy   = "terraform"
    Team        = var.team
  }
  is_prod     = var.environment == "prod"
  db_size     = local.is_prod ? "db.r6g.large" : "db.t3.medium"
}

resource "aws_db_instance" "this" {
  instance_class = local.db_size
  tags           = local.common_tags
}
```

## Data Sources

```hcl
data "aws_vpc" "default" {
  default = true
}

data "aws_subnets" "public" {
  filter {
    name   = "vpc-id"
    values = [data.aws_vpc.default.id]
  }
  tags = { Tier = "public" }
}

data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]
  filter {
    name   = "name"
    values = ["al2023-ami-*-x86_64"]
  }
}
```

## Outputs

```hcl
output "vpc_id" {
  value       = aws_vpc.this.id
  description = "VPC ID"
}

output "db_endpoint" {
  value     = aws_db_instance.this.endpoint
  sensitive = true
}
```

## Workspaces Pattern

```bash
terraform workspace new staging
terraform workspace select prod
terraform plan -var-file="environments/${terraform.workspace}.tfvars"
```

## Key Rules
- Always use remote state with locking (S3 + DynamoDB or Terraform Cloud)
- Use `terraform plan -out=tfplan` then `terraform apply tfplan` in CI
- Never commit `.terraform/` or `*.tfstate` to git
- Use `moved` blocks instead of destroy/recreate when refactoring resources
- `lifecycle { prevent_destroy = true }` on critical resources (databases)

