stonewalker747
stonewalker747

Reputation: 151

Terraform lifecycle - how to prevent terraform from destroying existing resources on terraform apply

I’ve set a for_each loop in terraform IaC to create a list of resources when a terraform apply is ran with the list passed in as an input variable. So something like terraform apply -var-file “apps=[app,app2]”.

What I’m finding is that for each time a new apply is ran it loses the state of the previous applies, and overrides everything created, only to destroy the previous apps deployed. So for example, when the array above changes and there is one item listed, the second item is destroyed on the apply.

I’m unsure which, if any, lifecycle hook to use for something like this so it will only create what is not existing in state or update what is already existing, without destroying resources if they already exist but aren’t passed into the array. Any insight regarding this?

The basic code I’m using is similar to what’s on the terraform site for the for_each loop example:

locals {
  subnet_ids = toset([
    "subnet-abcdef",
    "subnet-012345",
  ])
}

resource "aws_instance" "server" {
  for_each = local.subnet_ids

  ami           = "ami-a1b2c3d4"
  instance_type = "t2.micro"
  subnet_id     = each.key 

  tags = {
    Name = "Server ${each.key}"
  }
}

Upvotes: 2

Views: 4547

Answers (2)

Ricky Levi
Ricky Levi

Reputation: 8007

Per documentation, you may want to try prevent_destroy option https://developer.hashicorp.com/terraform/tutorials/state/resource-lifecycle

resource "aws_instance" "example" {
  ...
  
  lifecycle {
    prevent_destroy = true
  }
}

Upvotes: 1

Marko E
Marko E

Reputation: 18223

This is a normal and expected behavior in Terraform. When adding/removing variable values which are used in creating resources, the state file is checked:

When Terraform creates a remote object in response to a change of configuration, it will record the identity of that remote object against a particular resource instance, and then potentially update or delete that object in response to future configuration changes.

If one of the variables on which resource creation depends on is removed, the resource itself will also be removed.

Variable values play an important role in the case where the change means a resource will be destroyed and recreated, which is the case when changing the subnet ID. It is sometimes useful to read the CloudFormation documentation about what change will cause what outcome [1]. In the case when a subnet ID is changed, the resource will have to be destroyed and recreated, or in CloudFormation terminology:

Update requires: Replacement

If I understand your question, you want to keep the previously created resources and only add new ones. If that is the case, removing any of the values in the list will cause the resource that used one of the values as a key to be removed. For example, in your current code, two resource instances will be created with the respective keys of subnet-abcdef and subnet-123456:

aws_instance.server["subnet-abcdef"]
aws_instance.server["subnet-123456"]

Since you are passing the variable values using the CLI, that means that if you omit any of the two, Terraform will treat that as if the resource with that particular key needs to be removed. In other words, the variable values for previously created resources always have to be present in the CLI call. In that case, it is much better to use a file where the variable values will be stored and where you can control what gets passed to the plan and apply steps. For example, you could create a variables.tf and terraform.tfvars files and add the following:

# variables.tf
variable "subnet_ids" {
  descirption = "Subnet IDs in which an EC2 instance will be created."
  type        = list(string)

}

# terraform.tfvars
subnet_ids = [
  "subnet-abcdef",
  "subnet-012345",
]

And then finally:

resource "aws_instance" "server" {
  for_each = toset(var.subnet_ids)

  ami           = "ami-a1b2c3d4"
  instance_type = "t2.micro"
  subnet_id     = each.key 

  tags = {
    Name = "Server ${each.key}"
  }
}

This way you don't have to worry if you have provided all the same values on the CLI or not. If you need to add something, you just add it to terraform.tfvars file and all the other resources will persist across changes. The same applies when you want to remove a resource: just remove the value from the list in the terraform.tfvars file.


[1] https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-instance.html#cfn-ec2-instance-subnetid

Upvotes: 2

Related Questions