How do I force terraform to recreate a resource?
Asked Answered
C

2

13

I have two resources:

resource "aws_lightsail_instance" "myserver-sig" {
  name              = "myserver-Sig"
  availability_zone = "eu-west-2a"
  blueprint_id      = "ubuntu_20_04"
  bundle_id         = "nano_2_0"
  key_pair_name     = "LightsailDefaultKeyPair"
}

and

resource "aws_lightsail_instance_public_ports" "myserver-sig-public-ports" {
  instance_name = aws_lightsail_instance.myserver-sig.name
  port_info {
    protocol  = "tcp"
    from_port = 443
    to_port   = 443
  }
  port_info {
    protocol  = "tcp"
    from_port = 80
    to_port   = 80
  }
  depends_on = [
    aws_lightsail_instance.myserver-sig,
  ]
}

When I first run terraform apply both resources are created.

If I want to replace the aws_lightsail_instance with a new version then the aws_lightsail_instance will redeploy, but the aws_lightsail_instance_public_ports will not because the ports haven't changed.

However as part of the deploy of aws_lightsail_instance it changes the public ports to close 443 and open 22. This means that the end state of the redeploy of the aws_lightsail_instance is that port 443 is closed.

If I run terraform apply again then it will correctly replace aws_lightsail_instance_public_ports opening port 443

How do I force a recreation of the aws_lightsail_instance_public_ports resource so that I only have to run terraform apply once?

Chestonchest answered 19/1, 2022 at 14:50 Comment(3)
Are you the only one running this code? Is there state file locking implemented?Constantina
I'm the only one running this code. It's the lightsail instance itself that starts with default ports open. Whenever you recreate a lightsail instance you always need to reconfigure the ports. This means that you can't lock the state to stop the instance from opening and closing it's own ports.Chestonchest
This seems like a bug in the AWS Terraform provider that you should submit to them. In the mean time running apply twice is probably the best option.Hedwig
L
21

You can use the lifecycle replace_triggered_by attribute to do this. This was introduced in Terraform 1.2.0 (released May 2022).

In your case to trigger the replace of aws_lightsail_instance_public_ports.myserver-sig-public-ports whenever aws_lightsail_instance.myserver-sig is replaced, add the following code to the aws_lightsail_instance_public_ports.myserver-sig-public-ports configuration:

resource "aws_lightsail_instance_public_ports" "myserver-sig-public-ports" {
  # ...

  lifecycle {
    replace_triggered_by = [
      aws_lightsail_instance.myserver-sig.id
    ]
  }
}

Thus, whenever the Lightsail instance is replaced, the public ports will automatically be triggered to be replaced.

If you want to always replace the public ports, even when the Lightsail instance has not been, replaced add this configuration:

resource "null_resource" "always_run" {
  triggers = {
    timestamp = "${timestamp()}"
  }
}

resource "aws_lightsail_instance_public_ports" "myserver-sig-public-ports" {
  # ...

  lifecycle {
    replace_triggered_by = [
      null_resource.always_run
    ]
  }
}
Lockup answered 28/12, 2022 at 20:53 Comment(0)
S
19

You can force the recreation (delete/create or -/+) by using the -replace=ADDRESS argument with terraform plan or terraform apply:

terraform apply -replace=aws_lightsail_instance_public_ports.myserver-sig-public-ports

This replaces the former workflow of terraform taint <resource_address> followed by a plan and apply. If you are using an older version of Terraform, then you would need to use taint instead:

terraform taint aws_lightsail_instance_public_ports.myserver-sig-public-ports
Streetman answered 19/1, 2022 at 16:3 Comment(8)
Is there a way to do it in main.tf? I just want to run terraform applyChestonchest
@Chestonchest How about adding new resource "aws_lightsail_instance_public_ports" "myserver-sig-public-ports-new" and comment the old one?Tynishatynwald
@Chestonchest My answer satisfies both of those. You are already doing it in the config containing the resource, and you are executing a terraform apply.Streetman
I want to execute terraform apply without any unusual arguments. I want a "one click" deployment where engineers don't have to think about odd edge cases. If I make that terraform command the new normal way of deploying then gradually more things will get added to the terraform command and in a year or two it'll be someone's job to know all of the edge cases required to deploy our infrastructure.Chestonchest
@Chestonchest I think you can comment out the resource in main.tf, apply it to destroy the resource, then uncomment and apply again to re-create it.Bessie
While this all may work technically, I totally agree with @Chestonchest that it's a non-satisfying solution to explicitly having to specify the resource name within the apply command, to trigger a recreation. There should be a "force_recreation" flag, or list option somewhere, that can be filled with terraform resources, which on apply should be deleted first, before doing the usual reconciliation.Winded
The taint way is probably still better than -replace as you can do that without having to modify the apply command.Maddi
You can user -out xxx flag for your plan, thus you will not be required to retype -replace=.... when you apply your plan. You will need just terraform apply xxxProvincialism

© 2022 - 2024 — McMap. All rights reserved.