Claude Skill

terraform

Terraform and OpenTofu configuration, modules, testing, state management, and HCL review. Use for "terraform module", "terraform test", "infrastructure as code", "IaC", "HCL", "tfvars", "terraform plan", "terraform apply", "OpenTofu", "tftest", or multi-environment patterns.

LLM Mart · 0 points · 9 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download iliaal-whetstone-distillery_generated-skills_terraform-bccd699.zip · 2 KB
Part of iliaal/whetstone — 62 skills

Install

skills CLI npx skills add https://github.com/iliaal/whetstone/tree/master/distillery/generated-skills/terraform
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install iliaal-whetstone@llmmart
Git git clone https://github.com/iliaal/whetstone.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole iliaal/whetstone collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Terraform & OpenTofu

File Organization & Naming

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
  • Lowercase with underscores: web_api, not webAPI or web-api
  • Descriptive nouns excluding resource type: aws_instance.web_api not aws_instance.web_api_instance
  • Singular, not plural
  • this for singleton resources (one of that type per module)
  • Contextual variable prefixes: vpc_cidr_block not cidr

Block Ordering

Resources: 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.

Module Structure

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.

count vs for_each

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.

Testing

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 block
  • expect_failures = [var.name] for negative testing (validate rejection of bad input)
  • mock_provider "aws" { mock_resource "..." { defaults = { ... } } } — plan-mode only, no credentials, fast CI
  • variables {} at file level (all runs) or within a run block (override)
  • Reference prior run outputs: run.setup.vpc_id
  • parallel = true on independent runs with separate state — creates sync point at next sequential run
  • File naming: *_unit_test.tftest.hcl (plan mode) vs *_integration_test.tftest.hcl (apply mode)

Version Pinning

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+).

State & Security

  • Remote backend with locking: S3+DynamoDB, Azure Blob, GCS, or Terraform Cloud. Never local state for shared infrastructure.
  • Encrypt state at rest. Never commit .tfstate, .terraform/, or *.tfplan. Always commit .terraform.lock.hcl.
  • default_tags on provider for consistent resource tagging.
  • Encryption at rest on all storage. Private networking by default — public access is opt-in.
  • Least-privilege security groups. No 0.0.0.0/0 ingress without explicit justification.
  • Never hardcode credentials — use assume_role, OIDC, or secrets managers.
  • Pre-commit: terraform fmt -recursive && terraform validate && trivy config .

Dependency Management

Use 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.

Files (whetstone)
  • manifest.json 1 KB
    {
      "query": "terraform",
      "search_queries": [
        "terraform"
      ],
      "generated": "2026-02-13",
      "token_count": 1376,
      "sources": [
        {
          "id": "wshobson/agents/terraform-module-library",
          "installs": 1556,
          "sha1": "5b8d7dcb6505cde2788f5635110e09684eaca99a"
        },
        {
          "id": "hashicorp/agent-skills/terraform-style-guide",
          "installs": 552,
          "sha1": "e132c6da8852dc3eb0bcab62908e8db19014a70c"
        },
        {
          "id": "hashicorp/agent-skills/terraform-test",
          "installs": 342,
          "sha1": "6ddadda37b788e4a0e7210d981e64d7e2668f136"
        },
        {
          "id": "hashicorp/agent-skills/terraform-stacks",
          "installs": 252,
          "sha1": "37fa0d540f050d3d52115e3967c135ee9e656d18"
        },
        {
          "id": "antonbabenko/terraform-skill/terraform-skill",
          "installs": 243,
          "sha1": "418a692d2368a69981acde5c361ece9c37dc35e7"
        },
        {
          "id": "jeffallan/claude-skills/terraform-engineer",
          "installs": 221,
          "sha1": "cfcc21c735b8bb60f6920c4e462795c3028e45ba"
        }
      ]
    }
    
  • SKILL.md 5 KB
    ---
    name: terraform
    description: >-
      Terraform and OpenTofu configuration, modules, testing, state management, and
      HCL review. Use for "terraform module", "terraform test", "infrastructure as
      code", "IaC", "HCL", "tfvars", "terraform plan", "terraform apply",
      "OpenTofu", "tftest", or multi-environment patterns.
    ---
    
    # Terraform & OpenTofu
    
    ## File Organization & Naming
    
    | 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 |
    
    - Lowercase with underscores: `web_api`, not `webAPI` or `web-api`
    - Descriptive nouns excluding resource type: `aws_instance.web_api` not `aws_instance.web_api_instance`
    - Singular, not plural
    - `this` for singleton resources (one of that type per module)
    - Contextual variable prefixes: `vpc_cidr_block` not `cidr`
    
    ## Block Ordering
    
    **Resources:** `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`.
    
    ## Module Structure
    
    | 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.
    
    ## count vs for_each
    
    | 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.
    
    ## Testing
    
    | 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 block
    - `expect_failures = [var.name]` for negative testing (validate rejection of bad input)
    - `mock_provider "aws" { mock_resource "..." { defaults = { ... } } }` — plan-mode only, no credentials, fast CI
    - `variables {}` at file level (all runs) or within a `run` block (override)
    - Reference prior run outputs: `run.setup.vpc_id`
    - `parallel = true` on independent runs with separate state — creates sync point at next sequential run
    - File naming: `*_unit_test.tftest.hcl` (plan mode) vs `*_integration_test.tftest.hcl` (apply mode)
    
    ## Version Pinning
    
    | 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+).
    
    ## State & Security
    
    - Remote backend with locking: S3+DynamoDB, Azure Blob, GCS, or Terraform Cloud. Never local state for shared infrastructure.
    - Encrypt state at rest. Never commit `.tfstate`, `.terraform/`, or `*.tfplan`. Always commit `.terraform.lock.hcl`.
    - `default_tags` on provider for consistent resource tagging.
    - Encryption at rest on all storage. Private networking by default — public access is opt-in.
    - Least-privilege security groups. No `0.0.0.0/0` ingress without explicit justification.
    - Never hardcode credentials — use assume_role, OIDC, or secrets managers.
    - Pre-commit: `terraform fmt -recursive && terraform validate && trivy config .`
    
    ## Dependency Management
    
    Use `locals` with `try()` to control deletion ordering without explicit `depends_on`:
    
    ```hcl
    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.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related