[AWS Provider v6] Part 1: Solving the 'Forced Replace' Crisis for 200 Resources
The transition from Terraform AWS Provider v5 to v6 was not just a simple patch; it was a "cataclysm." I recently migrated about 50 operating modules to v6 and faced a daunting situation where over 200 resources were pending "Replace" (destroy and recreate) even though I only ran plan without modifying the code.
This post is a troubleshooting log of 3 critical Breaking Changes encountered in a production environment and how I resolved them to achieve "Plan: 0 to add, 0 to change, 0 to destroy."
1. Observation Environment
- Terraform Core: v1.5.7 (The minimum version where the
importblock worked stably in my environment) - AWS Provider: v5.87.0 (Last version before v6) → v6.0.0
- Backend: S3 + DynamoDB (State Locking)
- Target: Approx. 50 modules including EC2, RDS, S3, IAM, etc.
2. Issue 1: Stricter Type Checking (Boolean String)
From v6, HCL's type flexibility has been removed. Immediate errors occurred in 12 legacy modules out of 50.
[Log]
Error: Invalid value for boolean
on modules/s3_base/main.tf line 24: mfa_delete = "false"
[Solution]
I corrected "false" (String) to false (Boolean). Since global regex replacement is risky (danger of modifying tag values), I used IDE search to modify only the specific attribute values.
- Result: Errors 12 → 0. However, about 200 changes still remained in the
plan.
3. Issue 2: S3 Lifecycle Rule Conflict
It was observed that inline lifecycle_rule blocks within aws_s3_bucket resources were ignored or deleted in v6.
[Plan Diff]
Existing data retention rules were marked for deletion (Risk of data loss).
~ resource "aws_s3_bucket" "log_archive" {
- lifecycle_rule { - id = "log_expiration_90days" ... }
}
[Solution]
I prevented policy deletion by removing the inline block and separating it into a distinct aws_s3_bucket_lifecycle_configuration resource.
4. Issue 3: Default Value Changes & 'Forced Replace'
This was the most dangerous issue. Over 80% of the 200 pending Replace items were Launch Templates and Security Group Rules.
[Root Cause Analysis]
The default value for the disable_api_termination attribute in aws_launch_template changed from null to false. In my case, combined with changes in user_data (Base64 encoding method differences), Terraform judged this as "Modifications impossible, must delete and recreate (ForceNew)."
[Solution]
Instead of relying on the Provider's default values, I explicitly defined the existing state (true or false) in the code.
resource "aws_launch_template" "app" {
# Defense against v6 default value change: Explicitly set value to remove Diff
disable_api_termination = false
}
- Result: Diff 200 → 145 → 0 (No changes achieved).
5. Summary of Principles
- Drift First: Eliminate existing inconsistencies first using
terraform plan -refresh-only. - Code Defense: Defend against Diffs caused by default value changes by modifying the code (fixing values).
- Zero Diff Goal: The goal at the time of upgrade is not refactoring, but 'Zero Changes'.