I use Claude to review Terraform before every apply — it finds what I miss
I ran terraform apply on a plan that had been reviewed by two engineers. Fifteen minutes later I realized I had created an S3 bucket with public read access. The plan clearly showed acl = "public-read" but we had both missed it in a 400-line diff. Now I run a Claude review before every non-trivial apply and it catches exactly these kinds of issues.
The review script
#!/bin/bash
# tf-review.sh — run before terraform apply
set -e
PLAN_FILE="${1:-tfplan}"
# Generate plan if not provided
if [ ! -f "$PLAN_FILE" ]; then
echo "Generating plan..."
terraform plan -out="$PLAN_FILE"
fi
# Convert plan to human-readable JSON
terraform show -json "$PLAN_FILE" > /tmp/tfplan.json
# Run Claude review
echo "Running security review..."
cat /tmp/tfplan.json | python3 - <<'EOF'
import sys
import json
import anthropic
plan = json.loads(sys.stdin.read())
client = anthropic.Anthropic()
# Extract relevant changes
changes = []
for change in plan.get("resource_changes", []):
if change.get("change", {}).get("actions", []) != ["no-op"]:
changes.append({
"resource": change["address"],
"type": change["type"],
"actions": change["change"]["actions"],
"before": change["change"].get("before"),
"after": change["change"].get("after"),
})
if not changes:
print("No infrastructure changes detected")
sys.exit(0)
changes_json = json.dumps(changes, indent=2)
response = client.messages.create(
model="claude-opus-4-5",
max_tokens=2000,
system="""You are a cloud security engineer reviewing Terraform plans.
Check for:
1. SECURITY: Public access on S3/storage, overly permissive security groups (0.0.0.0/0),
IAM policies with * actions, missing encryption, public RDS instances
2. RELIABILITY: Missing deletion_protection on databases, no backup retention,
single-AZ deployments for critical resources
3. COST: Unexpectedly large instance types, resources missing auto-scaling
4. DESTRUCTIVE: Resources being destroyed (especially databases, S3 buckets with data)
Format findings as:
## [SEVERITY: CRITICAL/HIGH/MEDIUM/LOW] Resource: Issue
What: specific misconfiguration
Risk: what could go wrong
Fix: the exact Terraform change needed
If no issues found, say "No issues found — safe to apply."
""",
messages=[{
"role": "user",
"content": f"Review this Terraform plan for issues:
{changes_json[:30000]}"
}]
)
print(response.content[0].text)
EOF
Adding it to your workflow
# Makefile
plan:
terraform plan -out=tfplan
review: plan
./scripts/tf-review.sh tfplan
apply: review
@echo "Press Enter to apply, Ctrl+C to cancel"
@read
terraform apply tfplan
What it catches that humans miss
Over six months of using this, here are the real findings Claude caught before apply:
- S3 bucket with
block_public_policy = false— intended for static assets but the ACL was wrong - Security group opening port 22 (SSH) to
0.0.0.0/0for "temporary" debugging - RDS instance without
deletion_protection = trueon a production database - Lambda with IAM role containing
"Action": "*"— way more permissions than needed - ElastiCache without encryption at rest — left from a dev environment copy-paste
None of these were caught in human review. They were all obvious once flagged. The human review was not negligent — it was just fatigued.
GitHub Actions integration
# .github/workflows/tf-review.yml
name: Terraform Review
on:
pull_request:
paths: ['terraform/**', 'infra/**']
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- name: Terraform Plan
run: terraform plan -out=tfplan
working-directory: terraform/
- name: AI Security Review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
terraform show -json tfplan > /tmp/plan.json
python3 scripts/tf-review.py /tmp/plan.json > review.md
working-directory: terraform/
- name: Post Review
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const review = fs.readFileSync('terraform/review.md', 'utf8');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: '## Infrastructure Security Review
' + review
});
Every Terraform PR now gets an automatic security review as a comment. CRITICAL and HIGH findings block the merge via branch protection rules. Medium and low findings are informational — the engineer decides whether to address them before merging.