hashicorp / terraform-provider-aws

The AWS Provider enables Terraform to manage AWS resources.
https://registry.terraform.io/providers/hashicorp/aws
Mozilla Public License 2.0
9.8k stars 9.15k forks source link

Can't use aws_acm_certificate_validation when alternate name not owned by terraform #15817

Open bedge opened 3 years ago

bedge commented 3 years ago

I needed to add additional domains to a cert, as I'm assuming ownership of another domain. The DNS for this other domain isn't owned by the same terraform context, so using aws_acm_certificate_validation forces it to try to create ACM validation entries in r53 for all of the cert names.

I was able to skip the ACM entry creation for the other domain by using a filter in the aws_acm_certificate_validation block:

resource "aws_acm_certificate_validation" "cert" {
  certificate_arn         = aws_acm_certificate.cert.arn
  validation_record_fqdns = [for record in aws_route53_record.cert_validation: 
                                record.fqdn 
                                    if length(regexall("otherdomain.com", record.name )) == 0 
                            ]
}

However, the result is:

Error: 1 error occurred:
        * missing xxxx.otherdomain.com DNS validation record: _7c4e76e3dab258fecef28f627a6cf0ea.xxx.otherdomain.com

So, despite specifically excluding this DNS validation record from being created, because it can't be, it's in another account, terraform apply fails because it wasn't created.

Community Note

Terraform CLI and Terraform AWS Provider Version

[I] ➜ terraform -v
Terraform v0.13.2
+ provider registry.terraform.io/hashicorp/aws v3.12.0
+ provider registry.terraform.io/hashicorp/random v2.3.0
+ provider registry.terraform.io/hashicorp/template v2.2.0
+ provider registry.terraform.io/hashicorp/tls v3.0.0

Your version of Terraform is out of date! The latest version
is 0.13.5. You can update by downloading from https://www.terraform.io/downloads.htm

Affected Resource(s)

aws_acm_certificate_validation

Terraform Configuration Files

resource "aws_route53_record" "cert_validation" {
  for_each = {
    for dvo in aws_acm_certificate.cert.domain_validation_options : dvo.domain_name => {
      name = dvo.resource_record_name
      record = dvo.resource_record_value
      type = dvo.resource_record_type
    }
  }
  name    = each.value.name
  type    = each.value.type
  records = [each.value.record]
  zone_id = var.zone_id
  ttl     = 60
}

resource "aws_acm_certificate_validation" "cert" {
  certificate_arn         = aws_acm_certificate.cert.arn
  validation_record_fqdns = [for record in aws_route53_record.cert_validation: 
                                record.fqdn 
                                    if length(regexall("otherdomain.com", record.name )) == 0 
                            ]
}

Expected Behavior

My expectation was that the otherdomain DNS validation record that was intentionally skipped would not be marked as a failure.

Actual Behavior

The one DNS validation record that was explicitly skipped was marked as an error.

Steps to Reproduce

  1. terraform apply
innovia commented 3 years ago

This resource is poorly documented on how it works:

Reading the source code

here: https://github.com/hashicorp/terraform-provider-aws/blob/0152d0ba7256bbeae1c38b6cd03f4ce82549c5e5/aws/resource_aws_acm_certificate_validation.go#L147-L149

and here: https://github.com/hashicorp/terraform-provider-aws/blob/0152d0ba7256bbeae1c38b6cd03f4ce82549c5e5/aws/resource_aws_acm_certificate_validation.go#L157-L161

it simply accepts all fqdns as input and then calls describe on the cert to get the full list of the domain validation options

then it iterates over the list of given funds, if found in the domain validation option of the cert it deletes it from the map

after that loop, it simply checks that the length should be zero

following by checking the state of the certificate regardless if the DNS record exists or not!

In my case, these other domains created outside of this account anyway and I can pass a provider for these record

locals {
  validation_records = {
    # for each domain validation options, build a map for dns records
    # skip if domain name starts with *. or domain name is in excluded_domains list
    for dvo in module.certificate_request.output.domain_validation_options :
    dvo.domain_name => {
      name   = dvo.resource_record_name
      record = dvo.resource_record_value
      type   = dvo.resource_record_type
    } if length(regexall("^\\*\\.", dvo.domain_name)) == 0 && !contains(var.excluded_domains, dvo.domain_name)
  }

  validation_record_fqdns = [
    for dvo in module.certificate_request.output.domain_validation_options :
    dvo.resource_record_name
  ]
}

resource "aws_route53_record" "example" {
  for_each = local.validation_records

  name            = each.value.name
  records         = [each.value.record]
  ttl             = 60
  type            = each.value.type
  zone_id         = data.aws_route53_zone.example.zone_id
}

resource "aws_acm_validation" "this" {
  certificate_arn         = module.certificate_request.output.arn
  validation_record_fqdns = local.validation_record_fqdns
}
poleszcz commented 1 year ago

@innovia I know... it has been a while... I'm not sure if I understand well your comment.

For my understanding the initial list of required fqdns is built based on certificate content. So there is no way to bypass this issue. Source code changed and moved since then... but I thinkt that the behavior didn't change.

https://github.com/hashicorp/terraform-provider-aws/blob/c4412ffb3ccf09a46c89f11d2787d680a1a183d8/internal/service/acm/certificate_validation.go#L64

alfredocambera-bitso commented 1 year ago

I'm having this same issue with a record I created using terraform. Using cloudflare. Haven't figured it out yet.

alfredocambera-bitso commented 1 year ago

I fixed by fetching the record ID using curl:

#!/usr/bin/env bash

NAME="REPLACE_WITH_YOUR_RECORD_NAME"

curl -s \
  --request GET \
  --url     "https://api.cloudflare.com/client/v4/zones/REPLACE_WITH_YOUR_ZONE_ID/dns_records?page=1&name=$NAME" \
  --header  'Content-Type: application/json' \
  --header "Authorization: Bearer REPLACE_WITH_YOUR_TOKEN"

For some reason when I fetched the record using https://github.com/cloudflare/python-cloudflare I was getting the wrong ID. Also, I enabled debug mode on terraform and noticed that terraform was also getting the wrong record ID. After fetching the record using curl, and importing it into TF it started working.

The curious thing is that after fetching the record using curl I started getting the right record IP using the python library.