The task looked small: a module managing part of the infrastructure needed a version bump to pick up a permissions fix that had, for the moment, been applied by hand instead of through code. Manual changes drifting out of version control is its own kind of debt, so rolling the fix into the actual module and letting it deploy properly was the obvious next step. Bump the version, run the plan, done.
The plan that came back proposed destroying and recreating the storage volumes attached to a live, stateful cluster.
Reading past the summary line
Not a modification. A replace: destroy, then create. For data volumes. Attached to something currently serving traffic. That’s the kind of line in a plan output that’s worth stopping everything to understand before doing anything else, because “apply it and see” is not a real option when the resource in question holds data you can’t casually regenerate.
The module had, somewhere between the version already in use and the version being bumped to, changed how it internally addresses the resources it manages: an older scheme identified each one by its position in a list, a newer scheme identified them by an explicit name instead. Both schemes can point at the exact same underlying resource, logically. Nothing about the resource itself needed to change. But the tool managing infrastructure state doesn’t know that two different addressing schemes are describing the same thing unless something explicitly tells it so. From its point of view, the resource at the old list-position address simply doesn’t exist anymore under the new addressing scheme, and the resource at the new named address doesn’t exist yet either. The only way it knows how to reconcile “doesn’t exist” with “should exist” is to destroy the (as far as it knows) orphaned one and create a fresh one to take its place.
Applying that plan as written would have taken a live cluster’s storage out from under it to fix an access-control policy that had nothing to do with storage at all.
What actually happened
That version bump did not go in as planned. The permission fix that prompted the whole thing got applied by hand instead, deliberately kept out of the module for the moment, accepting that it would live outside version control a bit longer as a smaller, known risk than the alternative. The addressing migration needed to happen first, and safely, which means explicitly telling the state tracker that the old address and the new address refer to the same resource, one at a time, verified, before the module version bump that assumes that mapping already happened gets applied on top of it. That’s its own project, deliberately kept separate rather than rushed through in the same sitting just because the trigger for looking at it was small.
The part worth carrying forward
A one-line version bump is not a small change by virtue of being one line. Its size is determined by what the new version actually does differently underneath, not by how much you had to type to request it, and the only reliable way to find that out is reading the actual plan output in full rather than trusting that a routine-sounding bump produces a routine-sized diff. The specific failure mode here, an internal addressing scheme changing between versions, is easy to miss because it’s invisible from the outside: the resource is logically identical before and after, so there’s no reason to suspect anything until the plan spells out, in exactly these terms, that it intends to delete something and rebuild it. That line is the entire warning. Reading past it is the entire risk.