From terraform-iac
Pre-publish checklist and design guide for Terraform/OpenTofu modules — covers interface design, input validation, output documentation, for_each patterns, version pinning, and testability.
How this skill is triggered — by the user, by Claude, or both
Slash command
/terraform-iac:module-design-checklistThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Before publishing a new module or accepting a module PR — to verify the module meets the contract standard that allows consumers to upgrade without surprises and compose modules safely.
Before publishing a new module or accepting a module PR — to verify the module meets the contract standard that allows consumers to upgrade without surprises and compose modules safely.
s3-private-bucket, not data-stuff). If the module deploys three conceptually distinct things, split it.variable block declares a type. Avoid bare any; use object({}) for structured inputs.variable "retention_days" {
type = number
description = "Log retention in days. Must be one of: 1, 3, 7, 14, 30, 60, 90."
default = 30
validation {
condition = contains([1, 3, 7, 14, 30, 60, 90], var.retention_days)
error_message = "retention_days must be one of: 1, 3, 7, 14, 30, 60, 90."
}
}
validation block with a helpful error_message surfaces errors in terraform plan, not after 3 minutes of apply.can(cidrhost(var.vpc_cidr, 0))length(var.name) > 0can(regex("^[a-z0-9-]+$", var.bucket_name))contains(["dev", "staging", "prod"], var.environment)name_prefix or name variable — consumers should control naming for their environment; hard-coded names break reuse.tags variable — always map(string), merged with any module-internal required tags:
variable "tags" {
type = map(string)
default = {}
}
# In resources:
tags = merge(var.tags, { ManagedBy = "terraform", Module = "s3-private-bucket" })
for_each, not count, for collections — count creates indexed resources; inserting or removing an item from the middle changes every subsequent index and Terraform recreates them. for_each keys on a stable identifier.# Wrong
resource "aws_iam_role" "worker" {
count = length(var.worker_names)
name = var.worker_names[count.index]
}
# Right
resource "aws_iam_role" "worker" {
for_each = toset(var.worker_names)
name = each.key
}
sensitive = true and document that it appears in state in plaintext.description on every output block.required_providers:
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
.terraform.lock.hcl — ensures reproducible runs across contributors and CI.main.examples/ subdirectory with a complete, runnable usage of the module.terraform validate — run in CI.terraform validate — catches syntax and provider schema errors; zero cost; run in CI.terraform plan against the example — catches provider API changes and broken references.# Run before opening a module PR
cd modules/<module-name>
terraform fmt -recursive -check
terraform validate
terraform-docs markdown . > README.md
cd examples/complete && terraform init && terraform validate
examples/ directory — consumers have to read the source to use it.any typed variables — callers get no editor assistance and errors are cryptic.count for resource sets that might have items added/removed — causes surprising resource replacement.sensitive = true — the value is printed in plan/apply output and CI logs.../../agents/terraform-module-engineer.md — composable module authoring ownership../../agents/iac-architect.md — module decomposition and boundariesnpx claudepluginhub mcorbett51090/ravenclaude --plugin terraform-iacGuides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Enforces test-driven development: write failing test first, then minimal code to pass. Use when implementing features or bugfixes.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.