Know what you can't undo before you terraform apply.
Recourse analyzes Terraform plans for blast radius and recoverability. It tells you which changes are reversible, which need backups to recover, and which will permanently destroy data.
npm install -g recourse-cliOr run directly:
npx recourse-cli plan plan.json# Generate a plan JSON
terraform plan -out=plan.bin
terraform show -json plan.bin > plan.json
# Analyze it
recourse-cli plan plan.jsonBLAST RADIUS REPORT
═══════════════════
DIRECT CHANGES
✗ DELETE aws_db_instance.main
Recoverability: unrecoverable
skip_final_snapshot=true, no backup retention; data will be lost
✗ DELETE aws_s3_bucket.logs
Recoverability: unrecoverable
Bucket deletion is permanent; versioning does not survive bucket deletion
~ UPDATE aws_security_group.web
Recoverability: reversible
SUMMARY
Unrecoverable: 2 resources
Recoverable (backup): 0 resources
Recoverable (effort): 0 resources
Reversible: 1 resource
⚠️ This plan contains unrecoverable changes.
| Tier | Label | Meaning |
|---|---|---|
| 1 | reversible |
Can undo with another apply (e.g., re-enable a setting) |
| 2 | recoverable-with-effort |
Can recreate but requires work (e.g., EC2 from AMI) |
| 3 | recoverable-from-backup |
Requires backup/snapshot to restore (e.g., RDS from snapshot) |
| 4 | unrecoverable |
Data is gone (e.g., RDS with skip_final_snapshot=true) |
Don't trust a verdict? See exactly why:
recourse-cli explain plan.json aws_db_instance.mainVERDICT
══════════════════════════════════════════════════
aws_db_instance.main → unrecoverable (high confidence)
skip_final_snapshot=true, no backup retention; data will be lost
CLASSIFICATION TRACE
──────────────────────────────────────────────────
✓ deletion_protection
→ false
No deletion protection
✗ skip_final_snapshot
→ true
No automatic snapshot on deletion
✗ backup_retention_period
→ 0
No automated backups
WHAT WOULD CHANGE THIS
──────────────────────────────────────────────────
• If deletion_protection were set to true
→ Verdict would be: blocked
Apply would fail; AWS blocks deletion when protection is enabled
• If skip_final_snapshot were false
→ Verdict would be: recoverable-from-backup
A final snapshot would be created before deletion
Fail the pipeline if any change is unrecoverable:
recourse-cli plan plan.json --fail-on unrecoverableExit codes:
0— No changes at or above the specified tier1— At least one change meets or exceeds the tier
Options for --fail-on: unrecoverable, backup, effort, reversible
- Databases: RDS instances/clusters, DynamoDB tables
- Storage: S3 buckets/objects, EBS volumes/snapshots
- Compute: EC2 instances, Lambda functions, AMIs
- Networking: VPCs, subnets, security groups, EIPs, load balancers
- IAM: Roles, policies, users
- Other: KMS keys, Route53 zones, SNS/SQS, CloudWatch logs
Run recourse-cli resources to see the full list.
GCP and Azure resources are classified via a classifier that reads abstract safety patterns from your plans. The classifier learned from AWS rules and generalizes across clouds.
What's tested (8 resource types):
- GCP:
google_project_iam_binding,google_sql_database_instance,google_storage_bucket - Azure:
azurerm_role_assignment,azurerm_dns_a_record,azurerm_storage_account
What works:
- IAM/access control resources (via naming patterns)
- Databases with
deletion_protection(via abstract feature transfer) - DNS records (config-only, always reversible)
What's not yet validated:
- Full coverage across all resource types
- Correct
unrecoverableverdicts for storage without versioning/backups
We're actively looking for testers. Run recourse-cli plan with --classifier on your GCP/Azure plans and tell us what we got wrong.
recourse-cli plan plan.json --format json
recourse-cli explain plan.json aws_db_instance.main --format jsonRecourse analyzes what's in the Terraform plan and state. It cannot:
- Verify backups exist outside Terraform (AWS Backup, manual snapshots)
- Check cross-account or cross-region configurations
- Know what's actually in your S3 buckets or databases
- Predict race conditions between plan and apply
The tool is conservative: when it can't verify something, it tells you in the limitations section of --explain.
MIT