Skill: jimmy974/terraform-skill (version 1.0.0)

--- name: terraform-skill description: Use when working with Terraform or OpenTofu - creating modules, writing tests (native test framework, Terratest), setting up CI/CD pipelines, reviewing configurations, choosing between testing approaches, debugging state issues, implementing security scanning (trivy, checkov), or making infrastructure-as-code architecture decisions license: Apache-2.0 metadata: author: Anton Babenko version: 1.6.0 --- # Terraform Skill for Claude Comprehensive Terraform and OpenTofu guidance covering testing, modules, CI/CD, and production patterns. Based on terraform-best-practices.com and enterprise experience. ## When to Use This Skill **Activate this skill when:** - Creating new Terraform or OpenTofu configurations or modules - Setting up testing infrastructure for IaC code - Deciding between testing approaches (validate, plan, frameworks) - Structuring multi-environment deployments - Implementing CI/CD for infrastructure-as-code - Reviewing or refactoring existing Terraform/OpenTofu projects - Choosing between module patterns or state management approaches **Don't use this skill for:** - Basic Terraform/OpenTofu syntax questions (Claude knows this) - Provider-specific API reference (link to docs instead) - Cloud platform questions unrelated to Terraform/OpenTofu ## Core Principles ### 1. Code Structure Philosophy **Module Hierarchy:** | Type | When to Use | Scope | |------|-------------|-------| | **Resource Module** | Single logical group of connected resources | VPC + subnets, Security group + rules | | **Infrastructure Module** | Collection of resource modules for a purpose | Multiple resource modules in one region/account | | **Composition** | Complete infrastructure | Spans multiple regions/accounts | **Hierarchy:** Resource → Resource Module → Infrastructure Module → Composition **Directory Structure:** ``` environments/ # Environment-specific configurations ├── prod/ ├── staging/ └── dev/ modules/ # Reusable modules ├── networking/ ├── compute/ └── data/ examples/ # Module usage examples (also serve as tests) ├── complete/ └── minimal/ ``` **Key principle from terraform-best-practices.com:** - Separate **environments** (prod, staging) from **modules** (reusable components) - Use **examples/** as both documentation and integration test fixtures - Keep modules small and focused (single responsibility) **For detailed module architecture, see:** [Code Patterns: Module Types & Hierarchy](references/code-patterns.md) ### 2. Naming Conventions **Resources:** ```hcl # Good: Descriptive, contextual resource "aws_instance" "web_server" { } resource "aws_s3_bucket" "application_logs" { } # Good: "this" for singleton resources (only one of that type) resource "aws_vpc" "this" { } resource "aws_security_group" "this" { } # Avoid: Generic names for non-singletons resource "aws_instance" "main" { } resource "aws_s3_bucket" "bucket" { } ``` **Singleton Resources:** Use `"this"` when your module creates only one resource of that type: ✅ DO: ```hcl resource "aws_vpc" "this" {} # Module creates one VPC resource "aws_security_group" "this" {} # Module creates one SG ``` ❌ DON'T use "this" for multiple resources: ```hcl resource "aws_subnet" "this" {} # If creating multiple subnets ``` Use descriptive names when creating multiple resources of the same type. **Variables:** ```hcl # Prefix with context when needed var.vpc_cidr_block # Not just "cidr" var.database_instance_class # Not just "instance_class" ``` **Files:** - `main.tf` - Primary resources - `variables.tf` - Input variables - `outputs.tf` - Output values - `versions.tf` - Provider versions - `data.tf` - Data sources (optional) ## Testing Strategy Framework ### Decision Matrix: Which Testing Approach? | Your Situation | Recommended Approach | Tools | Cost | |----------------|---------------------|-------|------| | **Quick syntax check** | Static analysis | `terraform validate`, `fmt` | Free | | **Pre-commit validation** | Static + lint | `validate`, `tflint`, `trivy`, `checkov` | Free | | **Terraform 1.6+, simple logic** | Native test framework | Built-in `terraform test` | Free-Low | | **Pre-1.6, or Go expertise** | Integration testing | Terratest | Low-Med | | **Security/compliance focus** | Policy as code | OPA, Sentinel | Free | | **Cost-sensitive workflow** | Mock providers (1.7+) | Native tests + mocking | Free | | **Multi-cloud, complex** | Full integration | Terratest + real infra | Med-High | ### Testing Pyramid for Infrastructure ``` /\ / \ End-to-End Tests (Expensive) /____\ - Full environment deployment / \ - Production-like setup /________\ / \ Integration Tests (Moderate) /____________\ - Module testing in isolation / \ - Real resources in test account /________________\ Static Analysis (Cheap) - validate, fmt, lint - Security scanning ``` ### Native Test Best Practices (1.6+) **Before generating test code:** 1. **Validate schemas with Terraform MCP:** ``` Search provider docs → Get resource schema → Identify block types ``` 2. **Choose correct command mode:** - `command = plan` - Fast, for input validation - `command = apply` - Required for computed values and set-type blocks 3. **Handle set-type blocks correctly:** - Cannot index with `[0]` - Use `for` expressions to iterate - Or use `command = apply` to materialize **Common patterns:** - S3 encryption rules: **set** (use for expressions) - Lifecycle transitions: **set** (use for expressions) - IAM policy statements: **set** (use for expressions) **For detailed testing guides, see:** - **[Testing Frameworks Guide](references/testing-frameworks.md)** - Deep dive into static analysis, native tests, and Terratest - **[Quick Reference](references/quick-reference.md#testing-approach-selection)** - Decision flowchart and command cheat sheet ## Code Structure Standards ### Resource Block Ordering **Strict ordering for consistency:** 1. `count` or `for_each` FIRST (blank line after) 2. Other arguments 3. `tags` as last real argument 4. `depends_on` after tags (if needed) 5. `lifecycle` at the very end (if needed) ```hcl # ✅ GOOD - Correct ordering resource "aws_nat_gateway" "this" { count = var.create_nat_gateway ? 1 : 0 allocation_id = aws_eip.this[0].id subnet_id = aws_subnet.public[0].id tags = { Name = "${var.name}-nat" } depends_on = [aws_internet_gateway.this] lifecycle { create_before_destroy = true } } ``` ### Variable Block Ordering 1. `description` (ALWAYS required) 2. `type` 3. `default` 4. `validation` 5. `nullable` (when setting to false) ```hcl variable "environment" { description = "Environment name for resource tagging" type = string default = "dev" validation { condition = contains(["dev", "staging", "prod"], var.environment) error_message = "Environment must be one of: dev, staging, prod." } nullable = false } ``` **For complete structure guidelines, see:** [Code Patterns: Block Ordering & Structure](references/code-patterns.md#block-ordering--structure) ## Count vs For_Each: When to Use Each ### Quick Decision Guide | Scenario | Use | Why | |----------|-----|-----| | Boolean condition (create or don't) | `count = condition ? 1 : 0` | Simple on/off toggle | | Simple numeric replication | `count = 3` | Fixed number of identical resources | | Items may be reordered/removed | `for_each = toset(list)` | Stable resource addresses | | Reference by key | `for_each = map` | Named access to resources | | Multiple named resources | `for_each` | Better maintainability | ### Common Patterns **Boolean conditions:** ```hcl # ✅ GOOD - Boolean condition resource "aws_nat_gateway" "this" { count = var.create_nat_gateway ? 1 : 0 # ... } ``` **Stable addressing with for_each:** ```hcl # ✅ GOOD - Removing "us-east-1b" only affects that subnet resource "aws_subnet" "private" { for_each = toset(var.availability_zones) availability_zone = each.key # ... } # ❌ BAD - Removing middle AZ recreates all subsequent subnets resource "aws_subnet" "private" { count = length(var.availability_zones) availability_zone = var.availability_zones[count.index] # ... } ``` **For migration guides and detailed examples, see:** [Code Patterns: Count vs For_Each](references/code-patterns.md#count-vs-for_each-deep-dive) ## Locals for Dependency Management **Use locals to ensure correct resource deletion order:** ```hcl # Problem: Subnets might be deleted after CIDR blocks, causing errors # Solution: Use try() in locals to hint deletion order locals { # References secondary CIDR first, falling back to VPC # Forces Terraform to delete subnets before CIDR association vpc_id = try( aws_vpc_ipv4_cidr_block_association.this[0].vpc_id, aws_vpc.this.id, "" ) } resource "aws_vpc" "this" { cidr_block = "10.0.0.0/16" } resource [REDACTED_SECRET] "this" { count = var.add_secondary_cidr ? 1 : 0 vpc_id = aws_vpc.this.id cidr_block = "10.1.0.0/16" } resource "aws_subnet" "public" { vpc_id = local.vpc_id # Uses local, not direct reference cidr_block = "10.1.0.0/24" } ``` **Why this matters:** - Prevents deletion errors when destroying infrastructure - Ensures correct dependency order without explicit `depends_on` - Particularly useful for VPC configurations with secondary CIDR blocks **For detailed examples, see:** [Code Patterns: Locals for Dependency Management](references/code-patterns.md#locals-for-dependency-management) ## Module Development ### Standard Module Structure ``` my-module/ ├── README.md # Usage documentation ├── main.tf # Primary resources ├── variables.tf # Input variables with descriptions ├── outputs.tf # Output values ├── versions.tf # Provider version constraints ├── examples/ │ ├── minimal/ # Minimal working example │ └── complete/ # Full-featured example └── tests/ # Test files └── module_test.tftest.hcl # Or .go ``` ### Best Practices Summary **Variables:** - ✅ Always include `description` - ✅ Use explicit `type` constraints - ✅ Provide sensible `default` values where appropriate - ✅ Add `validation` blocks for complex constraints - ✅ Use `sensitive = true` for secrets **Outputs:** - ✅ Always include `description` - ✅ Mark sensitive outputs with `sensitive = true` - ✅ Consider returning objects for related values - ✅ Document what consumers should do with each output **For detailed module patterns, see:** - **[Module Patterns Guide](references/module-patterns.md)** - Variable best practices, output design, ✅ DO vs ❌ DON'T patterns - **[Quick Reference](references/quick-reference.md#common-patterns)** - Resource naming, variable naming, file organization ## CI/CD Integration ### Recommended Workflow Stages 1. **Validate** - Format check + syntax validation + linting 2. **Test** - Run automated tests (native or Terratest) 3. **Plan** - Generate and review execution plan 4. **Apply** - Execute changes (with approvals for production) ### Cost Optimization Strategy 1. **Use mocking for PR validation** (free) 2. **Run integration tests only on main branch** (controlled cost) 3. **Implement auto-cleanup** (prevent orphaned resources) 4. **Tag all test resources** (track spending) **For complete CI/CD templates, see:** - **[CI/CD Workflows Guide](references/ci-cd-workflows.md)** - GitHub Actions, GitLab CI, Atlantis integration, cost optimization - **[Quick Reference](references/quick-reference.md#troubleshooting-guide)** - Common CI/CD issues and solutions ## Security & Compliance ### Essential Security Checks ```bash # Static security scanning trivy config . checkov -d . ``` ### Common Issues to Avoid ❌ **Don't:** - Store secrets in variables - Use default VPC - Skip encryption - Open security groups to 0.0.0.0/0 ✅ **Do:** - Use AWS Secrets Manager / Parameter Store - Create dedicated VPCs - Enable encryption at rest - Use least-privilege security groups **For detailed security guidance, see:** - **[Security & Compliance Guide](references/security-compliance.md)** - Trivy/Checkov integration, secrets management, state file security, compliance testing ## Version Management ### Version Constraint Syntax ```hcl version = "5.0.0" # Exact (avoid - inflexible) version = "~> 5.0" # Recommended: 5.0.x only version = ">= 5.0" # Minimum (risky - breaking changes) ``` ### Strategy by Component | Component | Strategy | Example | |-----------|----------|---------| | **Terraform** | Pin minor version | `required_version = "~> 1.9"` | | **Providers** | Pin major version | `version = "~> 5.0"` | | **Modules (prod)** | Pin exact version | `version = "5.1.2"` | | **Modules (dev)** | Allow patch updates | `version = "~> 5.1"` | ### Update Workflow ```bash # Lock versions initially terraform init # Creates .terraform.lock.hcl # Update to latest within constraints terraform init -upgrade # Updates providers # Review and test terraform plan ``` **For detailed version management, see:** [Code Patterns: Version Management](references/code-patterns.md#version-management) ## Modern Terraform Features (1.0+) ### Feature Availability by Version | Feature | Version | Use Case | |---------|---------|----------| | `try()` function | 0.13+ | Safe fallbacks, replaces `element(concat())` | | `nullable = false` | 1.1+ | Prevent null values in variables | | `moved` blocks | 1.1+ | Refactor without destroy/recreate | | `optional()` with defaults | 1.3+ | Optional object attributes | | Native testing | 1.6+ | Built-in test framework | | Mock providers | 1.7+ | Cost-free unit testing | | Provider functions | 1.8+ | Provider-specific data transformation | | Cross-variable validation | 1.9+ | Validate relationships between variables | | Write-only arguments | 1.11+ | Secrets never stored in state | ### Quick Examples ```hcl # try() - Safe fallbacks (0.13+) output "sg_id" { value = try(aws_security_group.this[0].id, "") } # optional() - Optional attributes with defaults (1.3+) variable "config" { type = object({ name = string timeout = optional(number, 300) # Default: 300 }) } # Cross-variable validation (1.9+) variable "environment" { type = string } variable "backup_days" { type = number validation { condition = var.environment == "prod" ? var.backup_days >= 7 : true error_message = "Production requires backup_days >= 7" } } ``` **For complete patterns and examples, see:** [Code Patterns: Modern Terraform Features](references/code-patterns.md#modern-terraform-features-10) ## Version-Specific Guidance ### Terraform 1.0-1.5 - Use Terratest for testing - No native testing framework available - Focus on static analysis and plan validation ### Terraform 1.6+ / OpenTofu 1.6+ - **New:** Native `terraform test` / `tofu test` command - Consider migrating from external frameworks for simple tests - Keep Terratest only for complex integration tests ### Terraform 1.7+ / OpenTofu 1.7+ - **New:** Mock providers for unit testing - Reduce cost by mocking external dependencies - Use real integration tests for final validation ### Terraform vs OpenTofu Both are fully supported by this skill. For licensing, governance, and feature comparison, see [Quick Reference: Terraform vs OpenTofu](references/quick-reference.md#terraform-vs-opentofu-comparison). ## Detailed Guides This skill uses **progressive disclosure** - essential information is in this main file, detailed guides are available when needed: 📚 **Reference Files:** - **[Testing Frameworks](references/testing-frameworks.md)** - In-depth guide to static analysis, native tests, and Terratest - **[Module Patterns](references/module-patterns.md)** - Module structure, variable/output best practices, ✅ DO vs ❌ DON'T patterns - **[CI/CD Workflows](references/ci-cd-workflows.md)** - GitHub Actions, GitLab CI templates, cost optimization, automated cleanup - **[Security & Compliance](references/security-compliance.md)** - Trivy/Checkov integration, secrets management, compliance testing - **[Quick Reference](references/quick-reference.md)** - Command cheat sheets, decision flowcharts, troubleshooting guide **How to use:** When you need detailed information on a topic, reference the appropriate guide. Claude will load it on demand to provide comprehensive guidance. ## License This skill is licensed under the **Apache License 2.0**. See the LICENSE file for full terms. **Copyright © 2026 Anton Babenko**


Security scan verdicts — ClawScan: clean | static analysis: suspicious | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)