TE

Runs formatting, validation, and planning for Terraform changes against a test environment.

Install

mkdir -p .claude/skills/test-changes && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/3315" && unzip -o skill.zip -d .claude/skills/test-changes && rm skill.zip

Installs to .claude/skills/test-changes

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Use after making changes to run terraform fmt, validate, and plan against test environment
90 charsno explicit “when” trigger
Intermediate

Key capabilities

  • Executes recursive terraform formatting
  • Runs infrastructure validation checks
  • Initializes test environments with upgrades
  • Simulates plan execution for side-effect analysis
  • Automates breaking change detection

How it works

It chains terraform commands (fmt, validate, init, plan) in a directed graph, enforcing pass-through checkpoints before each stage.

Inputs & outputs

You give it
Terraform configuration changes
You get back
Validation and plan result status

When to use test-changes

  • Format Terraform module code
  • Validate infrastructure configuration
  • Plan Terraform changes against test cluster

About this skill

Test Terraform Changes

Overview

Run the standard validation suite for Terraform/OpenTofu changes against the test environment.

Usage

/test-changes

Test Environment

  • Module code: current git worktree (git rev-parse --show-toplevel)
  • Test cluster: an existing cluster Terraform root supplied by the operator (<existing-cluster-terraform-root> in examples below)

Workflow

digraph test_flow {
    rankdir=TB;
    node [shape=box];

    fmt [label="1. terraform fmt -recursive"];
    no_null [label="2. forbid null_resource"];
    init_local [label="3. terraform init -backend=false"];
    validate [label="4. terraform validate"];
    tofu [label="5. OpenTofu temp-copy validate"];
    example [label="6. kube.tf.example parse check"];
    init [label="7. terraform init -upgrade"];
    plan [label="8. terraform plan"];
    review [label="9. Review plan output"];

    fmt -> no_null;
    no_null -> init_local;
    init_local -> validate;
    validate -> tofu;
    tofu -> example;
    example -> init;
    init -> plan;
    plan -> review;
}

Step 1: Format Check

cd "$(git rev-parse --show-toplevel)"
terraform fmt -recursive

Must pass before proceeding.

Step 2: Forbid null_resource Usage

cd "$(git rev-parse --show-toplevel)"
if rg -n 'resource[[:space:]]+"null_resource"|provider[[:space:]]+"null"|hashicorp/null' -g '*.tf' -g '*.tf.json' .; then
  echo "Use terraform_data instead of null_resource/hashicorp/null. Moved blocks from old null_resource addresses are allowed for state migration."
  exit 1
fi

Must pass before proceeding. Any operational placeholder resource should use the built-in terraform_data resource. Keep moved blocks that reference old null_resource addresses because they preserve upgrade/state migration safety.

Step 3: Initialize Local Providers

cd "$(git rev-parse --show-toplevel)"
terraform init -backend=false

Must pass before proceeding.

Step 4: Validate Module

cd "$(git rev-parse --show-toplevel)"
terraform validate -no-color

Must pass before proceeding.

Step 5: Validate OpenTofu Compatibility

cd "$(git rev-parse --show-toplevel)"
tmpdir="$(mktemp -d)"
rsync -a --exclude .git --exclude .terraform --exclude .terraform-tofu ./ "$tmpdir"/
(cd "$tmpdir" && tofu init -backend=false -input=false && tofu validate -no-color)
rm -rf "$tmpdir"

Must pass before proceeding. OpenTofu is officially supported and should catch the same module-contract validation errors as Terraform during plan. Use a temporary copy when validating both CLIs so OpenTofu cannot rewrite the ignored .terraform.lock.hcl or local plugin cache in the main Terraform checkout. Cross-variable contract failures are enforced by terraform_data.validation_contract, so invalid-combination tests should assert terraform plan, not only terraform validate.

Step 6: Validate kube.tf.example Parseability

cd "$(git rev-parse --show-toplevel)"
MODULE_ROOT="$PWD"
tmpdir="$(mktemp -d)"
cp kube.tf.example "$tmpdir/main.tf"
MODULE_ROOT="$MODULE_ROOT" perl -0pi -e 's#source = "kube-hetzner/kube-hetzner/hcloud"#source = "$ENV{MODULE_ROOT}"#' "$tmpdir/main.tf"
(cd "$tmpdir" && terraform fmt -check main.tf && terraform init -backend=false && terraform validate)

Must pass before proceeding.

Step 6.5: Validate Large Tailscale Examples

cd "$(git rev-parse --show-toplevel)"
uv run scripts/validate_tailscale_large_scale_examples.py

Must pass when Tailscale node transport, multinetwork, autoscaler, placement group, or example docs change. This checks the +100-node and 10,000-total-node reference topology math without creating real 10k infrastructure.

The v3 smoke matrix also covers public join endpoint family handling: IPv6-only control-plane public joins must remain valid, and public joins without a real public API host must fail before deployment. The helper retries transient provider-download failures during terraform init.

Step 6.6: Validate v3 Final-Polish Surfaces

cd "$(git rev-parse --show-toplevel)"
uv run scripts/validate_v3_final_polish_examples.py

Must pass when topology docs, cilium_gateway_api_enabled, embedded_registry_mirror, endpoint outputs, Cloudflare/Tailscale examples, or skills change. This keeps the v3 topology chooser, Gateway API example, registry mirror snippets, Cloudflare external-access boundary, and validation gates in sync.

Step 6.7: Run v3 Blast-Radius Plan Matrix

cd "$(git rev-parse --show-toplevel)"
uv run scripts/smoke_v3_plan_matrix.py

Must pass when Cilium Gateway API, embedded registry mirror, Tailscale node transport, multinetwork validation, or endpoint-mode logic changes. This creates disposable Terraform roots and never applies, but it needs a real HCloud token so successful plans can read provider data sources. It covers k3s and RKE2 Tailscale registry paths plus the single-Gateway-controller guard. Set SMOKE_HCLOUD_EXTERNAL_NETWORK_ID if no existing HCloud Network is available for the external-network Tailscale plan smoke.

Step 7: Initialize Test Environment

cd <existing-cluster-terraform-root>
terraform init -upgrade

This picks up changes from the local module.

Step 8: Plan Against Test Cluster

cd <existing-cluster-terraform-root>
terraform plan

What to Look For

Good Signs

  • Only expected resources change
  • No unexpected additions/deletions
  • Changes match your intended modifications

Red Flags (STOP!)

OutputMeaningAction
will be destroyedResource recreationSTOP - Breaking change
must be replacedResource recreationSTOP - Breaking change
forces replacementResource recreationSTOP - Breaking change
Unexpected changesSide effectsInvestigate before proceeding

Breaking Change = MAJOR Release

If terraform plan shows ANY resource destruction on existing infrastructure:

  1. STOP - This is NOT backward compatible
  2. The change requires a MAJOR version bump
  3. Migration guide is required
  4. Consider alternative approaches first

Empty-State Upgrade Gotcha

An empty state file is not an upgrade proof. It yields a fresh-deploy plan, so it cannot prove whether a module branch preserves existing resources. When no live upgrade root is available, use a tag-vs-branch plan-address-set diff as the structural proxy: render the same config against the released tag and the branch, save both plan JSON files, and compare sorted .resource_changes[].address sets. Address churn is the signal to investigate before any live apply.

Step 9: Review Plan Output

Checklist

  • terraform fmt -recursive passes
  • no live null_resource/hashicorp/null usage exists
  • terraform init -backend=false passes
  • terraform validate passes
  • OpenTofu temp-copy validation passes
  • kube.tf.example parses against the local checkout
  • uv run scripts/validate_tailscale_large_scale_examples.py passes when large-scale/Tailscale/networking examples are touched
  • uv run scripts/validate_v3_final_polish_examples.py passes when Gateway API/registry/topology/Cloudflare boundary docs are touched
  • uv run scripts/smoke_v3_plan_matrix.py passes when Gateway API/registry/Tailscale plan behavior is touched
  • Tailscale node-transport static cases pass/fail as expected when variables/networking are touched
  • terraform plan shows expected changes only
  • No resource destruction
  • No unexpected side effects
  • Changes are backward compatible

Quick Reference

# Full test sequence
cd "$(git rev-parse --show-toplevel)" && \
MODULE_ROOT="$PWD" && \
terraform fmt -recursive && \
if rg -n 'resource[[:space:]]+"null_resource"|provider[[:space:]]+"null"|hashicorp/null' -g '*.tf' -g '*.tf.json' .; then exit 1; fi && \
terraform init -backend=false && \
terraform validate && \
tmpdir="$(mktemp -d)" && \
rsync -a --exclude .git --exclude .terraform --exclude .terraform-tofu ./ "$tmpdir"/ && \
(cd "$tmpdir" && tofu init -backend=false && tofu validate) && \
rm -rf "$tmpdir" && \
tmpdir="$(mktemp -d)" && cp kube.tf.example "$tmpdir/main.tf" && \
MODULE_ROOT="$MODULE_ROOT" perl -0pi -e 's#source = "kube-hetzner/kube-hetzner/hcloud"#source = "$ENV{MODULE_ROOT}"#' "$tmpdir/main.tf" && \
(cd "$tmpdir" && terraform fmt -check main.tf && terraform init -backend=false && terraform validate) && \
rm -rf "$tmpdir" && \
uv run scripts/validate_tailscale_large_scale_examples.py && \
uv run scripts/validate_v3_final_polish_examples.py && \
uv run scripts/smoke_v3_plan_matrix.py && \
cd <existing-cluster-terraform-root> && \
terraform init -upgrade && \
terraform plan

Apply (Optional)

Only if plan looks correct and you want to test on actual infrastructure:

cd <existing-cluster-terraform-root>
terraform apply

Caution: This modifies real infrastructure. Only do this for thorough testing.

Teardown After Live Tests

When destroying a live test root, run the module teardown helper from the Terraform root:

cd <existing-cluster-terraform-root>
<module-checkout>/scripts/destroy.sh -auto-approve

scripts/destroy.sh is the recommended teardown path. It auto-retries only the known benign ingress-LB detach race (resource_already_detaching/422 from dual CCM and Terraform ownership), then prints a read-only orphan report including unlabeled primary IPs, out-of-state autoscaled nodes, and exact managed load balancer names. Use scripts/cleanup.sh only as the forceful fallback after the report shows leftovers or state is already wrecked.

Autoscaler-created servers are not in Terraform state. If an autoscaled server pins the network/subnet during destroy, delete it only after the control plane is dead, or first scale the autoscaler pool to min_nodes = 0. Deleting


Content truncated.

When not to use it

  • Production environments without prior staging validation
  • Repositories not using Terraform
  • When infrastructure changes require external coordination

Prerequisites

Terraform installedAccess to target infrastructure

Limitations

  • Requires specific local path access to test clusters
  • Cannot detect all architectural edge cases
  • Stops completely on any unexpected destruction flag

How it compares

It standardizes the infrastructure validation workflow into a rigid, non-bypassable sequence of checks.

Compared to similar skills

test-changes side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
test-changes (this skill)13moReviewIntermediate
aws-solution-architect203moReviewAdvanced
terraform-module-library74moNo flagsAdvanced
azure-deployment-preflight76moReviewAdvanced

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

aws-solution-architect

alirezarezvani

Design AWS architectures for startups using serverless patterns and IaC templates. Use when asked to design serverless architecture, create CloudFormation templates, optimize AWS costs, set up CI/CD pipelines, or migrate to AWS. Covers Lambda, API Gateway, DynamoDB, ECS, Aurora, and cost optimization.

2047

terraform-module-library

wshobson

Build reusable Terraform modules for AWS, Azure, and GCP infrastructure following infrastructure-as-code best practices. Use when creating infrastructure modules, standardizing cloud provisioning, or implementing reusable IaC components.

759

azure-deployment-preflight

github

Performs comprehensive preflight validation of Bicep deployments to Azure, including template syntax validation, what-if analysis, and permission checks. Use this skill before any deployment to Azure to preview changes, identify potential issues, and ensure the deployment will succeed. Activate when users mention deploying to Azure, validating Bicep files, checking deployment permissions, previewing infrastructure changes, running what-if, or preparing for azd provision.

746

terraform-azurerm-set-diff-analyzer

github

Analyze Terraform plan JSON output for AzureRM Provider to distinguish between false-positive diffs (order-only changes in Set-type attributes) and actual resource changes. Use when reviewing terraform plan output for Azure resources like Application Gateway, Load Balancer, Firewall, Front Door, NSG, and other resources with Set-type attributes that cause spurious diffs due to internal ordering changes.

534

terraform-skill

sickn33

Terraform infrastructure as code best practices

829

aws-advisor

tech-leads-club

Expert AWS Cloud Advisor for architecture design, security review, and implementation guidance. Leverages AWS MCP tools for accurate, documentation-backed answers. Use when user asks about AWS architecture, security, service selection, migrations, troubleshooting, or learning AWS. Triggers on AWS, Lambda, S3, EC2, ECS, EKS, DynamoDB, RDS, CloudFormation, CDK, Terraform, Serverless, SAM, IAM, VPC, API Gateway, or any AWS service.

529

Search skills

Search the agent skills registry