Migrate easily from count to for-each

Anyone ever changed tfstate from count to for_each where the terraform.exe was ran from a remote build agent and not a local laptop? This involves switching from integer to tag index on SEVERAL resources that are production and outages are a concern.

Hi @benniehanas,

One way to deal with this is to use moved blocks, which are part of Terraform’s refactoring features.

Terraform cannot guess the relationship between your numeric indices and your new string keys, so you will need to describe each instance separately to explain to Terraform how to migrate between them.

moved {
  from = aws_ram_principal_association.acct_assoc_east_2[19]
  to   = aws_ram_principal_association.acct_assoc_east_2["something"]
}

If the rule for translating between indices and keys is something systematic that you can express as code, then you could potentially write a script to generate a set of moved blocks so that you don’t need to write them all out manually. If you choose to do that, then you might prefer to use Terraform’s JSON configuration syntax instead of the native syntax, since that’s usually easier to generate in other programming languages:

{
  "moved": [
    {
      "from": "aws_ram_principal_association.acct_assoc_east_2[19]",
      "to": "aws_ram_principal_association.acct_assoc_east_2[\"something\"]"
    },
    {
      "from": "aws_ram_principal_association.acct_assoc_east_2[20]",
      "to": "aws_ram_principal_association.acct_assoc_east_2[\"other\"]"
    }
  ]
}

If you generate the above in a file with a .tf.json suffix in the same directory as your module containing this resource, Terraform should understand it as having the same meaning as two moved blocks with the from and to addresses indicated.

When writing a script for this it might be useful to start from information in your current Terraform state. If so, you can use terraform show -json to produce a JSON object that you can load in your favorite programming language to see which objects already exist. The structure of it is the JSON values representation, and for your purposes here I expect the logic would be something like this:

  • Find the object representing the module that contains these resource instances.
  • Scan that object’s resources array, searching for any objects where "mode" is "managed", "type" is "aws_ram_principal_association", and "name" is "acct_assoc_east_2".
  • For each matching object, "index" contains the current index, and "values" has the current state information for that object. Use that information to decide which string key is appropriate for that object, and generate a moved object like in my above example mapping from the current index to the new index.

I would suggest writing a script for this only if you have a large number of instances to migrate.

1 Like