Loading...
Automatically runs terraform plan when .tf files are modified to preview infrastructure changes
{
"hookConfig": {
"hooks": {
"postToolUse": {
"script": "./.claude/hooks/terraform-plan-executor.sh",
"matchers": [
"write",
"edit"
]
}
}
},
"scriptContent": "#!/bin/bash\n\n# Read the tool input from stdin\nINPUT=$(cat)\nTOOL_NAME=$(echo \"$INPUT\" | jq -r '.tool_name')\nFILE_PATH=$(echo \"$INPUT\" | jq -r '.tool_input.file_path // .tool_input.path // \"\"')\n\nif [ -z \"$FILE_PATH\" ]; then\n exit 0\nfi\n\n# Check if this is a Terraform file\nif [[ \"$FILE_PATH\" == *.tf ]] || [[ \"$FILE_PATH\" == *.tfvars ]]; then\n echo \"đī¸ Terraform Plan Executor - Analyzing infrastructure changes...\"\n echo \"đ File: $FILE_PATH\"\n \n # Check if file exists\n if [ ! -f \"$FILE_PATH\" ]; then\n echo \"â ī¸ Terraform file not found: $FILE_PATH\"\n exit 1\n fi\n \n # Get the directory containing the Terraform file\n TF_DIR=$(dirname \"$FILE_PATH\")\n TF_FILE=$(basename \"$FILE_PATH\")\n \n echo \"đ Working directory: $TF_DIR\"\n cd \"$TF_DIR\" || exit 1\n \n # Check if Terraform is installed\n if ! command -v terraform >/dev/null 2>&1; then\n echo \"â ī¸ Terraform not found - please install Terraform\"\n echo \"đĄ Install from: https://www.terraform.io/downloads\"\n exit 1\n fi\n \n # Get Terraform version\n TF_VERSION=$(terraform version -json 2>/dev/null | jq -r '.terraform_version' 2>/dev/null || terraform version | head -1)\n echo \"đĻ Terraform version: $TF_VERSION\"\n \n # Step 1: Format check\n echo \"\"\n echo \"đ¨ Checking Terraform formatting...\"\n if terraform fmt -check \"$TF_FILE\"; then\n echo \"â
Terraform formatting is correct\"\n else\n echo \"â ī¸ Terraform formatting issues detected\"\n echo \"đĄ Run 'terraform fmt' to fix formatting\"\n \n # Auto-fix formatting if requested\n echo \"đ§ Auto-fixing formatting...\"\n terraform fmt \"$TF_FILE\"\n echo \"â
Formatting applied to $TF_FILE\"\n fi\n \n # Step 2: Validation\n echo \"\"\n echo \"đ Validating Terraform configuration...\"\n if terraform validate; then\n echo \"â
Terraform configuration is valid\"\n else\n echo \"â Terraform validation failed\"\n echo \"đĄ Fix validation errors before proceeding\"\n exit 1\n fi\n \n # Step 3: Initialize if needed\n if [ ! -d \".terraform\" ]; then\n echo \"\"\n echo \"đ Initializing Terraform...\"\n if terraform init; then\n echo \"â
Terraform initialized successfully\"\n else\n echo \"â Terraform initialization failed\"\n exit 1\n fi\n fi\n \n # Step 4: Run terraform plan\n echo \"\"\n echo \"đ Running Terraform plan...\"\n \n PLAN_FILE=\".terraform-plan-$(date +%s)\"\n \n if terraform plan -out=\"$PLAN_FILE\" -compact-warnings; then\n echo \"â
Terraform plan completed successfully\"\n \n # Analyze the plan\n echo \"\"\n echo \"đ Plan Analysis:\"\n \n # Show plan summary\n if terraform show -json \"$PLAN_FILE\" >/dev/null 2>&1; then\n PLAN_JSON=$(terraform show -json \"$PLAN_FILE\" 2>/dev/null)\n \n # Count changes\n RESOURCES_TO_ADD=$(echo \"$PLAN_JSON\" | jq -r '.resource_changes[]? | select(.change.actions[]? == \"create\") | .address' 2>/dev/null | wc -l)\n RESOURCES_TO_CHANGE=$(echo \"$PLAN_JSON\" | jq -r '.resource_changes[]? | select(.change.actions[]? == \"update\") | .address' 2>/dev/null | wc -l)\n RESOURCES_TO_DESTROY=$(echo \"$PLAN_JSON\" | jq -r '.resource_changes[]? | select(.change.actions[]? == \"delete\") | .address' 2>/dev/null | wc -l)\n \n echo \" âĸ Resources to add: $RESOURCES_TO_ADD\"\n echo \" âĸ Resources to change: $RESOURCES_TO_CHANGE\"\n echo \" âĸ Resources to destroy: $RESOURCES_TO_DESTROY\"\n \n # Show resource details if any changes\n if [ \"$RESOURCES_TO_ADD\" -gt 0 ] || [ \"$RESOURCES_TO_CHANGE\" -gt 0 ] || [ \"$RESOURCES_TO_DESTROY\" -gt 0 ]; then\n echo \"\"\n echo \"đ Detailed Changes:\"\n \n if [ \"$RESOURCES_TO_ADD\" -gt 0 ]; then\n echo \" đĻ Resources to create:\"\n echo \"$PLAN_JSON\" | jq -r '.resource_changes[]? | select(.change.actions[]? == \"create\") | \" âĸ \" + .address' 2>/dev/null\n fi\n \n if [ \"$RESOURCES_TO_CHANGE\" -gt 0 ]; then\n echo \" đ Resources to modify:\"\n echo \"$PLAN_JSON\" | jq -r '.resource_changes[]? | select(.change.actions[]? == \"update\") | \" âĸ \" + .address' 2>/dev/null\n fi\n \n if [ \"$RESOURCES_TO_DESTROY\" -gt 0 ]; then\n echo \" đī¸ Resources to destroy:\"\n echo \"$PLAN_JSON\" | jq -r '.resource_changes[]? | select(.change.actions[]? == \"delete\") | \" âĸ \" + .address' 2>/dev/null\n fi\n else\n echo \" âšī¸ No infrastructure changes detected\"\n fi\n fi\n \n # Clean up plan file\n rm -f \"$PLAN_FILE\"\n \n else\n echo \"â Terraform plan failed\"\n rm -f \"$PLAN_FILE\"\n exit 1\n fi\n \n # Additional analysis\n echo \"\"\n echo \"đ Configuration Analysis:\"\n \n # Count resources in current file\n RESOURCE_COUNT=$(grep -c '^resource ' \"$TF_FILE\" 2>/dev/null || echo 0)\n DATA_COUNT=$(grep -c '^data ' \"$TF_FILE\" 2>/dev/null || echo 0)\n VAR_COUNT=$(grep -c '^variable ' \"$TF_FILE\" 2>/dev/null || echo 0)\n OUTPUT_COUNT=$(grep -c '^output ' \"$TF_FILE\" 2>/dev/null || echo 0)\n \n echo \" âĸ Resources defined: $RESOURCE_COUNT\"\n echo \" âĸ Data sources: $DATA_COUNT\"\n echo \" âĸ Variables: $VAR_COUNT\"\n echo \" âĸ Outputs: $OUTPUT_COUNT\"\n \n # Check for common patterns\n if grep -q 'provider ' \"$TF_FILE\" 2>/dev/null; then\n echo \" âĸ đ Provider configurations detected\"\n fi\n \n if grep -q 'module ' \"$TF_FILE\" 2>/dev/null; then\n echo \" âĸ đĻ Module usage detected\"\n fi\n \n if grep -q 'locals ' \"$TF_FILE\" 2>/dev/null; then\n echo \" âĸ đˇī¸ Local values defined\"\n fi\n \n # Security and best practices check\n echo \"\"\n echo \"đ Security Analysis:\"\n \n if grep -i 'password\\\\|secret\\\\|key' \"$TF_FILE\" 2>/dev/null | grep -v 'var\\.' | grep -v 'data\\.' >/dev/null; then\n echo \" âĸ â ī¸ Potential hardcoded secrets detected - use variables instead\"\n fi\n \n if grep -q '0.0.0.0/0' \"$TF_FILE\" 2>/dev/null; then\n echo \" âĸ â ī¸ Open security group rules detected (0.0.0.0/0)\"\n fi\n \n if ! grep -q 'tags\\\\|Tags' \"$TF_FILE\" 2>/dev/null && [ \"$RESOURCE_COUNT\" -gt 0 ]; then\n echo \" âĸ đĄ Consider adding resource tags for better management\"\n fi\n \n echo \"\"\n echo \"đĄ Terraform Best Practices:\"\n echo \" âĸ Use terraform fmt to maintain consistent formatting\"\n echo \" âĸ Store sensitive values in variables, not hardcoded\"\n echo \" âĸ Use remote state backend for team collaboration\"\n echo \" âĸ Implement resource tagging strategy\"\n echo \" âĸ Use terraform validate in CI/CD pipelines\"\n echo \" âĸ Review plans carefully before applying\"\n \n echo \"\"\n echo \"đ¯ Terraform plan execution complete!\"\n \nelse\n echo \"âšī¸ File is not a Terraform file: $FILE_PATH\"\nfi\n\nexit 0"
}.claude/hooks/~/.claude/hooks/{
"hooks": {
"postToolUse": {
"script": "./.claude/hooks/terraform-plan-executor.sh",
"matchers": [
"write",
"edit"
]
}
}
}#!/bin/bash
# Read the tool input from stdin
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // ""')
if [ -z "$FILE_PATH" ]; then
exit 0
fi
# Check if this is a Terraform file
if [[ "$FILE_PATH" == *.tf ]] || [[ "$FILE_PATH" == *.tfvars ]]; then
echo "đī¸ Terraform Plan Executor - Analyzing infrastructure changes..."
echo "đ File: $FILE_PATH"
# Check if file exists
if [ ! -f "$FILE_PATH" ]; then
echo "â ī¸ Terraform file not found: $FILE_PATH"
exit 1
fi
# Get the directory containing the Terraform file
TF_DIR=$(dirname "$FILE_PATH")
TF_FILE=$(basename "$FILE_PATH")
echo "đ Working directory: $TF_DIR"
cd "$TF_DIR" || exit 1
# Check if Terraform is installed
if ! command -v terraform >/dev/null 2>&1; then
echo "â ī¸ Terraform not found - please install Terraform"
echo "đĄ Install from: https://www.terraform.io/downloads"
exit 1
fi
# Get Terraform version
TF_VERSION=$(terraform version -json 2>/dev/null | jq -r '.terraform_version' 2>/dev/null || terraform version | head -1)
echo "đĻ Terraform version: $TF_VERSION"
# Step 1: Format check
echo ""
echo "đ¨ Checking Terraform formatting..."
if terraform fmt -check "$TF_FILE"; then
echo "â
Terraform formatting is correct"
else
echo "â ī¸ Terraform formatting issues detected"
echo "đĄ Run 'terraform fmt' to fix formatting"
# Auto-fix formatting if requested
echo "đ§ Auto-fixing formatting..."
terraform fmt "$TF_FILE"
echo "â
Formatting applied to $TF_FILE"
fi
# Step 2: Validation
echo ""
echo "đ Validating Terraform configuration..."
if terraform validate; then
echo "â
Terraform configuration is valid"
else
echo "â Terraform validation failed"
echo "đĄ Fix validation errors before proceeding"
exit 1
fi
# Step 3: Initialize if needed
if [ ! -d ".terraform" ]; then
echo ""
echo "đ Initializing Terraform..."
if terraform init; then
echo "â
Terraform initialized successfully"
else
echo "â Terraform initialization failed"
exit 1
fi
fi
# Step 4: Run terraform plan
echo ""
echo "đ Running Terraform plan..."
PLAN_FILE=".terraform-plan-$(date +%s)"
if terraform plan -out="$PLAN_FILE" -compact-warnings; then
echo "â
Terraform plan completed successfully"
# Analyze the plan
echo ""
echo "đ Plan Analysis:"
# Show plan summary
if terraform show -json "$PLAN_FILE" >/dev/null 2>&1; then
PLAN_JSON=$(terraform show -json "$PLAN_FILE" 2>/dev/null)
# Count changes
RESOURCES_TO_ADD=$(echo "$PLAN_JSON" | jq -r '.resource_changes[]? | select(.change.actions[]? == "create") | .address' 2>/dev/null | wc -l)
RESOURCES_TO_CHANGE=$(echo "$PLAN_JSON" | jq -r '.resource_changes[]? | select(.change.actions[]? == "update") | .address' 2>/dev/null | wc -l)
RESOURCES_TO_DESTROY=$(echo "$PLAN_JSON" | jq -r '.resource_changes[]? | select(.change.actions[]? == "delete") | .address' 2>/dev/null | wc -l)
echo " âĸ Resources to add: $RESOURCES_TO_ADD"
echo " âĸ Resources to change: $RESOURCES_TO_CHANGE"
echo " âĸ Resources to destroy: $RESOURCES_TO_DESTROY"
# Show resource details if any changes
if [ "$RESOURCES_TO_ADD" -gt 0 ] || [ "$RESOURCES_TO_CHANGE" -gt 0 ] || [ "$RESOURCES_TO_DESTROY" -gt 0 ]; then
echo ""
echo "đ Detailed Changes:"
if [ "$RESOURCES_TO_ADD" -gt 0 ]; then
echo " đĻ Resources to create:"
echo "$PLAN_JSON" | jq -r '.resource_changes[]? | select(.change.actions[]? == "create") | " âĸ " + .address' 2>/dev/null
fi
if [ "$RESOURCES_TO_CHANGE" -gt 0 ]; then
echo " đ Resources to modify:"
echo "$PLAN_JSON" | jq -r '.resource_changes[]? | select(.change.actions[]? == "update") | " âĸ " + .address' 2>/dev/null
fi
if [ "$RESOURCES_TO_DESTROY" -gt 0 ]; then
echo " đī¸ Resources to destroy:"
echo "$PLAN_JSON" | jq -r '.resource_changes[]? | select(.change.actions[]? == "delete") | " âĸ " + .address' 2>/dev/null
fi
else
echo " âšī¸ No infrastructure changes detected"
fi
fi
# Clean up plan file
rm -f "$PLAN_FILE"
else
echo "â Terraform plan failed"
rm -f "$PLAN_FILE"
exit 1
fi
# Additional analysis
echo ""
echo "đ Configuration Analysis:"
# Count resources in current file
RESOURCE_COUNT=$(grep -c '^resource ' "$TF_FILE" 2>/dev/null || echo 0)
DATA_COUNT=$(grep -c '^data ' "$TF_FILE" 2>/dev/null || echo 0)
VAR_COUNT=$(grep -c '^variable ' "$TF_FILE" 2>/dev/null || echo 0)
OUTPUT_COUNT=$(grep -c '^output ' "$TF_FILE" 2>/dev/null || echo 0)
echo " âĸ Resources defined: $RESOURCE_COUNT"
echo " âĸ Data sources: $DATA_COUNT"
echo " âĸ Variables: $VAR_COUNT"
echo " âĸ Outputs: $OUTPUT_COUNT"
# Check for common patterns
if grep -q 'provider ' "$TF_FILE" 2>/dev/null; then
echo " âĸ đ Provider configurations detected"
fi
if grep -q 'module ' "$TF_FILE" 2>/dev/null; then
echo " âĸ đĻ Module usage detected"
fi
if grep -q 'locals ' "$TF_FILE" 2>/dev/null; then
echo " âĸ đˇī¸ Local values defined"
fi
# Security and best practices check
echo ""
echo "đ Security Analysis:"
if grep -i 'password\\|secret\\|key' "$TF_FILE" 2>/dev/null | grep -v 'var\.' | grep -v 'data\.' >/dev/null; then
echo " âĸ â ī¸ Potential hardcoded secrets detected - use variables instead"
fi
if grep -q '0.0.0.0/0' "$TF_FILE" 2>/dev/null; then
echo " âĸ â ī¸ Open security group rules detected (0.0.0.0/0)"
fi
if ! grep -q 'tags\\|Tags' "$TF_FILE" 2>/dev/null && [ "$RESOURCE_COUNT" -gt 0 ]; then
echo " âĸ đĄ Consider adding resource tags for better management"
fi
echo ""
echo "đĄ Terraform Best Practices:"
echo " âĸ Use terraform fmt to maintain consistent formatting"
echo " âĸ Store sensitive values in variables, not hardcoded"
echo " âĸ Use remote state backend for team collaboration"
echo " âĸ Implement resource tagging strategy"
echo " âĸ Use terraform validate in CI/CD pipelines"
echo " âĸ Review plans carefully before applying"
echo ""
echo "đ¯ Terraform plan execution complete!"
else
echo "âšī¸ File is not a Terraform file: $FILE_PATH"
fi
exit 0terraform init fails with 'backend configuration changed' error
Run terraform init -reconfigure to update backend configuration. Use terraform init -migrate-state to migrate existing state. Check backend {} block in .tf files matches current state location.
Plan execution triggers on .tfvars edits but fails with missing vars
Pass variable file explicitly: terraform plan -var-file="$TF_FILE" if .tfvars file. Add conditional: [[ "$TF_FILE" == *.tfvars ]] && PLAN_ARGS="-var-file=$TF_FILE". Ensure terraform.tfvars is in same directory.
Hook changes to wrong directory breaking subsequent operations
Save original directory: ORIG_DIR=$(pwd) before cd "$TF_DIR". Always return: cd "$ORIG_DIR" || exit 1 at script end. Use pushd/popd for safer directory stack management.
terraform validate fails when run from subdirectory with modules
Always run from root module directory. Find root: while [ ! -f .terraform.lock.hcl ] && [ $(pwd) != / ]; do cd ..; done. Run terraform init before validate when .terraform/ missing.
Plan shows destroy actions when only formatting was changed
This indicates state drift or provider version change. Run terraform refresh before plan. Check provider version constraints in required_providers block. Review .terraform.lock.hcl for version updates.
Loading reviews...
Join our community of Claude power users. No spam, unsubscribe anytime.
Automated accessibility testing and compliance checking for web applications following WCAG guidelines
Automatically generates or updates API documentation when endpoint files are modified
Automatically formats code files after Claude writes or edits them using Prettier, Black, or other formatters