terraform-provider-google: [google-beta provider]: compute_managed_ssl_certificate unable to create cert with alternative name

Community Note

  • Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritize this request.
  • Please do not leave +1 or me too comments, they generate extra noise for issue followers and do not help prioritize the request.
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment.
  • If an issue is assigned to the modular-magician user, it is either in the process of being autogenerated, or is planned to be autogenerated soon. If an issue is assigned to a user, that user is claiming responsibility for the issue. If an issue is assigned to hashibot, a community member has claimed the issue already.

Terraform Version

v0.11.13 (we cannot upgrade right now due to breaking changes at the v0.12 level)

Affected Resource(s)

  • google_beta
  • compute_managed_ssl_certificate

Terraform Configuration Files

Working:

resource "google_compute_managed_ssl_certificate" "default" {
  provider = "google-beta"
  name     = "test-cert"
  project  = "my-gcp-project"

  managed {
    domains = [
      "foo.domain.org",
    ]
  }
}

Broken:

resource "google_compute_managed_ssl_certificate" "default" {
  provider = "google-beta"
  name     = "test-cert"
  project  = "my-gcp-project"

  managed {
    domains = [
      "foo.domain.org",
      "foo2.domain.org",
    ]
  }
}

Debug Output

N/A

Panic Output

N/A

Expected Behavior

A cert can be created with more than one name covered on it (Alternative Names/ANs). Certs can be created on GCP using the gcloud command. For the above example, if I wanted to create a cert covering foo.domain.org and foo2.domain.org, I would use:

gcloud beta compute ssl-certificates create test-cert --domains="foo.domain.org","foo2.domain.org"

And it would succeed:

Created [https://www.googleapis.com/compute/beta/projects/my-gcp-project/global/sslCertificates/test-cert].
NAME       TYPE     CREATION_TIMESTAMP             EXPIRE_TIME  MANAGED_STATUS
test-cert  MANAGED  2020-07-07T05:07:45.387-07:00               PROVISIONING
    foo.domain.org: PROVISIONING
    foo2.domain.org: PROVISIONING

However, when you use the Terraform file with more than one name on the domains, even though the documentation (link at end of report) says it supports up to 100 domains, it refuses to accept the additional domains:

$ terraform plan -out tfplan

Error: google_compute_managed_ssl_certificate.default: managed.0.domains: attribute supports 1 item maximum, config has 2 declared

Actual Behavior

Provider refuses the second domain even though it is supported underneath.

Steps to Reproduce

  1. terraform plan and terraform apply the “working” example earlier, updating the project and credentials values. It will create a SSL cert to “foo.domain.org” (it won’t provision, but that’s not the problem here)
  2. Destroy the cert using terraform destroy
  3. terraform plan the “broken” example – it will fail the plan with the message “attribute supports 1 item maximum, config has 2 declared”

Important Factoids

N/A

References

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 18 (7 by maintainers)

Most upvoted comments

@huang-jy yes, great point! i brought this up to my team, and this didn’t even cross my mind, but we can join the list together, and that should work fine, so just change that block to

locals {
  len_certs = length(var.alternative-cert-names)
  domain_names = join(", ", var.alternative-cert-names)
}

resource "random_integer" "cert-suffix" {
  min = 1
  max = 100
  keepers = {
    # Generate a new integer each time we change the cert name list
    alternative_cert_names = local.domain_names
  }
}

Hi @huang-jy! Thank you for your patience and letting me see your code! I think what you’re seeing is actually a known upstream issue in terraform. However, I was able to manipulate what you had to circumvent the bug.

The bug you’re hitting can be avoided if your proxy depends on the cert’s self_link field, and lucky for you, the self_link can be passed into the proxy’s ssl_certificates field.

I had to make a few changes, but with these changes it should follow the best practices that I mentioned before - in the sense that it will create a new cert, apply it to the proxy, and then delete the old cert. However, it won’t wait for the cert to be active - but my understanding is that this was not important to you.

certs.tf (I added/modified the following code)

locals {
  len_certs = length(var.alternative-cert-names)
  last_cert_name = local.len_certs == 0 ? "" : "${var.alternative-cert-names[local.len_certs - 1]}"
}

resource "random_integer" "cert-suffix" {
  min = 1
  max = 100
  keepers = {
    # Generate a new integer each time we add a new domain to the end of the cert name list
    alternative_cert_names = local.last_cert_name
  }
}

resource "google_compute_managed_ssl_certificate" "multi-name-cert" {
  count    = length(var.alternative-cert-names) != 0 ? 1 : 0
  provider = google-beta
  name     = "${var.backend_name}-cert-${random_integer.cert-suffix.result}"
  project  = var.project_id

  lifecycle {
    create_before_destroy = true
  }

  managed {
    domains = concat(["${var.backend_name}.${var.domain}."], "${var.alternative-cert-names}")
  }
}

As you can see, I added the lifecycle.create_before_destroy block to the cert. What this will do is create a new cert before destroying the current one, allowing us to update the proxy to the new value before the current cert is destroyed. I had to also add the random int to the end of the cert name so that we didn’t try to recreate a cert of the same name, since the name has to be unique. The catch here is that whenever you want to add a domain to alternative-cert-names, you will need to add it to the end of the list, as that is the value that it’s checking to generate a new random int.

lb_cdn.tf (I modified the following code)

resource "google_compute_target_https_proxy" "single-name-cert-proxy" {
  count            = length(var.alternative-cert-names) == 0 ? 1 : 0
  name             = "${var.backend_name}-${var.env}-static-website-proxy-single"
  project          = var.project_id
  url_map          = google_compute_url_map.urlmap.self_link
  ssl_certificates = [google_compute_managed_ssl_certificate.single-name-cert.0.self_link]
}

resource "google_compute_target_https_proxy" "multi-name-cert-proxy" {
  count            = length(var.alternative-cert-names) != 0 ? 1 : 0
  name             = "${var.backend_name}-${var.env}-static-website-proxy-multiple"
  project          = var.project_id
  url_map          = google_compute_url_map.urlmap.self_link
  ssl_certificates = [google_compute_managed_ssl_certificate.multi-name-cert.0.self_link]
}

I deleted the depends_on per the bug above, and instead created the dependency by referencing the self_link of each cert here. This will circumvent the bug and create the appropriate dependency, acknowledging that the proxy needs to be updated when the cert self_link changes (which will happen when it’s name changes). I also added “single/multiple” to the end of the proxy names so there wasn’t any race condition when deleting the single-cert proxy and creating the multiple-cert proxy.

Please let me know if this helps. Thanks!