Terraform patterns

Terraform infrastructure-as-code agent skill and plugin for Claude Code, Codex, Gemini CLI, Cursor, OpenClaw.

How to use it

Claude Code
  1. Run the line below. It pulls the whole folder into ~/.claude/skills/terraform-patterns, including the files SKILL.md points to.
  2. Describe your job in plain words. Claude Code follows the skill from there.
Claude Code — installs the whole folder, not just SKILL.md
npx degit alirezarezvani/claude-skills/engineering/terraform-patterns/skills/terraform-patterns#main ~/.claude/skills/terraform-patterns

For one project only, change the path to .claude/skills/terraform-patterns. This skill also uses plan.json, infracost-base.json, infracost.yml — copying SKILL.md alone won't be enough. See the folder on GitHub.

Claude (web or desktop app)
  1. On this page open ⋯ → Download .md.
  2. Save it as SKILL.md in a folder, zip the folder, then Customize → Skills → + → Create skill → Upload a skill.
  3. Pick the file and Save. Claude shows the name and description and runs a security scan.
  4. Check the skill is switched on.
  5. Start a new chat and describe your job in plain words. The AI follows the skill from there.
ChatGPT or another app
  1. ChatGPT: make a Project and paste it into Instructions.
  2. Neither? Paste it at the top of a new chat — it works for that chat.
Not working?
  • Check which app you pasted it into — the steps above name the right one.
  • Some skills need the paid tier of Claude or ChatGPT.
Step-by-step guide with screenshots · Ask in the forum

Paste into Claude, ChatGPT or Cursor.

Source of Terraform patterns

Show the full text741 lines
namedescriptionlicensemetadata
terraform-patternsTerraform infrastructure-as-code agent skill and plugin for Claude Code, Codex, Gemini CLI, Cursor, OpenClaw. Covers module design patterns, state management strategies, provider configuration, security hardening, policy-as-code with Sentinel/OPA, and CI/CD plan/apply workflows. Use when: user wants to design Terraform modules, manage state backends, review Terraform security, implement multi-region deployments, or follow IaC best practices.MIT version: 1.0.0 author: Alireza Rezvani category: engineering updated: 2026-03-15

Terraform Patterns

Predictable infrastructure. Secure state. Modules that compose. No drift.

Opinionated Terraform workflow that turns sprawling HCL into well-structured, secure, production-grade infrastructure code. Covers module design, state management, provider patterns, security hardening, and CI/CD integration.

Not a Terraform tutorial — a set of concrete decisions about how to write infrastructure code that doesn't break at 3 AM.


Slash Commands

Command What it does
/terraform:review Analyze Terraform code for anti-patterns, security issues, and structure problems
/terraform:module Design or refactor a Terraform module with proper inputs, outputs, and composition
/terraform:security Audit Terraform code for security vulnerabilities, secrets exposure, and IAM misconfigurations

When This Skill Activates

Recognize these patterns from the user:

  • "Review this Terraform code"
  • "Design a Terraform module for..."
  • "My Terraform state is..."
  • "Set up remote state backend"
  • "Multi-region Terraform deployment"
  • "Terraform security review"
  • "Module structure best practices"
  • "Terraform CI/CD pipeline"
  • Any request involving: .tf files, HCL, Terraform modules, state management, provider configuration, infrastructure-as-code

If the user has .tf files or wants to provision infrastructure with Terraform → this skill applies.


Workflow

/terraform:review — Terraform Code Review
  1. Analyze current state

    • Read all .tf files in the target directory
    • Identify module structure (flat vs nested)
    • Count resources, data sources, variables, outputs
    • Check naming conventions
  2. Apply review checklist

    MODULE STRUCTURE
    ├── Variables have descriptions and type constraints
    ├── Outputs expose only what consumers need
    ├── Resources use consistent naming: {provider}_{type}_{purpose}
    ├── Locals used for computed values and DRY expressions
    └── No hardcoded values — everything parameterized or in locals
    
    STATE & BACKEND
    ├── Remote backend configured (S3, GCS, Azure Blob, Terraform Cloud)
    ├── State locking enabled (DynamoDB for S3, native for others)
    ├── State encryption at rest enabled
    ├── No secrets stored in state (or state access is restricted)
    └── Workspaces or directory isolation for environments
    
    PROVIDERS
    ├── Version constraints use pessimistic operator: ~> 5.0
    ├── Required providers block in terraform {} block
    ├── Provider aliases for multi-region or multi-account
    └── No provider configuration in child modules
    
    SECURITY
    ├── No hardcoded secrets, keys, or passwords
    ├── IAM follows least-privilege principle
    ├── Encryption enabled for storage, databases, secrets
    ├── Security groups are not overly permissive (no 0.0.0.0/0 ingress on sensitive ports)
    └── Sensitive variables marked with sensitive = true
    
  3. Generate report

    python3 scripts/tf_module_analyzer.py ./terraform
    
  4. Run security scan

    python3 scripts/tf_security_scanner.py ./terraform
    
/terraform:module — Module Design
  1. Identify module scope

    • Single responsibility: one module = one logical grouping
    • Determine inputs (variables), outputs, and resource boundaries
    • Decide: flat module (single directory) vs nested (calling child modules)
  2. Apply module design checklist

    STRUCTURE
    ├── main.tf        — Primary resources
    ├── variables.tf   — All input variables with descriptions and types
    ├── outputs.tf     — All outputs with descriptions
    ├── versions.tf    — terraform {} block with required_providers
    ├── locals.tf      — Computed values and naming conventions
    ├── data.tf        — Data sources (if any)
    └── README.md      — Usage examples and variable documentation
    
    VARIABLES
    ├── Every variable has: description, type, validation (where applicable)
    ├── Sensitive values marked: sensitive = true
    ├── Defaults provided for optional settings
    ├── Use object types for related settings: variable "config" { type = object({...}) }
    └── Validate with: validation { condition = ... }
    
    OUTPUTS
    ├── Output IDs, ARNs, endpoints — things consumers need
    ├── Include description on every output
    ├── Mark sensitive outputs: sensitive = true
    └── Don't output entire resources — only specific attributes
    
    COMPOSITION
    ├── Root module calls child modules
    ├── Child modules never call other child modules
    ├── Pass values explicitly — no hidden data source lookups in child modules
    ├── Provider configuration only in root module
    └── Use module "name" { source = "./modules/name" }
    
  3. Generate module scaffold

    • Output file structure with boilerplate
    • Include variable validation blocks
    • Add lifecycle rules where appropriate
/terraform:security — Security Audit
  1. Code-level audit

    Check Severity Fix
    Hardcoded secrets in .tf files Critical Use variables with sensitive = true or vault
    IAM policy with * actions Critical Scope to specific actions and resources
    Security group with 0.0.0.0/0 on port 22/3389 Critical Restrict to known CIDR blocks or use SSM/bastion
    S3 bucket without encryption High Add server_side_encryption_configuration block
    S3 bucket with public access High Add aws_s3_bucket_public_access_block
    RDS without encryption High Set storage_encrypted = true
    RDS publicly accessible High Set publicly_accessible = false
    CloudTrail not enabled Medium Add aws_cloudtrail resource
    Missing prevent_destroy on stateful resources Medium Add lifecycle { prevent_destroy = true }
    Variables without sensitive = true for secrets Medium Add sensitive = true to secret variables
  2. State security audit

    Check Severity Fix
    Local state file Critical Migrate to remote backend with encryption
    Remote state without encryption High Enable encryption on backend (SSE-S3, KMS)
    No state locking High Enable DynamoDB for S3, native for TF Cloud
    State accessible to all team members Medium Restrict via IAM policies or TF Cloud teams
  3. Generate security report

    python3 scripts/tf_security_scanner.py ./terraform
    python3 scripts/tf_security_scanner.py ./terraform --output json
    

Tooling

scripts/tf_module_analyzer.py

CLI utility for analyzing Terraform directory structure and module quality.

Features:

  • Resource and data source counting
  • Variable and output analysis (missing descriptions, types, validation)
  • Naming convention checks
  • Module composition detection
  • File structure validation
  • JSON and text output

Usage:

# Analyze a Terraform directory
python3 scripts/tf_module_analyzer.py ./terraform

# JSON output
python3 scripts/tf_module_analyzer.py ./terraform --output json

# Analyze a specific module
python3 scripts/tf_module_analyzer.py ./modules/vpc
scripts/tf_security_scanner.py

CLI utility for scanning .tf files for common security issues.

Features:

  • Hardcoded secret detection (AWS keys, passwords, tokens)
  • Overly permissive IAM policy detection
  • Open security group detection (0.0.0.0/0 on sensitive ports)
  • Missing encryption checks (S3, RDS, EBS)
  • Public access detection (S3, RDS, EC2)
  • Sensitive variable audit
  • JSON and text output

Usage:

# Scan a Terraform directory
python3 scripts/tf_security_scanner.py ./terraform

# JSON output
python3 scripts/tf_security_scanner.py ./terraform --output json

# Strict mode (elevate warnings)
python3 scripts/tf_security_scanner.py ./terraform --strict

Module Design Patterns

Pattern 1: Flat Module (Small/Medium Projects)
infrastructure/
├── main.tf          # All resources
├── variables.tf     # All inputs
├── outputs.tf       # All outputs
├── versions.tf      # Provider requirements
├── terraform.tfvars # Environment values (not committed)
└── backend.tf       # Remote state configuration

Best for: Single application, < 20 resources, one team owns everything.

Pattern 2: Nested Modules (Medium/Large Projects)
infrastructure/
├── environments/
│   ├── dev/
│   │   ├── main.tf          # Calls modules with dev params
│   │   ├── backend.tf       # Dev state backend
│   │   └── terraform.tfvars
│   ├── staging/
│   │   └── ...
│   └── prod/
│       └── ...
├── modules/
│   ├── networking/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   ├── compute/
│   │   └── ...
│   └── database/
│       └── ...
└── versions.tf

Best for: Multiple environments, shared infrastructure patterns, team collaboration.

Pattern 3: Mono-Repo with Terragrunt
infrastructure/
├── terragrunt.hcl           # Root config
├── modules/                  # Reusable modules
│   ├── vpc/
│   ├── eks/
│   └── rds/
├── dev/
│   ├── terragrunt.hcl       # Dev overrides
│   ├── vpc/
│   │   └── terragrunt.hcl   # Module invocation
│   └── eks/
│       └── terragrunt.hcl
└── prod/
    ├── terragrunt.hcl
    └── ...

Best for: Large-scale, many environments, DRY configuration, team-level isolation.


Provider Configuration Patterns

Version Pinning
terraform {
  required_version = ">= 1.5.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"    # Allow 5.x, block 6.0
    }
    random = {
      source  = "hashicorp/random"
      version = "~> 3.5"
    }
  }
}
Multi-Region with Aliases
provider "aws" {
  region = "us-east-1"
}

provider "aws" {
  alias  = "west"
  region = "us-west-2"
}

resource "aws_s3_bucket" "primary" {
  bucket = "my-app-primary"
}

resource "aws_s3_bucket" "replica" {
  provider = aws.west
  bucket   = "my-app-replica"
}
Multi-Account with Assume Role
provider "aws" {
  alias  = "production"
  region = "us-east-1"

  assume_role {
    role_arn = "arn:aws:iam::PROD_ACCOUNT_ID:role/TerraformRole"
  }
}

State Management Decision Tree

Single developer, small project?
├── Yes → Local state (but migrate to remote ASAP)
└── No
    ├── Using Terraform Cloud/Enterprise?
    │   └── Yes → TF Cloud native backend (built-in locking, encryption, RBAC)
    └── No
        ├── AWS?
        │   └── S3 + DynamoDB (encryption, locking, versioning)
        ├── GCP?
        │   └── GCS bucket (native locking, encryption)
        ├── Azure?
        │   └── Azure Blob Storage (native locking, encryption)
        └── Other?
            └── Consul or PostgreSQL backend

Environment isolation strategy:
├── Separate state files per environment (recommended)
│   ├── Option A: Separate directories (dev/, staging/, prod/)
│   └── Option B: Terraform workspaces (simpler but less isolation)
└── Single state file for all environments (never do this)

CI/CD Integration Patterns

GitHub Actions Plan/Apply
# .github/workflows/terraform.yml
name: Terraform
on:
  pull_request:
    paths: ['terraform/**']
  push:
    branches: [main]
    paths: ['terraform/**']

jobs:
  plan:
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - run: terraform init
      - run: terraform validate
      - run: terraform plan -out=tfplan
      - run: terraform show -json tfplan > plan.json
      # Post plan as PR comment

  apply:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    environment: production
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - run: terraform init
      - run: terraform apply -auto-approve
Drift Detection
# Run on schedule to detect drift
name: Drift Detection
on:
  schedule:
    - cron: '0 6 * * 1-5'  # Weekdays at 6 AM

jobs:
  detect:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - run: terraform init
      - run: |
          terraform plan -detailed-exitcode -out=drift.tfplan 2>&1 | tee drift.log
          EXIT_CODE=$?
          if [ $EXIT_CODE -eq 2 ]; then
            echo "DRIFT DETECTED — review drift.log"
            # Send alert (Slack, PagerDuty, etc.)
          fi

Proactive Triggers

Flag these without being asked:

  • No remote backend configured → Migrate to S3/GCS/Azure Blob with locking and encryption.
  • Provider without version constraint → Add version = "~> X.0" to prevent breaking upgrades.
  • Hardcoded secrets in .tf files → Use variables with sensitive = true, or integrate Vault/SSM.
  • IAM policy with "Action": "*" → Scope to specific actions. No wildcard actions in production.
  • Security group open to 0.0.0.0/0 on SSH/RDP → Restrict to bastion CIDR or use SSM Session Manager.
  • No state locking → Enable DynamoDB table for S3 backend, or use TF Cloud.
  • Resources without tags → Add default_tags in provider block. Tags are mandatory for cost tracking.
  • Missing prevent_destroy on databases/storage → Add lifecycle block to prevent accidental deletion.

Multi-Cloud Provider Configuration

When a single root module must provision across AWS, Azure, and GCP simultaneously.

Provider Aliasing Pattern
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.0"
    }
    google = {
      source  = "hashicorp/google"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = var.aws_region
}

provider "azurerm" {
  features {}
  subscription_id = var.azure_subscription_id
}

provider "google" {
  project = var.gcp_project_id
  region  = var.gcp_region
}
Shared Variables Across Providers
variable "environment" {
  description = "Environment name used across all providers"
  type        = string
  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Must be dev, staging, or prod."
  }
}

locals {
  common_tags = {
    environment = var.environment
    managed_by  = "terraform"
    project     = var.project_name
  }
}
When to Use Multi-Cloud
  • Yes: Regulatory requirements mandate data residency across providers, or the org has existing workloads on multiple clouds.
  • No: "Avoiding vendor lock-in" alone is not sufficient justification. Multi-cloud doubles operational complexity. Prefer single-cloud unless there is a concrete business requirement.

OpenTofu Compatibility

OpenTofu is an open-source fork of Terraform maintained by the Linux Foundation under the MPL 2.0 license.

Migration from Terraform to OpenTofu
# 1. Install OpenTofu
brew install opentofu        # macOS
snap install --classic tofu  # Linux

# 2. Replace the binary — state files are compatible
tofu init                    # Re-initializes with OpenTofu
tofu plan                    # Identical plan output
tofu apply                   # Same apply workflow
License Considerations
Terraform (1.6+) OpenTofu
License BSL 1.1 (source-available) MPL 2.0 (open-source)
Commercial use Restricted for competing products Unrestricted
Community governance HashiCorp Linux Foundation
Feature Parity

OpenTofu tracks Terraform 1.6.x features. Key additions unique to OpenTofu:

  • Client-side state encryption (tofu init -encryption)
  • Early variable/locals evaluation
  • Provider-defined functions
When to Choose OpenTofu
  • You need a fully open-source license for your supply chain.
  • You want client-side state encryption without Terraform Cloud.
  • Otherwise, either tool works — the HCL syntax and provider ecosystem are identical.

Infracost Integration

Infracost estimates cloud costs from Terraform code before resources are provisioned.

PR Workflow
# Show cost breakdown for current code
infracost breakdown --path .

# Compare cost difference between current branch and main
infracost diff --path . --compare-to infracost-base.json
GitHub Actions Cost Comment
# .github/workflows/infracost.yml
name: Infracost
on: [pull_request]

jobs:
  cost:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: infracost/actions/setup@v3
        with:
          api-key: ${{ secrets.INFRACOST_API_KEY }}
      - run: infracost breakdown --path ./terraform --format json --out-file /tmp/infracost.json
      - run: infracost comment github --path /tmp/infracost.json --repo $GITHUB_REPOSITORY --pull-request ${{ github.event.pull_request.number }} --github-token ${{ secrets.GITHUB_TOKEN }} --behavior update
Budget Thresholds and Cost Policy
# infracost.yml — policy file
version: 2.9.0
policies:
  - path: "*"
    max_monthly_cost: "5000"    # Fail PR if estimated cost exceeds $5,000/month
    max_cost_increase: "500"    # Fail PR if cost increase exceeds $500/month

Import Existing Infrastructure

Bring manually-created resources under Terraform management.

terraform import Workflow
# 1. Write the resource block first (empty body is fine)
# main.tf:
# resource "aws_s3_bucket" "legacy" {}

# 2. Import the resource into state
terraform import aws_s3_bucket.legacy my-existing-bucket-name

# 3. Run plan to see attribute diff
terraform plan

# 4. Fill in the resource block until plan shows no changes
Bulk Import with Config Generation (Terraform 1.5+)
# Generate HCL for imported resources
terraform plan -generate-config-out=generated.tf

# Review generated.tf, then move resources into proper files
Common Pitfalls
  • Resource drift after import: The imported resource may have attributes Terraform does not manage. Run terraform plan immediately and resolve every diff.
  • State manipulation: Use terraform state mv to rename or reorganize. Use terraform state rm to remove without destroying. Always back up state before manipulation: terraform state pull > backup.tfstate.
  • Sensitive defaults: Imported resources may expose secrets in state. Restrict state access and enable encryption.

Terragrunt Patterns

Terragrunt is a thin wrapper around Terraform that provides DRY configuration for multi-environment setups.

Root terragrunt.hcl (Shared Config)
# terragrunt.hcl (root)
remote_state {
  backend = "s3"
  generate = {
    path      = "backend.tf"
    if_exists = "overwrite_terragrunt"
  }
  config = {
    bucket         = "my-org-terraform-state"
    key            = "${path_relative_to_include()}/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}
Child terragrunt.hcl (Environment Override)
# prod/vpc/terragrunt.hcl
include "root" {
  path = find_in_parent_folders()
}

terraform {
  source = "../../modules/vpc"
}

inputs = {
  environment = "prod"
  cidr_block  = "10.0.0.0/16"
}
Dependencies Between Modules
# prod/eks/terragrunt.hcl
dependency "vpc" {
  config_path = "../vpc"
}

inputs = {
  vpc_id     = dependency.vpc.outputs.vpc_id
  subnet_ids = dependency.vpc.outputs.private_subnet_ids
}
When Terragrunt Adds Value
  • Yes: 3+ environments with identical module structure, shared backend config, or cross-module dependencies.
  • No: Single environment, small team, or simple directory-based isolation already works. Terragrunt adds a learning curve and another binary to manage.

Installation

One-liner (any tool)
git clone https://github.com/alirezarezvani/claude-skills.git
cp -r claude-skills/engineering/terraform-patterns ~/.claude/skills/
Multi-tool install
./scripts/convert.sh --skill terraform-patterns --tool codex|gemini|cursor|windsurf|openclaw
OpenClaw
clawhub install terraform-patterns

  • senior-devops — Broader DevOps scope (CI/CD, monitoring, containerization). Complementary — use terraform-patterns for IaC-specific work, senior-devops for pipeline and infrastructure operations.
  • aws-solution-architect — AWS architecture design. Complementary — terraform-patterns implements the infrastructure, aws-solution-architect designs it.
  • senior-security — Application security. Complementary — terraform-patterns covers infrastructure security posture, senior-security covers application-level threats.
  • ci-cd-pipeline-builder — Pipeline construction. Complementary — terraform-patterns defines infrastructure, ci-cd-pipeline-builder automates deployment.
1---
2name: "terraform-patterns"
3description: "Terraform infrastructure-as-code agent skill and plugin for Claude Code, Codex, Gemini CLI, Cursor, OpenClaw. Covers module design patterns, state management strategies, provider configuration, security hardening, policy-as-code with Sentinel/OPA, and CI/CD plan/apply workflows. Use when: user wants to design Terraform modules, manage state backends, review Terraform security, implement multi-region deployments, or follow IaC best practices."
4license: MIT
5metadata:
6 version: 1.0.0
7 author: Alireza Rezvani
8 category: engineering
9 updated: 2026-03-15
10---
11 
12# Terraform Patterns
13 
14> Predictable infrastructure. Secure state. Modules that compose. No drift.
15 
16Opinionated Terraform workflow that turns sprawling HCL into well-structured, secure, production-grade infrastructure code. Covers module design, state management, provider patterns, security hardening, and CI/CD integration.
17 
18Not a Terraform tutorial — a set of concrete decisions about how to write infrastructure code that doesn't break at 3 AM.
19 
20---
21 
22## Slash Commands
23 
24| Command | What it does |
25|---------|-------------|
26| `/terraform:review` | Analyze Terraform code for anti-patterns, security issues, and structure problems |
27| `/terraform:module` | Design or refactor a Terraform module with proper inputs, outputs, and composition |
28| `/terraform:security` | Audit Terraform code for security vulnerabilities, secrets exposure, and IAM misconfigurations |
29 
30---
31 
32## When This Skill Activates
33 
34Recognize these patterns from the user:
35 
36- "Review this Terraform code"
37- "Design a Terraform module for..."
38- "My Terraform state is..."
39- "Set up remote state backend"
40- "Multi-region Terraform deployment"
41- "Terraform security review"
42- "Module structure best practices"
43- "Terraform CI/CD pipeline"
44- Any request involving: `.tf` files, HCL, Terraform modules, state management, provider configuration, infrastructure-as-code
45 
46If the user has `.tf` files or wants to provision infrastructure with Terraform → this skill applies.
47 
48---
49 
50## Workflow
51 
52### `/terraform:review` — Terraform Code Review
53 
541. **Analyze current state**
55 - Read all `.tf` files in the target directory
56 - Identify module structure (flat vs nested)
57 - Count resources, data sources, variables, outputs
58 - Check naming conventions
59 
602. **Apply review checklist**
61 
62 ```
63 MODULE STRUCTURE
64 ├── Variables have descriptions and type constraints
65 ├── Outputs expose only what consumers need
66 ├── Resources use consistent naming: {provider}_{type}_{purpose}
67 ├── Locals used for computed values and DRY expressions
68 └── No hardcoded values — everything parameterized or in locals
69 
70 STATE & BACKEND
71 ├── Remote backend configured (S3, GCS, Azure Blob, Terraform Cloud)
72 ├── State locking enabled (DynamoDB for S3, native for others)
73 ├── State encryption at rest enabled
74 ├── No secrets stored in state (or state access is restricted)
75 └── Workspaces or directory isolation for environments
76 
77 PROVIDERS
78 ├── Version constraints use pessimistic operator: ~> 5.0
79 ├── Required providers block in terraform {} block
80 ├── Provider aliases for multi-region or multi-account
81 └── No provider configuration in child modules
82 
83 SECURITY
84 ├── No hardcoded secrets, keys, or passwords
85 ├── IAM follows least-privilege principle
86 ├── Encryption enabled for storage, databases, secrets
87 ├── Security groups are not overly permissive (no 0.0.0.0/0 ingress on sensitive ports)
88 └── Sensitive variables marked with sensitive = true
89 ```
90 
913. **Generate report**
92 ```bash
93 python3 scripts/tf_module_analyzer.py ./terraform
94 ```
95 
964. **Run security scan**
97 ```bash
98 python3 scripts/tf_security_scanner.py ./terraform
99 ```
100 
101### `/terraform:module` — Module Design
102 
1031. **Identify module scope**
104 - Single responsibility: one module = one logical grouping
105 - Determine inputs (variables), outputs, and resource boundaries
106 - Decide: flat module (single directory) vs nested (calling child modules)
107 
1082. **Apply module design checklist**
109 
110 ```
111 STRUCTURE
112 ├── main.tf — Primary resources
113 ├── variables.tf — All input variables with descriptions and types
114 ├── outputs.tf — All outputs with descriptions
115 ├── versions.tf — terraform {} block with required_providers
116 ├── locals.tf — Computed values and naming conventions
117 ├── data.tf — Data sources (if any)
118 └── README.md — Usage examples and variable documentation
119 
120 VARIABLES
121 ├── Every variable has: description, type, validation (where applicable)
122 ├── Sensitive values marked: sensitive = true
123 ├── Defaults provided for optional settings
124 ├── Use object types for related settings: variable "config" { type = object({...}) }
125 └── Validate with: validation { condition = ... }
126 
127 OUTPUTS
128 ├── Output IDs, ARNs, endpoints — things consumers need
129 ├── Include description on every output
130 ├── Mark sensitive outputs: sensitive = true
131 └── Don't output entire resources — only specific attributes
132 
133 COMPOSITION
134 ├── Root module calls child modules
135 ├── Child modules never call other child modules
136 ├── Pass values explicitly — no hidden data source lookups in child modules
137 ├── Provider configuration only in root module
138 └── Use module "name" { source = "./modules/name" }
139 ```
140 
1413. **Generate module scaffold**
142 - Output file structure with boilerplate
143 - Include variable validation blocks
144 - Add lifecycle rules where appropriate
145 
146### `/terraform:security` — Security Audit
147 
1481. **Code-level audit**
149 
150 | Check | Severity | Fix |
151 |-------|----------|-----|
152 | Hardcoded secrets in `.tf` files | Critical | Use variables with sensitive = true or vault |
153 | IAM policy with `*` actions | Critical | Scope to specific actions and resources |
154 | Security group with 0.0.0.0/0 on port 22/3389 | Critical | Restrict to known CIDR blocks or use SSM/bastion |
155 | S3 bucket without encryption | High | Add `server_side_encryption_configuration` block |
156 | S3 bucket with public access | High | Add `aws_s3_bucket_public_access_block` |
157 | RDS without encryption | High | Set `storage_encrypted = true` |
158 | RDS publicly accessible | High | Set `publicly_accessible = false` |
159 | CloudTrail not enabled | Medium | Add `aws_cloudtrail` resource |
160 | Missing `prevent_destroy` on stateful resources | Medium | Add `lifecycle { prevent_destroy = true }` |
161 | Variables without `sensitive = true` for secrets | Medium | Add `sensitive = true` to secret variables |
162 
1632. **State security audit**
164 
165 | Check | Severity | Fix |
166 |-------|----------|-----|
167 | Local state file | Critical | Migrate to remote backend with encryption |
168 | Remote state without encryption | High | Enable encryption on backend (SSE-S3, KMS) |
169 | No state locking | High | Enable DynamoDB for S3, native for TF Cloud |
170 | State accessible to all team members | Medium | Restrict via IAM policies or TF Cloud teams |
171 
1723. **Generate security report**
173 ```bash
174 python3 scripts/tf_security_scanner.py ./terraform
175 python3 scripts/tf_security_scanner.py ./terraform --output json
176 ```
177 
178---
179 
180## Tooling
181 
182### `scripts/tf_module_analyzer.py`
183 
184CLI utility for analyzing Terraform directory structure and module quality.
185 
186**Features:**
187- Resource and data source counting
188- Variable and output analysis (missing descriptions, types, validation)
189- Naming convention checks
190- Module composition detection
191- File structure validation
192- JSON and text output
193 
194**Usage:**
195```bash
196# Analyze a Terraform directory
197python3 scripts/tf_module_analyzer.py ./terraform
198 
199# JSON output
200python3 scripts/tf_module_analyzer.py ./terraform --output json
201 
202# Analyze a specific module
203python3 scripts/tf_module_analyzer.py ./modules/vpc
204```
205 
206### `scripts/tf_security_scanner.py`
207 
208CLI utility for scanning `.tf` files for common security issues.
209 
210**Features:**
211- Hardcoded secret detection (AWS keys, passwords, tokens)
212- Overly permissive IAM policy detection
213- Open security group detection (0.0.0.0/0 on sensitive ports)
214- Missing encryption checks (S3, RDS, EBS)
215- Public access detection (S3, RDS, EC2)
216- Sensitive variable audit
217- JSON and text output
218 
219**Usage:**
220```bash
221# Scan a Terraform directory
222python3 scripts/tf_security_scanner.py ./terraform
223 
224# JSON output
225python3 scripts/tf_security_scanner.py ./terraform --output json
226 
227# Strict mode (elevate warnings)
228python3 scripts/tf_security_scanner.py ./terraform --strict
229```
230 
231---
232 
233## Module Design Patterns
234 
235### Pattern 1: Flat Module (Small/Medium Projects)
236 
237```
238infrastructure/
239├── main.tf # All resources
240├── variables.tf # All inputs
241├── outputs.tf # All outputs
242├── versions.tf # Provider requirements
243├── terraform.tfvars # Environment values (not committed)
244└── backend.tf # Remote state configuration
245```
246 
247Best for: Single application, < 20 resources, one team owns everything.
248 
249### Pattern 2: Nested Modules (Medium/Large Projects)
250 
251```
252infrastructure/
253├── environments/
254│ ├── dev/
255│ │ ├── main.tf # Calls modules with dev params
256│ │ ├── backend.tf # Dev state backend
257│ │ └── terraform.tfvars
258│ ├── staging/
259│ │ └── ...
260│ └── prod/
261│ └── ...
262├── modules/
263│ ├── networking/
264│ │ ├── main.tf
265│ │ ├── variables.tf
266│ │ └── outputs.tf
267│ ├── compute/
268│ │ └── ...
269│ └── database/
270│ └── ...
271└── versions.tf
272```
273 
274Best for: Multiple environments, shared infrastructure patterns, team collaboration.
275 
276### Pattern 3: Mono-Repo with Terragrunt
277 
278```
279infrastructure/
280├── terragrunt.hcl # Root config
281├── modules/ # Reusable modules
282│ ├── vpc/
283│ ├── eks/
284│ └── rds/
285├── dev/
286│ ├── terragrunt.hcl # Dev overrides
287│ ├── vpc/
288│ │ └── terragrunt.hcl # Module invocation
289│ └── eks/
290│ └── terragrunt.hcl
291└── prod/
292 ├── terragrunt.hcl
293 └── ...
294```
295 
296Best for: Large-scale, many environments, DRY configuration, team-level isolation.
297 
298---
299 
300## Provider Configuration Patterns
301 
302### Version Pinning
303```hcl
304terraform {
305 required_version = ">= 1.5.0"
306 
307 required_providers {
308 aws = {
309 source = "hashicorp/aws"
310 version = "~> 5.0" # Allow 5.x, block 6.0
311 }
312 random = {
313 source = "hashicorp/random"
314 version = "~> 3.5"
315 }
316 }
317}
318```
319 
320### Multi-Region with Aliases
321```hcl
322provider "aws" {
323 region = "us-east-1"
324}
325 
326provider "aws" {
327 alias = "west"
328 region = "us-west-2"
329}
330 
331resource "aws_s3_bucket" "primary" {
332 bucket = "my-app-primary"
333}
334 
335resource "aws_s3_bucket" "replica" {
336 provider = aws.west
337 bucket = "my-app-replica"
338}
339```
340 
341### Multi-Account with Assume Role
342```hcl
343provider "aws" {
344 alias = "production"
345 region = "us-east-1"
346 
347 assume_role {
348 role_arn = "arn:aws:iam::PROD_ACCOUNT_ID:role/TerraformRole"
349 }
350}
351```
352 
353---
354 
355## State Management Decision Tree
356 
357```
358Single developer, small project?
359├── Yes → Local state (but migrate to remote ASAP)
360└── No
361 ├── Using Terraform Cloud/Enterprise?
362 │ └── Yes → TF Cloud native backend (built-in locking, encryption, RBAC)
363 └── No
364 ├── AWS?
365 │ └── S3 + DynamoDB (encryption, locking, versioning)
366 ├── GCP?
367 │ └── GCS bucket (native locking, encryption)
368 ├── Azure?
369 │ └── Azure Blob Storage (native locking, encryption)
370 └── Other?
371 └── Consul or PostgreSQL backend
372 
373Environment isolation strategy:
374├── Separate state files per environment (recommended)
375│ ├── Option A: Separate directories (dev/, staging/, prod/)
376│ └── Option B: Terraform workspaces (simpler but less isolation)
377└── Single state file for all environments (never do this)
378```
379 
380---
381 
382## CI/CD Integration Patterns
383 
384### GitHub Actions Plan/Apply
385 
386```yaml
387# .github/workflows/terraform.yml
388name: Terraform
389on:
390 pull_request:
391 paths: ['terraform/**']
392 push:
393 branches: [main]
394 paths: ['terraform/**']
395 
396jobs:
397 plan:
398 runs-on: ubuntu-latest
399 if: github.event_name == 'pull_request'
400 steps:
401 - uses: actions/checkout@v4
402 - uses: hashicorp/setup-terraform@v3
403 - run: terraform init
404 - run: terraform validate
405 - run: terraform plan -out=tfplan
406 - run: terraform show -json tfplan > plan.json
407 # Post plan as PR comment
408 
409 apply:
410 runs-on: ubuntu-latest
411 if: github.ref == 'refs/heads/main' && github.event_name == 'push'
412 environment: production
413 steps:
414 - uses: actions/checkout@v4
415 - uses: hashicorp/setup-terraform@v3
416 - run: terraform init
417 - run: terraform apply -auto-approve
418```
419 
420### Drift Detection
421 
422```yaml
423# Run on schedule to detect drift
424name: Drift Detection
425on:
426 schedule:
427 - cron: '0 6 * * 1-5' # Weekdays at 6 AM
428 
429jobs:
430 detect:
431 runs-on: ubuntu-latest
432 steps:
433 - uses: actions/checkout@v4
434 - uses: hashicorp/setup-terraform@v3
435 - run: terraform init
436 - run: |
437 terraform plan -detailed-exitcode -out=drift.tfplan 2>&1 | tee drift.log
438 EXIT_CODE=$?
439 if [ $EXIT_CODE -eq 2 ]; then
440 echo "DRIFT DETECTED — review drift.log"
441 # Send alert (Slack, PagerDuty, etc.)
442 fi
443```
444 
445---
446 
447## Proactive Triggers
448 
449Flag these without being asked:
450 
451- **No remote backend configured** → Migrate to S3/GCS/Azure Blob with locking and encryption.
452- **Provider without version constraint** → Add `version = "~> X.0"` to prevent breaking upgrades.
453- **Hardcoded secrets in .tf files** → Use variables with `sensitive = true`, or integrate Vault/SSM.
454- **IAM policy with `"Action": "*"`** → Scope to specific actions. No wildcard actions in production.
455- **Security group open to 0.0.0.0/0 on SSH/RDP** → Restrict to bastion CIDR or use SSM Session Manager.
456- **No state locking** → Enable DynamoDB table for S3 backend, or use TF Cloud.
457- **Resources without tags** → Add default_tags in provider block. Tags are mandatory for cost tracking.
458- **Missing `prevent_destroy` on databases/storage** → Add lifecycle block to prevent accidental deletion.
459 
460---
461 
462## Multi-Cloud Provider Configuration
463 
464When a single root module must provision across AWS, Azure, and GCP simultaneously.
465 
466### Provider Aliasing Pattern
467 
468```hcl
469terraform {
470 required_providers {
471 aws = {
472 source = "hashicorp/aws"
473 version = "~> 5.0"
474 }
475 azurerm = {
476 source = "hashicorp/azurerm"
477 version = "~> 3.0"
478 }
479 google = {
480 source = "hashicorp/google"
481 version = "~> 5.0"
482 }
483 }
484}
485 
486provider "aws" {
487 region = var.aws_region
488}
489 
490provider "azurerm" {
491 features {}
492 subscription_id = var.azure_subscription_id
493}
494 
495provider "google" {
496 project = var.gcp_project_id
497 region = var.gcp_region
498}
499```
500 
501### Shared Variables Across Providers
502 
503```hcl
504variable "environment" {
505 description = "Environment name used across all providers"
506 type = string
507 validation {
508 condition = contains(["dev", "staging", "prod"], var.environment)
509 error_message = "Must be dev, staging, or prod."
510 }
511}
512 
513locals {
514 common_tags = {
515 environment = var.environment
516 managed_by = "terraform"
517 project = var.project_name
518 }
519}
520```
521 
522### When to Use Multi-Cloud
523 
524- **Yes**: Regulatory requirements mandate data residency across providers, or the org has existing workloads on multiple clouds.
525- **No**: "Avoiding vendor lock-in" alone is not sufficient justification. Multi-cloud doubles operational complexity. Prefer single-cloud unless there is a concrete business requirement.
526 
527---
528 
529## OpenTofu Compatibility
530 
531OpenTofu is an open-source fork of Terraform maintained by the Linux Foundation under the MPL 2.0 license.
532 
533### Migration from Terraform to OpenTofu
534 
535```bash
536# 1. Install OpenTofu
537brew install opentofu # macOS
538snap install --classic tofu # Linux
539 
540# 2. Replace the binary — state files are compatible
541tofu init # Re-initializes with OpenTofu
542tofu plan # Identical plan output
543tofu apply # Same apply workflow
544```
545 
546### License Considerations
547 
548| | Terraform (1.6+) | OpenTofu |
549|---|---|---|
550| **License** | BSL 1.1 (source-available) | MPL 2.0 (open-source) |
551| **Commercial use** | Restricted for competing products | Unrestricted |
552| **Community governance** | HashiCorp | Linux Foundation |
553 
554### Feature Parity
555 
556OpenTofu tracks Terraform 1.6.x features. Key additions unique to OpenTofu:
557- Client-side state encryption (`tofu init -encryption`)
558- Early variable/locals evaluation
559- Provider-defined functions
560 
561### When to Choose OpenTofu
562 
563- You need a fully open-source license for your supply chain.
564- You want client-side state encryption without Terraform Cloud.
565- Otherwise, either tool works — the HCL syntax and provider ecosystem are identical.
566 
567---
568 
569## Infracost Integration
570 
571Infracost estimates cloud costs from Terraform code before resources are provisioned.
572 
573### PR Workflow
574 
575```bash
576# Show cost breakdown for current code
577infracost breakdown --path .
578 
579# Compare cost difference between current branch and main
580infracost diff --path . --compare-to infracost-base.json
581```
582 
583### GitHub Actions Cost Comment
584 
585```yaml
586# .github/workflows/infracost.yml
587name: Infracost
588on: [pull_request]
589 
590jobs:
591 cost:
592 runs-on: ubuntu-latest
593 steps:
594 - uses: actions/checkout@v4
595 - uses: infracost/actions/setup@v3
596 with:
597 api-key: ${{ secrets.INFRACOST_API_KEY }}
598 - run: infracost breakdown --path ./terraform --format json --out-file /tmp/infracost.json
599 - run: infracost comment github --path /tmp/infracost.json --repo $GITHUB_REPOSITORY --pull-request ${{ github.event.pull_request.number }} --github-token ${{ secrets.GITHUB_TOKEN }} --behavior update
600```
601 
602### Budget Thresholds and Cost Policy
603 
604```yaml
605# infracost.yml — policy file
606version: 2.9.0
607policies:
608 - path: "*"
609 max_monthly_cost: "5000" # Fail PR if estimated cost exceeds $5,000/month
610 max_cost_increase: "500" # Fail PR if cost increase exceeds $500/month
611```
612 
613---
614 
615## Import Existing Infrastructure
616 
617Bring manually-created resources under Terraform management.
618 
619### terraform import Workflow
620 
621```bash
622# 1. Write the resource block first (empty body is fine)
623# main.tf:
624# resource "aws_s3_bucket" "legacy" {}
625 
626# 2. Import the resource into state
627terraform import aws_s3_bucket.legacy my-existing-bucket-name
628 
629# 3. Run plan to see attribute diff
630terraform plan
631 
632# 4. Fill in the resource block until plan shows no changes
633```
634 
635### Bulk Import with Config Generation (Terraform 1.5+)
636 
637```bash
638# Generate HCL for imported resources
639terraform plan -generate-config-out=generated.tf
640 
641# Review generated.tf, then move resources into proper files
642```
643 
644### Common Pitfalls
645 
646- **Resource drift after import**: The imported resource may have attributes Terraform does not manage. Run `terraform plan` immediately and resolve every diff.
647- **State manipulation**: Use `terraform state mv` to rename or reorganize. Use `terraform state rm` to remove without destroying. Always back up state before manipulation: `terraform state pull > backup.tfstate`.
648- **Sensitive defaults**: Imported resources may expose secrets in state. Restrict state access and enable encryption.
649 
650---
651 
652## Terragrunt Patterns
653 
654Terragrunt is a thin wrapper around Terraform that provides DRY configuration for multi-environment setups.
655 
656### Root terragrunt.hcl (Shared Config)
657 
658```hcl
659# terragrunt.hcl (root)
660remote_state {
661 backend = "s3"
662 generate = {
663 path = "backend.tf"
664 if_exists = "overwrite_terragrunt"
665 }
666 config = {
667 bucket = "my-org-terraform-state"
668 key = "${path_relative_to_include()}/terraform.tfstate"
669 region = "us-east-1"
670 encrypt = true
671 dynamodb_table = "terraform-locks"
672 }
673}
674```
675 
676### Child terragrunt.hcl (Environment Override)
677 
678```hcl
679# prod/vpc/terragrunt.hcl
680include "root" {
681 path = find_in_parent_folders()
682}
683 
684terraform {
685 source = "../../modules/vpc"
686}
687 
688inputs = {
689 environment = "prod"
690 cidr_block = "10.0.0.0/16"
691}
692```
693 
694### Dependencies Between Modules
695 
696```hcl
697# prod/eks/terragrunt.hcl
698dependency "vpc" {
699 config_path = "../vpc"
700}
701 
702inputs = {
703 vpc_id = dependency.vpc.outputs.vpc_id
704 subnet_ids = dependency.vpc.outputs.private_subnet_ids
705}
706```
707 
708### When Terragrunt Adds Value
709 
710- **Yes**: 3+ environments with identical module structure, shared backend config, or cross-module dependencies.
711- **No**: Single environment, small team, or simple directory-based isolation already works. Terragrunt adds a learning curve and another binary to manage.
712 
713---
714 
715## Installation
716 
717### One-liner (any tool)
718```bash
719git clone https://github.com/alirezarezvani/claude-skills.git
720cp -r claude-skills/engineering/terraform-patterns ~/.claude/skills/
721```
722 
723### Multi-tool install
724```bash
725./scripts/convert.sh --skill terraform-patterns --tool codex|gemini|cursor|windsurf|openclaw
726```
727 
728### OpenClaw
729```bash
730clawhub install terraform-patterns
731```
732 
733---
734 
735## Related Skills
736 
737- **senior-devops** — Broader DevOps scope (CI/CD, monitoring, containerization). Complementary — use terraform-patterns for IaC-specific work, senior-devops for pipeline and infrastructure operations.
738- **aws-solution-architect** — AWS architecture design. Complementary — terraform-patterns implements the infrastructure, aws-solution-architect designs it.
739- **senior-security** — Application security. Complementary — terraform-patterns covers infrastructure security posture, senior-security covers application-level threats.
740- **ci-cd-pipeline-builder** — Pipeline construction. Complementary — terraform-patterns defines infrastructure, ci-cd-pipeline-builder automates deployment.
741 

Discussion

Alternatives

Also in Cloud & infraSee all 533 in Development →
Docker MCP gatewayDocker's own CLI plugin: run any server from the Docker MCP Catalog in its own container, behind one connection, with secrets kept out of env vars.Coding · MITTechnical Codebase Discovery & Onboarding PromptA prompt designed to guide a deep technical analysis of a code repository to accelerate developer onboarding. It instructs an AI to analyze the entire codebase and generate a structured Markdown document covering architecture, technology stack, key components, execution and data flows, integrations, testing, security, and build/deployment, serving as a technical reference guide.Coding · CC0-1.0NextflowBuild, run, and debug Nextflow data pipelines and nf-core workflows end to end. Use whenever the user mentions Nextflow, nf-core, .nf files, nextflow.config, DSL2, processes/channels/operators, samplesheets, or wants to run a community pipeline (e.g. nf-core/rnaseq, nf-core/sarek), write or test a module/subworkflow with nf-test, configure executors/containers (Docker, Singularity/Apptainer, Conda, Wave), scale a workflow to HPC/SLURM or cloud (AWS Batch, Google Batch, Azure, Kubernetes), or debug a failed/-resume run. Make sure to use this skill for any reproducible scientific/bioinformatics workflow work even if the user does not say the word "Nextflow", and for authoring nf-core-compliant pipelines, modules, configs, and linting.Science · MITCloud Cost OptimizationOptimize cloud costs across AWS, Azure, GCP, and OCI through resource rightsizing, tagging strategies, reserved instances, and spending analysis. Use when reducing cloud expenses, analyzing infrastructure costs, or implementing cost governance policies.Infrastructure & ops · MIT