Terraform: Rename an existing Resource causes it to be deleted and not replaced

Created on 16 Dec 2016  ยท  7Comments  ยท  Source: hashicorp/terraform

Hi there,

Thank you for opening an issue. Please note that we try to keep the Terraform issue tracker reserved for bug reports and feature requests. For general usage questions, please see: https://www.terraform.io/community.html.

Terraform Version

Terraform v0.8.1

Affected Resource(s)

Please list the resources as a list, for example:

  • AWS_CLOUDWATCH_METRIC_ALARM

However this may well effect multiple resources as the issue is with the resource rename.

Terraform Configuration Files

resource "aws_cloudwatch_metric_alarm" "ReportingServices_Redshift_Disk_Space" {
  alarm_name = "ReportingServices_Redshift_Disk_Space"
  comparison_operator = "GreaterThanOrEqualToThreshold"
  evaluation_periods = "5"
  metric_name = "PercentageDiskSpaceUsed"
  dimensions {
    ClusterIdentifier = "redshift.cluster.id"
  }
  namespace = "AWS/Redshift"
  period = "300"
  statistic = "Average"
  threshold = "75"
  alarm_description = "This metric monitors disk utilisation of redshift cluster"
  insufficient_data_actions = []
}

Expected Behavior

I first deploy the resource everything works. I then change the name of the resource to:

resource "aws_cloudwatch_metric_alarm" "ReportingServices_Redshift2_Disk_Space" {

I would expect terraform to re-create the resource.

Actual Behavior

It deletes the resource but does not replace it. Therefore the alarm disappears.

Steps to Reproduce

  1. terraform apply
bug provideaws

Most helpful comment

I noticed this (or something like it) with a route53 record resource. I renamed it, and Terraform kicked off the create of the new one and the destruction of the old one in parallel, so it ended up creating first and then destroying what it had created, because the identifying attributes from an AWS perspective had not changed.

Short of Terraform actually recognising that two resources are in fact the same thing (and that a rename has occurred), which would be nice (and feasible in principle), it might be good if it destroyed all resources of a given type before creating any new resources of the same type, thus avoiding the possibility of this happening... or at least make this behaviour optional.

All 7 comments

Terraform tracks resources by their name. If you change the name, you have _created a new resource_ and _deleted the old resource_. There is no way for Terraform to tell you changed the name of an existing resource since its all just text. :)

You can simulate this behavior by using terraform state mv to rename the resource in the state. Otherwise, there isn't a reliable way for Terraform to automatically do this. If you have any ideas though, I'm open to it.

The issue is Terraform doesn't recreate the resource, it just deletes the existing one but doesn't re-create a new one.

@philltomlinson I reopened this since, as you said, it seems that there's more to this than first met the eye.

If you're able, I think it would help to see what the output of terraform plan looks like when you're in the state of having just renamed the resource but not yet applied that change. If Terraform is working as expected there should be both a create and a destroy diff in the plan, but based on what you've described it seems like we'd see it show just the destroy plan, and possibly some other output that might be a clue as to what Terraform thinks it's doing here.

Correct @apparentlymart please find attached what happens. So I change the name of an existing resource and the following is output when I run "terraform plan"

C:\git\awsservices\CCSAWS_ReportingServices\resources\terraform>terraform plan -var "aws_region=eu-west-1" -var "promotion_level=dev" -var "nest=phill"
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but
will not be persisted to local or remote state storage.

aws_cloudwatch_metric_alarm.ReportingServices_Redshift_Disk_Space: Refreshing state... (ID: dev_phill_ReportingServices_Redshift_Disk_Space)
aws_cloudwatch_metric_alarm.ReportingServices_Lambda_Errors: Refreshing state... (ID: dev_phill_ReportingServices_Lambda_Errors)
aws_cloudwatch_metric_alarm.ReportingServices_Tableau_ELB_Health_Check: Refreshing state... (ID: dev_phill_ReportingServices_Tableau_ELB_Health_Check)
aws_cloudwatch_metric_alarm.ReportingServices_Redshift_Health_Check: Refreshing state... (ID: dev_phill_ReportingServices_Redshift_Health_Check)

The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed. Cyan entries are data sources to be read.

Note: You didn't specify an "-out" parameter to save this plan, so when
"apply" is called, Terraform can't guarantee this is what will execute.

+ aws_cloudwatch_metric_alarm.ReportingServices_Redshift2_Disk_Space
    actions_enabled:              "true"
    alarm_description:            "This metric monitors disk utilisation of redshift cluster"
    alarm_name:                   "dev_phill_ReportingServices_Redshift_Disk_Space"
    comparison_operator:          "GreaterThanOrEqualToThreshold"
    dimensions.%:                 "1"
    dimensions.ClusterIdentifier: "redshift.cluster.id"
    evaluation_periods:           "5"
    metric_name:                  "PercentageDiskSpaceUsed"
    namespace:                    "AWS/Redshift"
    period:                       "300"
    statistic:                    "Average"
    threshold:                    "75"

- aws_cloudwatch_metric_alarm.ReportingServices_Redshift_Disk_Space


Plan: 1 to add, 0 to change, 1 to destroy.

Then when I run the apply it says its deletes a resource and adds a resource however it appears to only perform the destory, meaning the rename of the resource has actually deleted it

C:\git\awsservices\CCSAWS_ReportingServices\resources\terraform>terraform apply -var "aws_region=eu-west-1" -var "promotion_level=dev" -var "nest=phill"
aws_cloudwatch_metric_alarm.ReportingServices_Redshift_Disk_Space: Refreshing state... (ID: dev_phill_ReportingServices_Redshift_Disk_Space)
aws_cloudwatch_metric_alarm.ReportingServices_Tableau_ELB_Health_Check: Refreshing state... (ID: dev_phill_ReportingServices_Tableau_ELB_Health_Check)
aws_cloudwatch_metric_alarm.ReportingServices_Lambda_Errors: Refreshing state... (ID: dev_phill_ReportingServices_Lambda_Errors)
aws_cloudwatch_metric_alarm.ReportingServices_Redshift_Health_Check: Refreshing state... (ID: dev_phill_ReportingServices_Redshift_Health_Check)
aws_cloudwatch_metric_alarm.ReportingServices_Redshift_Disk_Space: Destroying...
aws_cloudwatch_metric_alarm.ReportingServices_Redshift2_Disk_Space: Creating...
  actions_enabled:              "" => "true"
  alarm_description:            "" => "This metric monitors disk utilisation of redshift cluster"
  alarm_name:                   "" => "dev_phill_ReportingServices_Redshift_Disk_Space"
  comparison_operator:          "" => "GreaterThanOrEqualToThreshold"
  dimensions.%:                 "" => "1"
  dimensions.ClusterIdentifier: "" => "redshift.cluster.id"
  evaluation_periods:           "" => "5"
  metric_name:                  "" => "PercentageDiskSpaceUsed"
  namespace:                    "" => "AWS/Redshift"
  period:                       "" => "300"
  statistic:                    "" => "Average"
  threshold:                    "" => "75"
aws_cloudwatch_metric_alarm.ReportingServices_Redshift_Disk_Space: Destruction complete
aws_cloudwatch_metric_alarm.ReportingServices_Redshift2_Disk_Space: Creation complete

Apply complete! Resources: 1 added, 0 changed, 1 destroyed.

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path: terraform.tfstate

Outputs:

debug_variables = AWS Region 'eu-west-1' Promotion level 'dev' and nest 'phill'

To clean up I have to complete remove the resource from the actual terraform code run apply then re-insert the code and perform apply.

Hi @philltomlinson,

I don't know the aws_cloudwatch_metric_alarm resource very well but from a quick skim of the code it looks like the alarm_name attribute is used as the unique identifier for each of these in the underlying API, and that this is an example of a particular tricky class of API for Terraform's purposes where the operation for "Create" and the operation for "Update" are really the same operation "Put".

One consequence of that is that Terraform can't tell if it's being asked to "create" a resource that already exists... it will just quietly overwrite it, thinking it was a create.

So that then leads to the situation you've seen here: a race condition where Terraform has two resource instances corresponding to the same AWS object...

  • Create an alarm with name dev_phill_ReportingServices_Redshift_Disk_Space (actually replaces existing resource, but Terraform doesn't know)
  • Delete the existing alarm with name dev_phill_ReportingServices_Redshift_Disk_Space (actually deletes the same resource we just created, since they are the same physical object in the API)

If this is indeed the cause then that makes this issue specific to this resource, though certainly there are other resources that exhibit the same problem for similar reasons. The workaround for now is to make sure that the metric_name argument changes whenever the resource itself changes, to avoid the issue where the two operations collide on the same underlying API object.

I noticed this (or something like it) with a route53 record resource. I renamed it, and Terraform kicked off the create of the new one and the destruction of the old one in parallel, so it ended up creating first and then destroying what it had created, because the identifying attributes from an AWS perspective had not changed.

Short of Terraform actually recognising that two resources are in fact the same thing (and that a rename has occurred), which would be nice (and feasible in principle), it might be good if it destroyed all resources of a given type before creating any new resources of the same type, thus avoiding the possibility of this happening... or at least make this behaviour optional.

I'm going to lock this issue because it has been closed for _30 days_ โณ. This helps our maintainers find and focus on the active issues.

If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

Was this page helpful?
0 / 5 - 0 ratings