Search code examples
amazon-web-servicesterraformamazon-ecs

How to use the moved block in Terraform to rename an ECS cluster and avoid forced replacement?


I have deployed an ecs and other resources such as its capacity provider among others in a unique way... now I have modified the module to deploy in map type which requires having a "name" for each ecs, it is possible to use the move block to tell terraform the new name?

moved {
  from = module.aws_ecs[0].aws_ecs_cluster.ecs_cluster
  to   = module.aws_ecs[0].aws_ecs_cluster.ecs_clusters["backend"]
}

After the terraform plan it tells me that it does a move but at the same time a replace (forces replacement) because the old name is different from the one I have displayed in aws.

Terraform will perform the following actions:

# module.aws_ecs[0].aws_ecs_cluster.ecs_clusters["backend"] must be replaced
  # (moved from module.aws_ecs[0].aws_ecs_cluster.ecs_cluster)
-/+ resource "aws_ecs_cluster" "ecs_clusters" {
      ~ arn      = "arn:aws:ecs:us-xxx-2:xxxxxx:cluster/analytics-test-pres-ecs" -> (known after apply)
      ~ id       = "arn:aws:ecs:us-xxx-2:xxxxxx:cluster/analytics-test-pres-ecs" -> (known after apply)
      ~ name     = "analytics-test-pres-ecs" -> "analytics-test-pres-backend-ecs" # forces replacement
      ~ tags     = {
            "Environment" = "xxx"
          ~ "Name"        = "analytics-test-pres-ecs" -> "analytics-test-pres-backend-ecs"

}

Solution

  • This plan is telling you two separate things about what has changed:

    1. The address that Terraform is using to track this object has changed from module.aws_ecs[0].aws_ecs_cluster.ecs_cluster to module.aws_ecs[0].aws_ecs_cluster.ecs_clusters["backend"], because you've changed the resource block to use for_each and added a moved block to hint Terraform to treat that as a new address for the existing object.
    2. The name attribute of the object itself has changed from "analytics-test-pres-ecs" to "analytics-test-pres-backend-ecs", presumably because you've changed the name argument in that resource block, and the remote API does not support changing the name of an ECS cluster so the provider's only recourse is to replace the object.

    There is no way to use a moved block to avoid the second change, because the second change is completely independent of how Terraform itself tracks this object and is instead caused by how the ECS API refers to this object. There is nothing Terraform or the hashicorp/aws provider can do to change the fact that the ECS API does not allow renaming an ECS cluster once it has been created.

    If you want to avoid replacing this object, there are two main options:

    • Define the name argument so that the string you're assigning is still "analytics-test-pres-ecs" for this object.

      I assume you're now defining this argument as something like "analytics-test-pres-${each.key}-ecs". To preserve the old name while still allowing new keys you'd need to make a special exception for the key backend, something like this:

      name = (
        each.key != "backend" ?
        "analytics-test-pres-${each.key}-ecs" :
        "analytics-test-pres-ecs"
      )
      

      This then makes your configuration explicit about how the system has evolved over time, in a similar way to how the moved block commemorated how you've changed the address Terraform uses to track this object, but as a rule for how to define name rather than as a rule for how Terraform should track the object.

    • Add a lifecycle block (if you don't already have one for this resource) and specify ignore_changes = [name] to tell Terraform that it should not attempt to respond to differences between the remote object's current name and the name defined in the current configuration.

      This means that Terraform will ignore the fact that the existing name differs from the configured name, but this rule will apply to all instances of the resource, so if you change the definition of name again in the future all of your existing objects will be retained with their old names, rather than just this one "backend" object. That might be desirable or not depending on your long-term goals.