Replace_triggered_by on conditionally created resources

So this is an interesting one, and I am not sure how to go about it. I believe it could be a bug in the implementation, but figured I would ask the community first.

I recently opened [Bug]: aws_batch_compute_environment: launch_template version known after apply does not ForceNew · Issue #37440 · hashicorp/terraform-provider-aws · GitHub because we are seeing that launch_template updates do not properly trigger a replacement for the immutable field in an aws_batch_compute_environment.

Our solution when we came across this bug was to use replace_triggered_by, which fixed the issue for the state calling the module that was running into issues.

However, we now have issues with other states using the batch module–ones that DO NOT have a launch template.

My question is–is there a way to set replace_triggered_by to evaluate conditionally if the resource exists, but ignore it if it doesnt? Here is some example code to help provide a clearer picture:

resource "aws_batch_compute_environment" "default" {
  for_each = var.compute_environments

  dynamic "launch_template" {
    for_each = contains(["EC2", "SPOT"], lookup(each.value, "compute_type", var.compute_type)) && length(lookup(each.value, "launch_template", var.launch_template)) > 0 ? [aws_launch_template.default[each.key]] : []
    content {
      launch_template_id = launch_template.value.id
      version            = launch_template.value.default_version
    }
  }

  lifecycle {
    replace_triggered_by = [aws_launch_template.default[each.key]]
  }
}

resource "aws_launch_template" "default" {
  for_each = {
    for k, v in var.compute_environments : k => lookup(v, "launch_template", var.launch_template) if length(lookup(v, "launch_template", var.launch_template)) > 0
  }
  # ... removed for brevity
}

With the above code, we create launch templates and associate them with the batch compute environment if and only if the user calling the module specifies a launch template. Otherwise, we leave it blank, as AWS does not require a launch template for a batch compute environment.

The issue here is that when we have a state that DOES NOT have launch templates, we get an error because replace_triggered_by is pointing at an object that does not exist. The error is:

╷
│ Error: no change found for aws_launch_template.default["foo"] in module.batch
│
│
╵

Is this expected behavior? Is there a way to avoid this and have Terraform only evaluate the replace_triggered_by when the object inside exists? I tried various expressions but they are not allowed inside the lifecycle block.

Thanks in advance for any support you can provide!

Hi @nomeelnoj1,

That’s an interesting situation, and I think what you’re asking would be still covered by one of the use cases for the terraform_data utility resource.

Because replace_triggered_by needs to be a list of references as opposed to values, there’s currently no way to dynamically process that as it needs to be static. What you could do however is point those references at an intermediary resource where you can do that data processing.

I think the simplest change might look something like this:

resource "terraform_data" "launch_templates" {
  # this way the terraform_data.launch_templates keys always align with 
  # aws_batch_compute_environment
  for_each = var.compute_environments
  input = try(aws_launch_template.default[each.key].id, null)
}
...
  # then within aws_batch_compute_environment you can point to the proxy
  # resource to look for changes.
  lifecycle {
    replace_triggered_by = [terraform_data.launch_templates[each.key]]
  }

that is so funny–while you were writing this, this is basically verbatim the exact solution we came up with!

I also looked at using $Latest as the launch template version, but despite AWS docs saying that the compute environments are reevaluated when the LT updates, this is not the behavior we saw in AWS with a compute environment that had an invalid LT–it took recreating the compute environment for the job to launch.

Thanks so much for your reply!