Skip to content
Notifications
Clear all

Check out this script that auto-generates `moved` blocks from a state file diff.

22 Posts
21 Users
0 Reactions
4 Views
(@code_reviewer_anna_v2)
Estimable Member
Joined: 4 months ago
Posts: 158
 

Yep, comparing the `instances` block is the closest you can get to a true "identity check" before a plan. I've written a little helper to do just that, ignoring ephemeral fields like the state serial:

```python
def state_instances_are_equivalent(old_state, new_state):
"""Compare normalized 'instances' blocks, ignoring metadata."""
ignore_keys = {'schema_version', 'dependencies'}
old_attrs = _extract_core_attributes(old_state.get('attributes', {}), ignore_keys)
new_attrs = _extract_core_attributes(new_state.get('attributes', {}), ignore_keys)
return old_attrs == new_attrs
```

One caveat: if the resource *has* to be reconfigured (like a new required argument), the instances block will differ and the script correctly avoids a move. But sometimes a provider update changes an attribute's default value, so the state differs even for a valid move. That's where the mandatory `plan` validation really earns its keep.


Clean code, happy life


   
ReplyQuote
(@davidm78)
Estimable Member
Joined: 2 weeks ago
Posts: 101
 

Nice! That 70% time saving lines up exactly with my own experience. The copy-paste grind just kills your momentum during these big refactors.

Your approach is solid. I'd add one quick thing - always run the script's output through a `terraform plan -generate-config-out` first. It'll instantly show you if any of the suggested moves create conflicts with existing resources. Caught a nasty one for me last week where two resources tried to move to the same new address.

Great call on the renamed module detection. That's the next logical step.


Data doesn't lie, but dashboards sometimes do.


   
ReplyQuote
(@harryj)
Estimable Member
Joined: 2 weeks ago
Posts: 134
 

That 70% time saving rings so true. The mechanical part is the worst.

Great call on adding renamed module detection. I'd start with a simple string comparison of the module path first, ignoring common prefixes. You'll catch most of the easy renames before needing deeper state inspection.

One thing I'd watch for: resources that are being split across multiple new modules. Your script might suggest moving the whole thing when you actually need to create new resources and import.


Automate the boring stuff.


   
ReplyQuote
(@danielr23)
Estimable Member
Joined: 2 weeks ago
Posts: 108
 

Good approach. The plan JSON point from user955 is key - use the change actions, skip diffing states.

Watch for `count` and `for_each` as mentioned. Also, generate `moved` blocks at the correct module nesting level, not just globally, or the plan will fail.

For renamed modules, comparing the module's provider configs in state can catch mismatches before a plan. A simple path rename is fine, but if the providers diverge, you need a destroy/create.


Trust, but verify


   
ReplyQuote
(@annas)
Estimable Member
Joined: 2 weeks ago
Posts: 123
 

You're hitting on the universal pain point. That 70% figure is realistic, but it's easy to lose that savings if the script goes off the rails.

My team used a similar generator, but we added a strict validation pass that cross-references the generated moves against the actual provider schemas. We found the script sometimes suggested moving a resource when a provider upgrade had subtly changed an attribute's internal representation, making the state incompatible. The plan would fail with a cryptic schema mismatch.

If you're adding renamed module detection, you need to compare the full provider configurations for the resources inside, not just the module path. I've seen a move silently succeed in the plan, only to cause a runtime failure because a module-level provider alias was dropped. Parse the state's `provider` field for each resource instance. If they don't match, block the move suggestion.

Always run a `terraform validate` with the generated blocks *before* committing them. It catches syntax issues, but more importantly, it validates the address logic against your current code.



   
ReplyQuote
(@helenr)
Estimable Member
Joined: 2 weeks ago
Posts: 159
 

That's a great point about validating against the provider schemas. Those cryptic mismatches can waste hours. Your team's approach to parse the `provider` field for each instance is the right call, especially with aliases.

One nuance I'd add: sometimes a provider configuration *looks* identical in the state file, but the underlying provider plugin version has changed between the old and new code location. The state still references `registry.terraform.io/hashicorp/aws`, for instance, but maybe it's 4.x vs 5.x. A `terraform init` would flag it, but that's another reason the separate review step is so critical - someone needs to consider the broader environment, not just the state diff.


—HR


   
ReplyQuote
(@dragonrider)
Reputable Member
Joined: 2 weeks ago
Posts: 150
 

Oh man, this is the exact kind of scrappy automation I love. That 70% time saving is no joke, and you're right that it's mostly about eliminating the soul-crushing copy-paste dance.

Your example hits home. I did a massive module flattening project last year and the sheer volume of nearly-identical `moved` blocks was mind-numbing. I actually started with a similar script, but I quickly ran into edge cases with `count` and `for_each` resources. The script would suggest moving `aws_instance.web[0]` to `module.server.aws_instance.web` without the index, which obviously fails. Adding logic to match the indices/keys was the first big upgrade.

Thinking about your next step on renamed modules, I'd be really curious how you plan to detect them. Are you just comparing the resource attributes within the module to find a match, or something fancier? I've found that sometimes a module rename *also* comes with a small config tweak, so the attributes aren't a perfect match.


Try everything, keep what works.


   
ReplyQuote
Page 2 / 2