Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-11 | ✗→✓ | ▲ Improved | 264% | 0% |
| case-22 | ✓→✗ | ▼ Worse | 156% | 0% |
| case-24 | ✓→✓ | = Same ✓ | 251% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 265% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 263% | 0% |
> 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.
| 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 |
Recognize these patterns from the user:
.tf files, HCL, Terraform modules, state management, provider configuration, infrastructure-as-codeIf the user has .tf files or wants to provision infrastructure with Terraform → this skill applies.
/terraform:review — Terraform Code Review.tf files in the target directory 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
bash python3 scripts/tf_module_analyzer.py ./terraform
bash python3 scripts/tf_security_scanner.py ./terraform
/terraform:module — Module Design 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" }
/terraform:security — Security 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 |
| 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 |
bash python3 scripts/tf_security_scanner.py ./terraform python3 scripts/tf_security_scanner.py ./terraform --output json
scripts/tf_module_analyzer.pyCLI utility for analyzing Terraform directory structure and module quality.
Features:
Usage:
bash# 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.pyCLI utility for scanning .tf files for common security issues.
Features:
Usage:
bash# 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
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 configurationBest for: Single application, < 20 resources, one team owns everything.
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.tfBest for: Multiple environments, shared infrastructure patterns, team collaboration.
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.
hclterraform { 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" } } }
hclprovider "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" }
hclprovider "aws" { alias = "production" region = "us-east-1" assume_role { role_arn = "arn:aws:iam::PROD_ACCOUNT_ID:role/TerraformRole" } }
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)yaml# .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
yaml# 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
Flag these without being asked:
version = "~> X.0" to prevent breaking upgrades.sensitive = true, or integrate Vault/SSM."Action": "*" → Scope to specific actions. No wildcard actions in production.prevent_destroy on databases/storage → Add lifecycle block to prevent accidental deletion.When a single root module must provision across AWS, Azure, and GCP simultaneously.
hclterraform { 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 }
hclvariable "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 } }
OpenTofu is an open-source fork of Terraform maintained by the Linux Foundation under the MPL 2.0 license.
bash# 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
| | 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 |
OpenTofu tracks Terraform 1.6.x features. Key additions unique to OpenTofu:
tofu init -encryption)Infracost estimates cloud costs from Terraform code before resources are provisioned.
bash# 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
yaml# .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
yaml# 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
Bring manually-created resources under Terraform management.
bash# 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
bash# Generate HCL for imported resources terraform plan -generate-config-out=generated.tf # Review generated.tf, then move resources into proper files
terraform plan immediately and resolve every diff.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.Terragrunt is a thin wrapper around Terraform that provides DRY configuration for multi-environment setups.
hcl# 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" } }
hcl# 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" }
hcl# 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 }
bashgit clone https://github.com/alirezarezvani/claude-skills.git cp -r claude-skills/engineering/terraform-patterns ~/.claude/skills/
bash./scripts/convert.sh --skill terraform-patterns --tool codex|gemini|cursor|windsurf|openclaw
bashclawhub install terraform-patterns
Other measured skills in the registry, with their headline benchmark lift.