Upgrading to AWS Provider 6.x: Diagnosing Breaking Changes and a Safe Transition Order

4 min read

The upgrade to treat more carefully in Terraform operations is not the CLI but the provider major. The conclusion up front: a provider major changes resource schemas themselves, so plan diffs can appear with no code changes at all, and the standard route is “go through the latest 5.x → cross-check the official upgrade guide → confirm a no-op or fully explained diff → roll out stack by stack.” The general CLI procedure is in the previous post; this one uses AWS provider 6.x to cover the risks specific to majors. Changes listed here were verified as of 2026-08 — recheck the official guide when you apply them.

Why a provider major is heavier than the CLI #

CLI minors are protected by the compatibility promises; a provider major is the opposite — a release that exists in order to break compatibility. Resource arguments get removed, defaults change, whole resource types disappear. When schemas change, plan shows diffs even with untouched code and infrastructure, and some of those diffs mean replacement: destroy and recreate. A replacement verdict on a stateful resource like a database is not a diff, it is an incident forecast.

A major upgrade is therefore not “bump the number, run plan once” but a full reading of the plan output. The reading criteria come below.

What breaks in 6.0 #

From the official upgrade guide, the changes most likely to bite in practice:

ChangeContentCode affected
Resource-level regionregion argument added to most resources; multi-region without provider aliasesExisting alias patterns keep working; new designs gain an option
OpsWorks removed entirely17 resource types deletedStacks using OpsWorks must migrate before upgrading
SimpleDB removedaws_simpledb_domain deletedAny code using it
aws_eip vpc argument removedReplaced by the domain argumentOlder EIP code
Strict booleans“1” and “0” strings no longer accepted as booleansLegacy code from the early days
Redshift defaults flippedpublicly_accessible defaults false, encrypted defaults trueRedshift code that omitted the arguments now shows diffs

The biggest structural change is resource-level region. Multi-region so far meant one provider alias per region attached to resources (the ACM and CloudFront setup in Practice #10 is that pattern); from 6.x, resources can take region directly. Existing alias code keeps working, so it does not block the upgrade, but newly designed stacks can now cut down on aliases.

The flipped-defaults category is the quiet, dangerous one. Redshift code that omitted those arguments shows diffs against the new defaults in 6.x. Read that as a prompt to make intent explicit: write the arguments out and the diff disappears.

Transition order: via the latest 5.x #

  1. First move to the newest 5.x minor: the last version before a major emits deprecation warnings for what the major will remove. Burning these down first surfaces most of what would otherwise break at 6.0.
  2. Cross-check the upgrade guide against your code: grep your repository for the resources and arguments on the change list and fix them ahead of time.
  3. Update constraints and the lock file: switch required_providers to ~> 6.0 and refresh the lock file with terraform init -upgrade. Make this its own PR, separate from any CLI upgrade.
  4. Read the whole plan: the ideal is no-op, but for majors the acceptable bar is “every diff explained.” In-place changes (~) from schema cleanups are mostly harmless; anything marked forces replacement does not get applied until its cause is understood. A replacement verdict on a data-tier resource is a stop condition.
  5. Roll out stack by stack: complete an apply on a development stack to confirm real-world effects before production. Not everything must cross at once, but keep the mixed-version window short.

Shared modules: where the blocker lives #

Shared internal modules and registry modules add one more variable. If a module’s required_providers caps at >= 5.0, < 6.0, root stacks cannot move to 6.x however much they want to. Conversely, once a module starts using 6.x-only syntax such as resource-level region, stacks still on 5.x can no longer consume it.

Modules go first. Widen the module’s provider constraint to be 6.x-compatible (ideally spanning both, like >= 5.x, < 7.0, during the transition), migrate the root stacks in sequence, then raise the module floor to 6.x to close it out. If a third-party module is pinned to 5.x, its upstream’s readiness becomes the schedule constraint for the whole upgrade — which is why it is the first thing to check in the cross-check step.

Summary #

  • A provider major is a schema-breaking release. Diffs appear without code changes, replacements can hide among them, and a full plan reading is mandatory.
  • The standard route goes through the latest 5.x. Burning down deprecation warnings first surfaces most of what 6.0 would break.
  • 6.0’s core changes: resource-level region, OpsWorks and SimpleDB removal, strict booleans, flipped Redshift defaults. Kill default-flip diffs by writing arguments explicitly.
  • Reading criteria: in-place changes are mostly harmless, forces replacement means no apply until explained, and data-tier replacements are stop conditions.
  • Shared modules’ provider constraints are the schedule bottleneck. Widen module compatibility first, move the roots, then close the floor.
X