State move but terraform still wants to destroy and create the resource

What could be causing that ?
I refactored a module that creates an ec2 instance a lot of supplementary resources

Now I would like those resources to be managed by the new module so I moved them using terraform state move.

Example

Move "module.devops-infrastructure-al2[0].module.kms_keys.aws_kms_alias.this" to "module.aws_instances[0].module.kms_keys.aws_kms_alias.this"
Successfully moved 1 object(s).

My goal is to get to the stage where the plan is not trying to create nor destroy anything and I would appreciate advice

I was expecting plan to not show any changes rather than wanting to destroy and create the resource

The plan says it will be destroying instance 0 (a number) and creating instance “0” (a string). That’s where the mismatch between configuration and state is.

1 Like

Hmm it is suspiscious and I need to look into it and figure out why one is a string and one is a number. I am using a instance_count variable that is a number for both modules.

If that would be the case it would be great

When calling the module I use index maybe there is the problem and i need to get this index into an int instead of string

module "aws_instances" {
  for_each = {
    for index, ec2 in var.ec2_instances : index => ec2
  }

Whenever you use for_each, the resource index will always be a string.

For the resource indexes to be numbers, you would need to use count instead:

module "aws_instances" {
  count = length(var.ec2_instances)

  # You would also need to change each use of:
  #    each.value
  # into
  #    var.ec2_instances[count.index]

I should add, however, that it is almost always better to use for_each rather than count.

The only time you should use count is when all of the instances are completely identical to each other - however since you’re defining them via var.ec2_instances which seems to be a list, it suggests different items in the list have different properties.

The reason for this, is that if you later need to add/remove items from var.ec2_instances in a way that makes other items shift position in the list, Terraform will want to perform undesirable changes.

Let’s say you have [0], [1], and [2]. You decide to remove [1] but want to keep [2]. Terraform will now either destroy both [1] and [2], and then build a new [1] based on the old definition of [2] - or it will destroy [2] and try to in-place modify the old [1] to be like the definition of [2].

Therefore you should use for_each with meaningful names, unless all of the instantances are equivalent.

You are right max all the instances are different so I went with for_each.

Count is not a good option

So it seems that the only way to replace those instances is for them to actually be destroyed ?

It really sucks because all the attributes are identical except of the index being a string.

Is there no option to use for_each but still be able to have the index as an integer?

No, but why would you even want that? In the earlier part of your message you acknowledged that they are of different types.

If you really want to refer to your for_each instances via strings of “0” ”1” and “2”, then you can - but you’ll need to use terraform state mv again to add in the quotes around the strings.

The thing is I definitely don’t want those instances to go down - my goal after state moves is a plan with no changes

And it seems like the destruction/recreation is forced because of mismatch

The plan says it will be destroying instance 0 (a number) and creating instance “0” (a string). That’s where the mismatch between configuration and state is.

So I thought maybe if I used for each but referred to them via integer somehow I would be able to get a plan that sees no changes rather than forcing the destruction and creation of instances.

The problem is that you did this:

You needed to include quotes around the 0 in the move destination. So now you will need to move them again to add the quotes.

You are a legend max this was the issue.
Thank you so much!

Hey Max,
Really appreciate you help here!
I have a module using count attribute
Purpose of module is to first check for user in a gcp group & then add him if not available.
Its working fine when terraform deployment is successful but when I retry terraform deployment, it tries to destroy the resource under that module.

So I put lifecycle (prevent destroy = true) but I am getting below error:

Error: Instance cannot be destroyed

on …/terraform-modules/group_member_addition/main.tf line 12:

12: resource “googleworkspace_group_member” “new_member” {

Resource module.member_addition[0].googleworkspace_group_member.new_member has lifecycle.prevent_destroy set, but the plan calls for this resource to be destroyed. To avoid this error and continue with the plan, either disable lifecycle.prevent_destroy or reduce the scope of the plan using the -target flag.