
This article is part of a series on Terraform Providers.
HashiCorp pushed the AWS Provider v6.0 release to May 2025, with beta1 dropping on May 7th. The stable release? Expect it around June 18, 2025.
Here's what's weird - they originally planned for April, but the community feedback must've been intense. Beta2 hit on May 22nd. And if you're still on v5.x after stable drops, you'll only get security patches. No grace period mentioned.
All 16 OpsWorks resources? Gone. Vanished. If you're still using these in May 2025 (a full year after AWS killed the service), you've got bigger problems:
# These are dead in v6.0
resource "aws_opsworks_stack" "legacy" {
name = "my-stack" # RIP
}
resource "aws_opsworks_instance" "web" {
stack_id = aws_opsworks_stack.legacy.id # Won't work
}Remember when Terraform let you be lazy with booleans? Those days are over.
# Old way (v5.x) - worked but was sloppy
resource "aws_launch_template" "old_habits" {
block_device_mappings {
ebs {
delete_on_termination = "1" # Nope
encrypted = "0" # Also nope
}
}
}
# New way (v6.0) - what you should've done anyway
resource "aws_launch_template" "clean_code" {
block_device_mappings {
ebs {
delete_on_termination = true
encrypted = false
}
}
}Redshift clusters suddenly became security-conscious:
publicly_accessible now defaults to false (was true - yikes)encrypted now defaults to true (finally)That EIP resource you've been using wrong? The vpc parameter is gone:
# Before
resource "aws_eip" "old" {
vpc = true # Dead parameter
}
# After
resource "aws_eip" "new" {
domain = "vpc"
}Let's get practical. Run these commands before you even think about upgrading:
# Find the zombies (deprecated resources)
grep -r "aws_opsworks_\|aws_simpledb_\|aws_worklink_" . --include="*.tf"
# Hunt down lazy boolean usage
grep -r '"[01]"' . --include="*.tf" | \
grep -E "delete_on_termination|encrypted|ebs_optimized"
# Check your provider constraints
grep -r "version.*=" . --include="*.tf" | grep awsAnd here's a TFLint config that'll catch issues:
# .tflint.hcl
plugin "aws" {
enabled = true
version = "0.39.0"
source = "github.com/terraform-linters/tflint-ruleset-aws"
}
rule "terraform_deprecated_lookup" {
enabled = true
}State backups before migration? Non-negotiable:
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p state-backups/${DATE}
terraform state pull > state-backups/${DATE}/terraform.tfstate.backup
cp .terraform.lock.hcl state-backups/${DATE}/If you're not testing your IaC, what are you even doing? Here's a basic compatibility test:
func TestV6Compatibility(t *testing.T) {
terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
TerraformDir: "../infrastructure",
Vars: map[string]interface{}{
"test_mode": true,
},
})
defer terraform.Destroy(t, terraformOptions)
// Apply with current provider
terraform.InitAndApply(t, terraformOptions)
// Verify plan shows no changes
planOutput := terraform.Plan(t, terraformOptions)
assert.Contains(t, planOutput, "No changes")
}Add this to your GitHub Actions (or whatever you use):
name: Provider v6 Compatibility
on:
pull_request:
paths:
- '**.tf'
- '.terraform.lock.hcl'
jobs:
validate-v6:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: hashicorp/setup-terraform@v2
with:
terraform_version: 1.9.0
- name: Test v6 compatibility
run: |
terraform init -upgrade
terraform validate
tflint --recursive --format=compactSpeaking of which - if you're managing multiple environments and provider versions, platforms like Scalr handle this complexity with policy-based controls. You can set provider version constraints per environment, which beats managing it manually across dozens of workspaces.
Start with your least critical modules. I usually go:
For each module:
# modules/networking/versions.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0.0-beta2" # Start with beta
}
}
}My coffee's getting cold as I write this, but here's the pattern that works:
# environments/production/versions.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "= 6.0.0" # Exact pin for prod
}
}
}This is actually cool. No more provider alias gymnastics:
provider "aws" {
region = "us-east-1"
}
# Default region resource
resource "aws_s3_bucket" "main" {
bucket = "my-main-bucket"
}
# Override region inline
resource "aws_s3_bucket" "backup" {
bucket = "my-backup-bucket"
region = "eu-west-1" # Just works now
}
# Cross-region replication without aliases
resource "aws_s3_bucket_replication_configuration" "cross_region" {
bucket = aws_s3_bucket.main.id
rule {
id = "backup"
status = "Enabled"
destination {
bucket = aws_s3_bucket.backup.arn
# No provider alias needed!
}
}
}When (not if) things go sideways:
# 1. Revert provider version in versions.tf
# 2. Clean the provider cache
rm -rf .terraform/
rm .terraform.lock.hcl
# 3. Reinitialize
terraform init
# 4. If state is corrupted
terraform state push state-backups/[timestamp]/terraform.tfstate.backupFor the dreaded "resource instance managed by newer provider version" error:
# Remove problematic resource
terraform state rm aws_instance.borked
# Re-import with old provider
terraform import aws_instance.borked i-1234567890abcdef0You know what helps here? Infrastructure management platforms that version your state files automatically. Scalr, for instance, keeps state history so you can roll back without hunting for backup files. But I'm not here to sell you anything.
The Terraform community's tracking this migration in various places:
One team built a Grafana dashboard tracking their 847-module migration. Another used AWS Migration Factory with QuickSight. Whatever works.
Breaking Change
Impact
Migration Effort
Rollback Risk
OpsWorks removal
High if used
Complete rewrite
Low
Boolean strictness
Medium
Find & replace
Low
Default value changes
High
Test extensively
Medium
SimpleDB/WorkLink removal
Low
Stay on v5.x
Low
EIP vpc parameter
Low
Simple update
Low
Multi-region support
Positive
Refactor aliases
Low
The bottom line? Test in dev, migrate incrementally, keep backups. And maybe consider using a platform that handles provider version management across teams - but hey, that's just me thinking out loud.
What strikes me about this v6.0 release is how long it's been coming. We've known about these deprecations forever. If you're scrambling now, in late May 2025, you've had plenty of warning. The multi-region support alone makes the migration worth it, assuming you've cleaned up your technical debt.
Start your migration this week. Or don't. Your on-call rotation, your choice.
