Depends_on with for_each not working as expected

I have the following (simplified) terraform file to setup a google load balancer with google managed certificates:

locals {
  domains = ["test1.example.com", "test2.example.com"]
}

resource "google_compute_managed_ssl_certificate" "certificate" {
  for_each = toset([for _, domain in local.domains: domain])
  name = "ssl-cert-${replace(each.key, ".", "-")}"
  managed {
    domains = [each.key]
  }
}

resource "google_compute_target_https_proxy" "https_proxy" {
  name       = "https-proxy"
  url_map    = google_compute_url_map.load_balancer.id
  ssl_certificates = [for certificate in google_compute_managed_ssl_certificate.certificate: certificate.self_link]
  depends_on = [
    google_compute_managed_ssl_certificate.certificate
  ]
}

resource "google_compute_url_map" "load_balancer" {
  name            = "load-balancer"
  default_url_redirect {
    https_redirect = true
    host_redirect = "example.com"
    redirect_response_code = "MOVED_PERMANENTLY_DEFAULT"
    strip_query = true
  }
}

The initial provisioning of the resources works as expected however if I remove one of the domains from local.domains terraform has a problem correctly handling the dependency.

For example when changing local.domains to ["test1.example.com"], terraform will try to destroy the managed certificate before modifying google_compute_target_https_proxy.https_proxy resulting in an error because the certificate is still in use.

Am I missing something in my configuration?

1 Like

A similar question was recently asked: Removing OCI security list and updating associated subnet

However I don’t really understand the answer given there - I had it on my list of things to investigate later.

Hi @dedalusj,

What is missing here (like in the linked thread) is that you need to use create_before_destroy on the certificate in order to ensure that dependent resources are updated before the certificate is destroyed.

The depends_on is redundant in your example, and does nothing because there is already a reference to google_compute_managed_ssl_certificate.certificate creating the dependency.

1 Like

This usage of create_before_destroy seems very different to the one in the documentation: The lifecycle Meta-Argument - Configuration Language | Terraform by HashiCorp

I’d never have expected from that, that it would influence ordering of updates of other resources.

Both use cases are actually the same thing, just from a different perspective – the feature could just as easily have been called “create and update before destroy”. The goal here is to update the google_compute_target_https_proxy before destroying the google_compute_managed_ssl_certificate, and regardless of whether you are updating the google_compute_target_https_proxy to a replacement google_compute_managed_ssl_certificate or updating it to remove the certificate, you want that to happen before the old certificate is destroyed.

While the obvious use of always ensuring an instance exists was the primary reason for the feature’s implementation, the ordering of updates seems to be the more interesting use case for many common resources.

You are right though, that documentation is probably long overdue for some updates

Thanks, using create_before_destroy does solve the problem.

However it is not clear at all why using it on google_compute_managed_ssl_certificate influence the order of operation with respect to google_compute_target_https_proxy.

Furthermore if terraform already knows that google_compute_target_https_proxy depends on google_compute_managed_ssl_certificate shouldn’t graph traversal dictate that updates to google_compute_target_https_proxy are performed before destroy operations in google_compute_managed_ssl_certificate? Cases where I would want destruction of dependent resources to be performed before updates seem to be the exception rather than the most common case.

You could actually put create_before_destroy in either resource and have the same effect, since adding it to the proxy would force the ordering on the certificate.

The “common case” was decided long ago when Terraform was first designed and destroy-then-create was implemented as the default replacement ordering. This has worked for the vast majority of use cases and avoided some problems inherent with the inverse case, but the decision is largely arbitrary. If the original default was the inverse, then we would just have a different set of exceptions, and similar threads like this explaining when to use destroy_before_create instead :wink:

Since you mentioned the graph traversal, while the decision of which order to use as a default was arbitrary, the relative order of operations is forced by the graph. I made some notes here if you are interested in how these are structured.

1 Like

Thanks that was helpful