All skills
jeffallan avatar

/terraform-engineer

@d0e7f4e
by jeffallanjeffallan/claude-skills12k stars
1,127

Use when implementing infrastructure as code with Terraform across AWS, Azure, or GCP. Invoke for module development (create reusable modules, manage module versioning), state management (migrate backends, import existing resources, resolve state conflicts), provider configuration, multi-environment workflows, and infrastructure testing.

Use this Skill: https://skilld.dev/gh/jeffallan/claude-skills/terraform-engineer

This session only. Nothing lands on disk.

referencestesting.md

≈2.5k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Terraform Testing Strategies

Terraform Plan Validation

Basic Plan Workflow

# Initialize and validate syntax
terraform init
terraform fmt -check
terraform validate

# Plan with output
terraform plan -out=tfplan

# Show plan in JSON for automated review
terraform show -json tfplan | jq .

# Apply specific plan
terraform apply tfplan

Plan with Variable Files

# Plan with specific tfvars
terraform plan -var-file="production.tfvars"

# Plan with inline variables
terraform plan -var="instance_count=5"

# Plan with multiple var files
terraform plan \
  -var-file="common.tfvars" \
  -var-file="production.tfvars"

Plan Analysis

# Resource targeting for specific resources
terraform plan -target=aws_vpc.main

# Refresh only (check drift)
terraform plan -refresh-only

# Destroy plan
terraform plan -destroy

# Save plan output
terraform plan -out=tfplan 2>&1 | tee plan-output.txt

Terraform Test (1.6+)

Test File Structure

tests/
├── unit/
│   ├── vpc_test.tftest.hcl
│   └── security_group_test.tftest.hcl
└── integration/
    └── complete_test.tftest.hcl

Basic Test

# tests/vpc_test.tftest.hcl
run "validate_vpc_cidr" {
  command = plan

  variables {
    cidr_block = "10.0.0.0/16"
    name       = "test-vpc"
  }

  assert {
    condition     = aws_vpc.main.cidr_block == "10.0.0.0/16"
    error_message = "VPC CIDR block did not match expected value"
  }

  assert {
    condition     = aws_vpc.main.enable_dns_hostnames == true
    error_message = "DNS hostnames should be enabled"
  }
}

run "validate_tags" {
  command = plan

  variables {
    cidr_block = "10.0.0.0/16"
    name       = "test-vpc"
    tags = {
      Environment = "test"
    }
  }

  assert {
    condition     = aws_vpc.main.tags["Environment"] == "test"
    error_message = "Environment tag not set correctly"
  }
}

Integration Test

# tests/integration/complete_test.tftest.hcl
run "create_full_stack" {
  command = apply

  variables {
    cidr_block = "10.0.0.0/16"
    name       = "integration-test"

    private_subnets = {
      app = { cidr_block = "10.0.1.0/24", az = "us-east-1a" }
    }
  }

  assert {
    condition     = length(aws_subnet.private) == 1
    error_message = "Should create exactly one private subnet"
  }

  assert {
    condition     = output.vpc_id != ""
    error_message = "VPC ID should not be empty"
  }
}

Run Tests

# Run all tests
terraform test

# Run specific test file
terraform test tests/vpc_test.tftest.hcl

# Verbose output
terraform test -verbose

# Keep test resources (for debugging)
terraform test -no-cleanup

Terratest (Go-based Testing)

Test Structure

tests/
├── go.mod
├── go.sum
└── vpc_test.go

go.mod

module github.com/example/terraform-modules/tests

go 1.21

require (
    github.com/gruntwork-io/terratest v0.45.0
    github.com/stretchr/testify v1.8.4
)

Basic Terratest

// tests/vpc_test.go
package test

import (
    "testing"

    "github.com/gruntwork-io/terratest/modules/terraform"
    "github.com/stretchr/testify/assert"
)

func TestVPCCreation(t *testing.T) {
    t.Parallel()

    terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
        TerraformDir: "../examples/complete",

        Vars: map[string]interface{}{
            "name":       "test-vpc",
            "cidr_block": "10.0.0.0/16",
        },

        EnvVars: map[string]string{
            "AWS_DEFAULT_REGION": "us-east-1",
        },
    })

    defer terraform.Destroy(t, terraformOptions)

    terraform.InitAndApply(t, terraformOptions)

    vpcID := terraform.Output(t, terraformOptions, "vpc_id")
    assert.NotEmpty(t, vpcID)

    vpcCIDR := terraform.Output(t, terraformOptions, "vpc_cidr_block")
    assert.Equal(t, "10.0.0.0/16", vpcCIDR)
}

Advanced Terratest with AWS SDK

package test

import (
    "testing"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/service/ec2"
    "github.com/gruntwork-io/terratest/modules/terraform"
    aws_helper "github.com/gruntwork-io/terratest/modules/aws"
    "github.com/stretchr/testify/assert"
)

func TestVPCConfiguration(t *testing.T) {
    t.Parallel()

    awsRegion := "us-east-1"

    terraformOptions := &terraform.Options{
        TerraformDir: "../examples/complete",
        Vars: map[string]interface{}{
            "name":       "test-vpc",
            "cidr_block": "10.0.0.0/16",
        },
    }

    defer terraform.Destroy(t, terraformOptions)
    terraform.InitAndApply(t, terraformOptions)

    vpcID := terraform.Output(t, terraformOptions, "vpc_id")

    // Verify VPC configuration using AWS SDK
    vpc := aws_helper.GetVpcById(t, vpcID, awsRegion)
    assert.Equal(t, "10.0.0.0/16", *vpc.CidrBlock)
    assert.True(t, *vpc.EnableDnsSupport)
    assert.True(t, *vpc.EnableDnsHostnames)

    // Verify tags
    tags := convertEC2TagsToMap(vpc.Tags)
    assert.Equal(t, "test-vpc", tags["Name"])
}

func convertEC2TagsToMap(tags []*ec2.Tag) map[string]string {
    result := make(map[string]string)
    for _, tag := range tags {
        result[*tag.Key] = *tag.Value
    }
    return result
}

Run Terratest

cd tests
go mod download
go test -v -timeout 30m

Policy as Code - OPA/Sentinel

Open Policy Agent (OPA)

policy.rego

package terraform.analysis

import input as tfplan

# Deny if resources are not tagged
deny[msg] {
    r := tfplan.resource_changes[_]
    r.change.actions[_] == "create"
    not r.change.after.tags.Environment
    msg := sprintf("Resource %s is missing Environment tag", [r.address])
}

# Require encryption for S3 buckets
deny[msg] {
    r := tfplan.resource_changes[_]
    r.type == "aws_s3_bucket"
    r.change.actions[_] == "create"
    not r.change.after.server_side_encryption_configuration
    msg := sprintf("S3 bucket %s must have encryption enabled", [r.address])
}

# Ensure VPC flow logs are enabled
deny[msg] {
    r := tfplan.resource_changes[_]
    r.type == "aws_vpc"
    r.change.actions[_] == "create"
    vpc_id := r.change.after.id
    not has_flow_log(vpc_id)
    msg := sprintf("VPC %s must have flow logs enabled", [r.address])
}

has_flow_log(vpc_id) {
    r := tfplan.resource_changes[_]
    r.type == "aws_flow_log"
    r.change.after.vpc_id == vpc_id
}

Run OPA Policy

# Generate plan in JSON
terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json

# Run OPA policy check
opa eval -i tfplan.json -d policy.rego "data.terraform.analysis.deny"

Conftest (OPA wrapper for testing)

# Install conftest
brew install conftest

# Test plan against policies
conftest test tfplan.json

# With specific namespace
conftest test tfplan.json --namespace terraform.analysis

TFLint

Installation and Configuration

# Install tflint
brew install tflint

# Initialize tflint plugins
tflint --init

.tflint.hcl

plugin "terraform" {
  enabled = true
  preset  = "recommended"
}

plugin "aws" {
  enabled = true
  version = "0.27.0"
  source  = "github.com/terraform-linters/tflint-ruleset-aws"
}

rule "terraform_naming_convention" {
  enabled = true

  format = "snake_case"
}

rule "terraform_required_version" {
  enabled = true
}

rule "terraform_required_providers" {
  enabled = true
}

rule "aws_instance_invalid_type" {
  enabled = true
}

rule "aws_s3_bucket_encryption" {
  enabled = true
}

Run TFLint

# Run linter
tflint

# With specific config
tflint --config=.tflint.hcl

# Recursive (all subdirectories)
tflint --recursive

# Output format
tflint --format=json

Pre-commit Hooks

.pre-commit-config.yaml

repos:
  - repo: https://github.com/antonbabenko/pre-commit-terraform
    rev: v1.83.6
    hooks:
      - id: terraform_fmt
      - id: terraform_validate
      - id: terraform_tflint
        args:
          - --args=--config=__GIT_WORKING_DIR__/.tflint.hcl
      - id: terraform_docs
        args:
          - --hook-config=--path-to-file=README.md
          - --hook-config=--add-to-existing-file=true
      - id: terraform_checkov
        args:
          - --args=--quiet
          - --args=--skip-check CKV_AWS_*

Setup

# Install pre-commit
pip install pre-commit

# Install hooks
pre-commit install

# Run manually
pre-commit run -a

CI/CD Pipeline Testing

GitHub Actions Example

name: Terraform Test

on: [pull_request]

jobs:
  terraform-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: 1.6.0

      - name: Terraform Format
        run: terraform fmt -check -recursive

      - name: Terraform Init
        run: terraform init

      - name: Terraform Validate
        run: terraform validate

      - name: TFLint
        uses: terraform-linters/setup-tflint@v3
        with:
          tflint_version: latest

      - name: Run TFLint
        run: tflint --recursive

      - name: Terraform Test
        run: terraform test

      - name: Checkov
        uses: bridgecrewio/checkov-action@master
        with:
          directory: .
          framework: terraform

Best Practices

  • Run terraform validate before every commit
  • Use terraform test for unit and integration tests
  • Implement policy as code for security compliance
  • Run TFLint in CI/CD pipelines
  • Use pre-commit hooks for automated checks
  • Test modules with Terratest for critical infrastructure
  • Always review plan output before apply
  • Test provider upgrades in isolated environments
  • Document test scenarios and expected outcomes
  • Automate testing in pull request workflows

Source: SKILL.md on GitHub

1 alert16d5 checks · Risk CRITICAL
  • Gen Agent Trust Hub16d

    This skill is flagged as highly risky due to multiple confirmed detections by automated security scanners. The main skill definition file is classified as malware by file reputation services, and an included documentation link is blacklisted as malicious. Additionally, the skill's workflow involving the execution of Terraform commands on user-supplied code introduces an indirect prompt injection surface.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer6mo

    4/6 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at d0e7f4e. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 2 months ago
Other metadata
metadata
{
  "author": "https://github.com/Jeffallan",
  "version": "1.1.0",
  "domain": "infrastructure",
  "triggers": "Terraform, infrastructure as code, IaC, terraform module, terraform state, AWS provider, Azure provider, GCP provider, terraform plan, terraform apply",
  "role": "specialist",
  "scope": "implementation",
  "output-format": "code",
  "related-skills": "cloud-architect, devops-engineer, kubernetes-specialist"
}
  • Infrastructure
  • terraform
  • aws
  • azure
  • gcp
  • iac
  • modules
  • state-management
  • providers

README badge

README badge for jeffallan/claude-skills/terraform-engineer

Writes Terraform modules and state configurations for AWS, Azure, and GCP infrastructure. Handles module design, remote backend setup with locking, provider version pinning, and validation workflows using terraform fmt, terraform validate, and tflint.

Generated from the current SKILL.md.

Does this skill support AWS, Azure, and GCP?
Yes. The skill covers provider configuration and patterns for all three cloud platforms, with examples for AWS S3 and references to provider-specific guidance.
What does this skill do if terraform plan fails?
The skill includes error recovery steps for state drift, provider auth errors, and dependency issues. It guides you through terraform refresh, terraform import, credential verification, and explicit depends_on fixes before re-planning.
Does this skill handle state management?
Yes. It covers remote backend setup with locking and encryption (S3 + DynamoDB example included), workspace management, state migrations, and resource import workflows.
Will this skill create modules for me?
Yes. The skill designs and implements reusable modules with input validation, clear outputs, semantic versioning, and documentation. It enforces module interface patterns and prevents circular dependencies.
Does this skill test Terraform code?
The skill runs terraform fmt and terraform validate before every plan, plus tflint for linting. It references detailed testing and policy-as-code guidance in bundled documentation.

Generated from the current SKILL.md. These answers refresh after source changes.