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)
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
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
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
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)
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
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
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
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)
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
Implement validation tests:
- Test variable validation rules trigger correctly
- Verify output schemas match documentation
- Check resource dependencies and ordering
- Validate provider authentication patterns
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
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:
{
"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
# 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
Source: williamzujkowski/cognitive-toolworks — distributed by TomeVault.
1---2name: terraform-module-best-practices3description: Usage examples and documentation Use when this capability is needed.4---56## Purpose & When-To-Use78Use when designing reusable Terraform modules that require:9- Input validation and type constraints10- Clear output contracts for module consumers11- Composition patterns for complex infrastructure12- Automated testing with Terratest13- Registry publishing and versioning1415**Trigger conditions:**16- "Create a Terraform module for [infrastructure component]"17- "Add validation to Terraform variables"18- "Write Terratest tests for [module]"19- "Publish module to Terraform Registry"2021## Pre-Checks2223- **Authoritative time**: Set `NOW_ET` to ISO 8601 in `America/New_York` using NIST/time.gov semantics: `2025-10-26T02:31:29-0400`24- **Module type validation**: Ensure `module_type` is one of: network, compute, database, composite25- **Cloud provider support**: Verify provider-specific features and resource availability26- **Terraform version**: Check compatibility with `>= 1.0` for validation features27- **Source freshness**: Validate against current Terraform language spec and provider docs2829## Procedure3031### Tier 1 — Basic Module Structure (T1≤2000 tokens)32331. **Define module layout:**34 ```35 terraform-<name>/36 main.tf # Primary resources37 variables.tf # Input variables with validation38 outputs.tf # Output values with descriptions39 versions.tf # Provider version constraints40 README.md # Usage documentation41 examples/ # Usage examples42 basic/43 tests/ # Terratest tests44 ```45462. **Create `variables.tf` with validation:**47 - Use `type` constraints (string, number, bool, list, map, object, set, tuple, any)48 - Add `validation` blocks for business logic constraints49 - Include `description` and `default` where appropriate50 - Use `sensitive = true` for secrets51523. **Define `outputs.tf` with clear contracts:**53 - Document output purpose and format54 - Use `description` for all outputs55 - Mark sensitive outputs with `sensitive = true`56 - Group related outputs logically57584. **Establish versioning in `versions.tf`:**59 - Pin Terraform version: `required_version = ">= 1.5"`60 - Pin provider versions with `~>` for minor updates61 - Document version rationale in comments6263### Tier 2 — Module Composition & Publishing (T2≤6000 tokens)64651. **Implement composition patterns:**66 - **Child modules**: Extract repeated resources into sub-modules67 - **Dependency injection**: Pass outputs from one module as inputs to another68 - **Data-only modules**: Use `data` sources for discovery/lookups69 - **For-each patterns**: Use `for_each` with maps for dynamic resources70712. **Add comprehensive examples:**72 - **Basic example**: Minimal required inputs73 - **Complete example**: All features enabled74 - **Composition example**: Integration with other modules75 - Each example should be self-contained and runnable76773. **Prepare for Registry publishing:**78 - Follow naming convention: `terraform-<PROVIDER>-<NAME>`79 - Create Git tags matching semantic versioning: `v1.0.0`80 - Write comprehensive `README.md` with inputs/outputs tables81 - Add `LICENSE` file (Apache-2.0, MIT, etc.)82 - Include module registry metadata in repository83844. **Document module contracts:**85 - Input variable requirements and defaults86 - Output value structures and types87 - Provider configuration requirements88 - Resource naming conventions8990### Tier 3 — Testing & CI/CD Integration (T3≤12000 tokens)91921. **Create Terratest tests:**93 - Install Terratest: `go get github.com/gruntwork-io/terratest/modules/terraform`94 - Write test structure:95 - Setup: Configure test inputs96 - Deploy: `terraform.InitAndApply(t, terraformOptions)`97 - Validate: Assert expected outputs and resource state98 - Teardown: `defer terraform.Destroy(t, terraformOptions)`99 - Test different scenarios: minimal, complete, error cases1001012. **Implement validation tests:**102 - Test variable validation rules trigger correctly103 - Verify output schemas match documentation104 - Check resource dependencies and ordering105 - Validate provider authentication patterns1061073. **Set up CI/CD pipeline:**108 - **Lint stage**: `terraform fmt -check`, `terraform validate`109 - **Security stage**: `tfsec`, `checkov`, `terrascan`110 - **Test stage**: Run Terratest suite in isolated environment111 - **Docs stage**: Auto-generate docs with `terraform-docs`112 - **Release stage**: Tag and publish to registry1131144. **Add pre-commit hooks:**115 - Format code: `terraform fmt -recursive`116 - Validate syntax: `terraform validate`117 - Update docs: `terraform-docs markdown table --output-file README.md`118 - Security scan: `tfsec .`119120## Decision Rules121122- If `module_type == "composite"` → require Tier 2 composition patterns123- If `testing_required == true` → require Tier 3 Terratest implementation124- If publishing to registry → require semantic versioning and comprehensive README125- If variable validation fails in tests → emit specific validation error message126- If module has >5 required variables → recommend using `object` type for grouping127- If module creates >10 resources → recommend breaking into child modules128- **Abort conditions:**129 - Missing provider configuration130 - Circular module dependencies131 - Hardcoded secrets or credentials132 - Missing required variable descriptions133134## Output Contract135136**module_structure:**137```json138{139 "root": ["main.tf", "variables.tf", "outputs.tf", "versions.tf", "README.md"],140 "examples": ["basic", "complete"],141 "tests": ["<module_name>_test.go"],142 "docs": ["README.md", "CHANGELOG.md"]143}144```145146**variables_tf:** String containing Terraform variable definitions with:147- `type` constraint148- `description` field149- `validation` blocks where applicable150- `default` values for optional inputs151- `sensitive` flag for secrets152153**outputs_tf:** String containing output definitions with:154- `description` for each output155- `sensitive` flag where needed156- Logical grouping (e.g., network_ids, instance_details)157158**test_code:** Go test file using Terratest framework with:159- Setup and teardown logic160- Terraform init/apply/destroy161- Output validation assertions162- Resource state checks163164**examples:** Directory structure with runnable examples:165- `examples/basic/main.tf` — minimal configuration166- `examples/complete/main.tf` — all features enabled167- Each with `README.md` explaining usage168169## Examples170171**Example (≤30 lines): AWS VPC Module with Validation**172```hcl173# variables.tf174variable "vpc_cidr" {175 type = string176 description = "CIDR block for VPC"177 validation {178 condition = can(cidrhost(var.vpc_cidr, 0))179 error_message = "Must be valid IPv4 CIDR block."180 }181}182183variable "subnet_count" {184 type = number185 description = "Number of subnets to create"186 validation {187 condition = var.subnet_count >= 2 && var.subnet_count <= 16188 error_message = "Subnet count must be between 2 and 16."189 }190}191192# outputs.tf193output "vpc_id" {194 description = "ID of the created VPC"195 value = aws_vpc.main.id196}197198output "subnet_ids" {199 description = "List of subnet IDs"200 value = aws_subnet.main[*].id201}202```203204## Quality Gates205206- **Token budgets**: T1≤2k (basic module structure), T2≤6k (composition + docs), T3≤12k (testing + CI/CD)207- **Example size**: ≤30 lines in `SKILL.md`; full examples in `examples/` directory208- **Variable validation**: All required variables must have `description`; validation rules for business logic209- **Output documentation**: All outputs must have `description` field210- **Testing coverage**: Minimum 3 test scenarios (basic, complete, error handling)211- **Security**: No hardcoded secrets; use `sensitive` flag; scan with `tfsec`212- **Versioning**: Semantic versioning for releases; provider version constraints in `versions.tf`213- **Formatting**: All code must pass `terraform fmt`214- **Documentation**: Auto-generated docs match actual inputs/outputs215216## Resources217218- **Terraform Module Development** (accessed 2025-10-26): https://developer.hashicorp.com/terraform/language/modules/develop219- **Terraform Module Publishing** (accessed 2025-10-26): https://developer.hashicorp.com/terraform/registry/modules/publish220- **Terraform Variable Validation** (accessed 2025-10-26): https://developer.hashicorp.com/terraform/language/values/variables#custom-validation-rules221- **Terratest Documentation** (accessed 2025-10-26): https://terratest.gruntwork.io/docs/222- **Terratest Terraform Module** (accessed 2025-10-26): https://terratest.gruntwork.io/docs/getting-started/quick-start/#example-2-terraform223- **HashiCorp Module Standards** (accessed 2025-10-26): https://developer.hashicorp.com/terraform/language/modules/develop/structure224- **Terraform Testing Best Practices** (accessed 2025-10-26): https://developer.hashicorp.com/terraform/language/modules/testing-experiment225226---227> Source: [williamzujkowski/cognitive-toolworks](https://github.com/williamzujkowski/cognitive-toolworks) — distributed by [TomeVault](https://tomevault.io).228<!-- tomevault:4.0:skill_md:2026-06-16 -->