Terraform Module Best Practices
Design reusable Terraform modules with variable validation, output schemas, module composition, and testing (Terratest).
Best use case
Terraform Module Best Practices is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Design reusable Terraform modules with variable validation, output schemas, module composition, and testing (Terratest).
Teams using Terraform Module Best Practices should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/terraform-module-patterns/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How Terraform Module Best Practices Compares
| Feature / Agent | Terraform Module Best Practices | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Design reusable Terraform modules with variable validation, output schemas, module composition, and testing (Terratest).
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
## Purpose & When-To-Use
Use when designing reusable Terraform modules that require:
- Input validation and type constraints
- Clear output contracts for module consumers
- Composition patterns for complex infrastructure
- Automated testing with Terratest
- Registry publishing and versioning
**Trigger conditions:**
- "Create a Terraform module for [infrastructure component]"
- "Add validation to Terraform variables"
- "Write Terratest tests for [module]"
- "Publish module to Terraform Registry"
## Pre-Checks
- **Authoritative time**: Set `NOW_ET` to ISO 8601 in `America/New_York` using NIST/time.gov semantics: `2025-10-26T02:31:29-0400`
- **Module type validation**: Ensure `module_type` is one of: network, compute, database, composite
- **Cloud provider support**: Verify provider-specific features and resource availability
- **Terraform version**: Check compatibility with `>= 1.0` for validation features
- **Source freshness**: Validate against current Terraform language spec and provider docs
## Procedure
### Tier 1 — Basic Module Structure (T1≤2000 tokens)
1. **Define module layout:**
```
terraform-<name>/
main.tf # Primary resources
variables.tf # Input variables with validation
outputs.tf # Output values with descriptions
versions.tf # Provider version constraints
README.md # Usage documentation
examples/ # Usage examples
basic/
tests/ # Terratest tests
```
2. **Create `variables.tf` with validation:**
- Use `type` constraints (string, number, bool, list, map, object, set, tuple, any)
- Add `validation` blocks for business logic constraints
- Include `description` and `default` where appropriate
- Use `sensitive = true` for secrets
3. **Define `outputs.tf` with clear contracts:**
- Document output purpose and format
- Use `description` for all outputs
- Mark sensitive outputs with `sensitive = true`
- Group related outputs logically
4. **Establish versioning in `versions.tf`:**
- Pin Terraform version: `required_version = ">= 1.5"`
- Pin provider versions with `~>` for minor updates
- Document version rationale in comments
### Tier 2 — Module Composition & Publishing (T2≤6000 tokens)
1. **Implement composition patterns:**
- **Child modules**: Extract repeated resources into sub-modules
- **Dependency injection**: Pass outputs from one module as inputs to another
- **Data-only modules**: Use `data` sources for discovery/lookups
- **For-each patterns**: Use `for_each` with maps for dynamic resources
2. **Add comprehensive examples:**
- **Basic example**: Minimal required inputs
- **Complete example**: All features enabled
- **Composition example**: Integration with other modules
- Each example should be self-contained and runnable
3. **Prepare for Registry publishing:**
- Follow naming convention: `terraform-<PROVIDER>-<NAME>`
- Create Git tags matching semantic versioning: `v1.0.0`
- Write comprehensive `README.md` with inputs/outputs tables
- Add `LICENSE` file (Apache-2.0, MIT, etc.)
- Include module registry metadata in repository
4. **Document module contracts:**
- Input variable requirements and defaults
- Output value structures and types
- Provider configuration requirements
- Resource naming conventions
### Tier 3 — Testing & CI/CD Integration (T3≤12000 tokens)
1. **Create Terratest tests:**
- Install Terratest: `go get github.com/gruntwork-io/terratest/modules/terraform`
- Write test structure:
- Setup: Configure test inputs
- Deploy: `terraform.InitAndApply(t, terraformOptions)`
- Validate: Assert expected outputs and resource state
- Teardown: `defer terraform.Destroy(t, terraformOptions)`
- Test different scenarios: minimal, complete, error cases
2. **Implement validation tests:**
- Test variable validation rules trigger correctly
- Verify output schemas match documentation
- Check resource dependencies and ordering
- Validate provider authentication patterns
3. **Set up CI/CD pipeline:**
- **Lint stage**: `terraform fmt -check`, `terraform validate`
- **Security stage**: `tfsec`, `checkov`, `terrascan`
- **Test stage**: Run Terratest suite in isolated environment
- **Docs stage**: Auto-generate docs with `terraform-docs`
- **Release stage**: Tag and publish to registry
4. **Add pre-commit hooks:**
- Format code: `terraform fmt -recursive`
- Validate syntax: `terraform validate`
- Update docs: `terraform-docs markdown table --output-file README.md`
- Security scan: `tfsec .`
## Decision Rules
- If `module_type == "composite"` → require Tier 2 composition patterns
- If `testing_required == true` → require Tier 3 Terratest implementation
- If publishing to registry → require semantic versioning and comprehensive README
- If variable validation fails in tests → emit specific validation error message
- If module has >5 required variables → recommend using `object` type for grouping
- If module creates >10 resources → recommend breaking into child modules
- **Abort conditions:**
- Missing provider configuration
- Circular module dependencies
- Hardcoded secrets or credentials
- Missing required variable descriptions
## Output Contract
**module_structure:**
```json
{
"root": ["main.tf", "variables.tf", "outputs.tf", "versions.tf", "README.md"],
"examples": ["basic", "complete"],
"tests": ["<module_name>_test.go"],
"docs": ["README.md", "CHANGELOG.md"]
}
```
**variables_tf:** String containing Terraform variable definitions with:
- `type` constraint
- `description` field
- `validation` blocks where applicable
- `default` values for optional inputs
- `sensitive` flag for secrets
**outputs_tf:** String containing output definitions with:
- `description` for each output
- `sensitive` flag where needed
- Logical grouping (e.g., network_ids, instance_details)
**test_code:** Go test file using Terratest framework with:
- Setup and teardown logic
- Terraform init/apply/destroy
- Output validation assertions
- Resource state checks
**examples:** Directory structure with runnable examples:
- `examples/basic/main.tf` — minimal configuration
- `examples/complete/main.tf` — all features enabled
- Each with `README.md` explaining usage
## Examples
**Example (≤30 lines): AWS VPC Module with Validation**
```hcl
# variables.tf
variable "vpc_cidr" {
type = string
description = "CIDR block for VPC"
validation {
condition = can(cidrhost(var.vpc_cidr, 0))
error_message = "Must be valid IPv4 CIDR block."
}
}
variable "subnet_count" {
type = number
description = "Number of subnets to create"
validation {
condition = var.subnet_count >= 2 && var.subnet_count <= 16
error_message = "Subnet count must be between 2 and 16."
}
}
# outputs.tf
output "vpc_id" {
description = "ID of the created VPC"
value = aws_vpc.main.id
}
output "subnet_ids" {
description = "List of subnet IDs"
value = aws_subnet.main[*].id
}
```
## Quality Gates
- **Token budgets**: T1≤2k (basic module structure), T2≤6k (composition + docs), T3≤12k (testing + CI/CD)
- **Example size**: ≤30 lines in `SKILL.md`; full examples in `examples/` directory
- **Variable validation**: All required variables must have `description`; validation rules for business logic
- **Output documentation**: All outputs must have `description` field
- **Testing coverage**: Minimum 3 test scenarios (basic, complete, error handling)
- **Security**: No hardcoded secrets; use `sensitive` flag; scan with `tfsec`
- **Versioning**: Semantic versioning for releases; provider version constraints in `versions.tf`
- **Formatting**: All code must pass `terraform fmt`
- **Documentation**: Auto-generated docs match actual inputs/outputs
## Resources
- **Terraform Module Development** (accessed 2025-10-26): https://developer.hashicorp.com/terraform/language/modules/develop
- **Terraform Module Publishing** (accessed 2025-10-26): https://developer.hashicorp.com/terraform/registry/modules/publish
- **Terraform Variable Validation** (accessed 2025-10-26): https://developer.hashicorp.com/terraform/language/values/variables#custom-validation-rules
- **Terratest Documentation** (accessed 2025-10-26): https://terratest.gruntwork.io/docs/
- **Terratest Terraform Module** (accessed 2025-10-26): https://terratest.gruntwork.io/docs/getting-started/quick-start/#example-2-terraform
- **HashiCorp Module Standards** (accessed 2025-10-26): https://developer.hashicorp.com/terraform/language/modules/develop/structure
- **Terraform Testing Best Practices** (accessed 2025-10-26): https://developer.hashicorp.com/terraform/language/modules/testing-experimentRelated Skills
UX Wireframe Designer
Design user experience wireframes, user flows, and interactive mockups for web and mobile applications using industry-standard notation
TypeScript Tooling Specialist
Generate TypeScript/JavaScript project scaffolding with npm/pnpm/yarn, Jest/Vitest, ESLint/Prettier, and bundling (Vite/Rollup/esbuild).
Python Tooling Specialist
Generate Python project scaffolding with Poetry/pipenv, pytest configuration, type hints (mypy), linting (ruff/black), and packaging (setuptools/flit).
Java Tooling Specialist
Generate Java project scaffolding with Maven/Gradle, JUnit 5, Mockito, Checkstyle/SpotBugs, and packaging (JAR/WAR/native-image).
C# .NET Tooling Specialist
Generate C# .NET project scaffolding with dotnet CLI, xUnit/NUnit, StyleCop analyzers, and packaging (NuGet/Docker).
Unit Testing Framework Generator
Generate unit test scaffolding and test suites for Jest, PyTest, Go testing, JUnit, RSpec with mocking, assertions, and coverage configuration
Testing Strategy Composer
Compose comprehensive testing strategies spanning unit, integration, e2e, and performance tests with optimal coverage.
Load Testing Scenario Designer
Design load testing scenarios using k6, JMeter, Gatling, or Locust with ramp-up patterns, think time modeling, and performance SLI validation.
Integration Testing Designer
Design integration test scenarios with database fixtures, external service mocks, contract testing, and test environment setup for microservices and APIs.
Chaos Engineering Experiment Designer
Design chaos engineering experiments to test system resilience with controlled failure injection, hypothesis formulation, and blast radius control.
Service Level Objective Validator
Validate SLO definitions against actual metrics, generate alerting rules, and design error budget policies with burn rate calculations.
Zero Trust Maturity Assessor
Evaluate zero-trust architecture maturity using CISA ZTMM with identity verification, device trust, micro-segmentation, and continuous monitoring.