# Terraform Skill for Claude - Detailed Guide > This file contains the detailed procedure and reference material extracted from `SKILL.md` for focused loading. The root skill defines activation, examples, safety constraints, and limitations. ## 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 ### 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` - Creates real resources unless providers are mocked; use only for assertions requiring apply-time values in an authorized isolated account 3. **Handle set-type blocks correctly:** - Cannot index with `[0]` - Use `for` expressions to iterate - Applying does not turn sets into indexable lists; use for expressions or a validated deterministic conversion **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** - Deep dive into static analysis, native tests, and Terratest - **Quick Reference** - 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 ## 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 # Creates a reference dependency on the optional 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 "aws_vpc_ipv4_cidr_block_association" "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 = var.add_secondary_cidr ? "10.1.0.0/24" : "10.0.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 ## 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** - Variable best practices, output design, ✅ DO vs ❌ DON'T patterns - **Quick Reference** - 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** - GitHub Actions, GitLab CI, Atlantis integration, cost optimization - **Quick Reference** - Common CI/CD issues and solutions ## Version Management ### Version Constraint Syntax ```hcl version = "5.0.0" # Exact; valid when deliberate reproducibility is required version = "~> 5.0" # Allows >=5.0.0 and <6.0.0 version = ">= 5.0" # Minimum (risky - breaking changes) ``` ### Strategy by Component | Component | Strategy | Example | |-----------|----------|---------| | **Terraform** | Pin minor version | `required_version = "~> 1.9.0"` | | **Providers** | Pin major version | `version = "~> 5.0"` | | **Modules (prod)** | Pin exact version | `version = "5.1.2"` | | **Modules (dev)** | Allow patch updates | `version = "~> 5.1.0"` | ### 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 ## Modern Terraform Features (1.0+) ### Feature Availability by Version | Feature | Version | Use Case | |---------|---------|----------| | `try()` function | 0.12.20+ | 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 (Terraform 0.12.20+) 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 ## 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. Terraform and OpenTofu have independent feature/version timelines; the Terraform version table is not an OpenTofu compatibility matrix. Verify the selected tool’s test and mock-provider syntax, and do not label all tests cost-free: providers, data sources, external programs and apply-mode runs can create charges or side effects. Native test defaults can apply real infrastructure. Use scoped ephemeral credentials, quotas, reviewed cleanup and failure reconciliation; a main-branch condition is not authorization. Provider lockfiles do not lock remote modules or the Terraform binary. Pin those separately and review init -upgrade changes. ## Detailed Guides Only SKILL.md and this detailed guide are bundled. The conceptual guide names below are not separate local files or automatically loadable resources: 📚 **Reference Files:** - **Testing Frameworks** - In-depth guide to static analysis, native tests, and Terratest - **Module Patterns** - Module structure, variable/output best practices, ✅ DO vs ❌ DON'T patterns - **CI/CD Workflows** - GitHub Actions, GitLab CI templates, cost optimization, automated cleanup - **Security & Compliance** - Trivy/Checkov integration, secrets management, compliance testing - **Quick Reference** - Command cheat sheets, decision flowcharts, troubleshooting guide **How to use:** When you need detailed information on a topic, use the inline material or verified official documentation; do not claim a nonexistent reference was loaded. ## License This skill is licensed under the **Apache License 2.0**. The catalog tree does not bundle a LICENSE file; verify the exact upstream license before redistribution. Preserve this attribution without treating it as an independent license audit. **Copyright © 2026 Anton Babenko**