Terraform destroys resources in incorrect order when dynamic blocks are in use

I am having an issue where Terraform is destroying resources in the incorrect order. The issue seems to always arise when dynamic blocks are used.

The usecase I have now is conditionally creating a resource and assigning it to another resource.

Example:

resource "aws_cloudfront_distribution" "x" {
   ..parameters omitted..

  default_cache_behavior {
    dynamic "function_association" {
      for_each = var.create_cf_function == true ? [1] : []

      content {
        event_type = "viewer-request"
        function_arn = aws_cloudfront_function.x[0].arn
      }
    }
  }
}

resource "aws_cloudfront_function" "x" {
  count = var.create_cf_function == true ? 1 : 0

  name = "My Function"
  public = true
  code = "console.log(1);" 
}

When creating the resources, terraform first creates the cloudfront function and then associates the function to the distribution. This is as expected.

However, during destroy (or if var.create_cf_function is false), terraform attempts to destroy the function first.

Expectation:
Terraform should first disassociate the function from the distribution, and then attempt to delete the function.

I am not sure if this is something I am doing wrong or if the issue goes deeper.

I have tried adding an explicit depends_on on the distribution for the function. But that did not fix the issue.

Hi @rashid,

I don’t think what you are describing could be directly caused by dynamic blocks, but I think the fact that function_association is a nested block type inside another resource – rather than a resource type in its own right – is part of the explanation for why this is happening.

Specifically, I think the problem here is that from Terraform Core’s perspective removing a function_association block from aws_cloudfront_distribution.x looks like an in-place update of the containing resource, rather than as something being deleted. Therefore the created plan includes (at least) two actions:

  • Update aws_cloudfront_distribution.x to now have zero function_association blocks.
  • Delete aws_cloudfront_function.x altogether.

The interaction between an update and a delete is one of those things where the “correct” ordering varies depending on the situation. In this case by thinking about how the remote system behaves we can clearly see that the function must be disassociated from the CloudFront distribution before destroying it, but for other resource types representing other API designs the reverse is true.

Unfortunately I don’t have a good suggestion for how to avoid this problem with the way these resource types are currently designed. If the association between a CloudFront Distribution and a CloudFront Function were itself represented by a resource then I expect Terraform would deduce the ordering correctly, but because the provider represents the function association as if it’s just an argument of the distribution itself the ordering is ambiguous and Terraform is reaching the wrong conclusion for this situation.

Hi @rashid,

As mentioned already, the natural order that Terraform assumes is to destroy resources before replacing them or updating dependencies. If the aws_cloudfront_distribution resource needs to be updated before the aws_cloudfront_function resource is deleted, then you must use the create_before_destroy lifecycle option to instruct terraform that the inverse order is desired.