Install
openclaw skills install compound-eng-terraformTerraform and OpenTofu configuration, modules, testing, state management, and HCL review. Use when working with Terraform, OpenTofu, HCL, tfvars, tftest, state migration, or IaC patterns.
openclaw skills install compound-eng-terraform| File | Purpose |
|---|---|
terraform.tf | Terraform + provider version requirements |
providers.tf | Provider configurations |
main.tf | Primary resources and data sources |
variables.tf | Input variables (alphabetical) |
outputs.tf | Output values (alphabetical) |
locals.tf | Local values |
web_api, not webAPI or web-apiaws_instance.web_api not aws_instance.web_api_instancethis for singleton resources (one of that type per module)vpc_cidr_block not cidrResources: count/for_each (blank line after) → arguments → nested blocks → tags → depends_on → lifecycle (last)
Variables: description → type → default → validation → nullable
Every variable needs type + description. Every output needs description. Mark secrets sensitive = true.
| Type | Scope | Example |
|---|---|---|
| Resource Module | Single logical group | VPC + subnets, SG + rules |
| Infrastructure Module | Collection of resource modules | Networking + compute for one region |
| Composition | Complete infrastructure | Spans regions/accounts |
module-name/
├── main.tf, variables.tf, outputs.tf, versions.tf
├── examples/
│ ├── minimal/
│ └── complete/
└── tests/
└── defaults.tftest.hcl
Keep modules small (single responsibility). examples/ double as documentation and integration test fixtures. Semantic versioning for all published modules.
| Scenario | Use |
|---|---|
| Boolean toggle (create or skip) | count = condition ? 1 : 0 |
| Named/keyed items that may reorder | for_each = toset(list) or map |
| Fixed identical replicas | count = N |
Default to for_each -- removing a middle item from a count list recreates all subsequent resources. Use count only for boolean conditionals or truly identical replicas.
| Situation | Approach |
|---|---|
| Quick validation | terraform fmt -check && terraform validate |
| Pre-commit | + tflint + trivy config . / checkov -d . |
| Logic validation (1.6+) | Native terraform test with command = plan |
| Cost-free unit tests (1.7+) | Native tests + mock_provider |
| Real infra validation | Native tests with command = apply, or Terratest (Go) |
Native test essentials (.tftest.hcl in tests/):
command = plan for fast unit tests; command = apply for integration (default)assert { condition = expr; error_message = "..." } -- multiple per run blockexpect_failures = [var.name] for negative testing (validate rejection of bad input)mock_provider "aws" { mock_resource "..." { defaults = { ... } } } -- plan-mode only, no credentials, fast CIvariables {} at file level (all runs) or within a run block (override)run.setup.vpc_idparallel = true on independent runs with separate state -- creates sync point at next sequential runstate_key = "name" required for parallel = true runs with independent state*_unit_test.tftest.hcl (plan mode) vs *_integration_test.tftest.hcl (apply mode)| Component | Strategy | Example |
|---|---|---|
| Terraform | Pin minor | required_version = "~> 1.9" |
| Providers | Pin major | version = "~> 5.0" |
| Modules (prod) | Pin exact | version = "5.1.2" |
| Modules (dev) | Allow patch | version = "~> 5.1" |
Key modern features: moved blocks (1.1+), optional() with defaults (1.3+), native testing (1.6+), mock providers (1.7+), cross-variable validation (1.9+), write-only arguments (1.11+).
Stacks (HCP, preview): orchestrates multiple configs as a single deployment unit -- evaluate for multi-environment patterns.
.tfstate, .terraform/, or *.tfplan. Always commit .terraform.lock.hcl.default_tags on provider for consistent resource tagging.0.0.0.0/0 ingress without explicit justification.terraform fmt -recursive && terraform validate && trivy config .moved { from = old; to = new } for refactoring resource names/modules without destroy-recreate. Remove block after apply.terraform force-unlock <ID> -- only after confirming no other operation runningterraform plan -refresh-only to detect, terraform apply -refresh-only to acceptterraform apply -replace=ADDR (not deprecated terraform taint)import blocks (1.5+) for declarative import, or terraform import ADDR IDUse locals with try() to control deletion ordering without explicit depends_on:
locals {
vpc_id = try(aws_vpc_ipv4_cidr_block_association.this[0].vpc_id, aws_vpc.this.id, "")
}
This forces Terraform to destroy subnets before CIDR associations -- prevents deletion errors.
cidrsubnet(var.vpc_cidr, 8, count.index) for calculated subnet CIDRs -- never hardcode subnetsprovider "aws" { alias = "eu_west_1" } + providers = { aws = aws.eu_west_1 } in module blocksRun before declaring done:
terraform fmt -check && terraform validate && tflint && trivy config .
All commands must pass with zero errors.