provider "aws" { alias = "certificate_requester" } provider "aws" { alias = "route53_cert_validator" } locals { # produces a list of maps of san to zone # [ { "*.foo.example.org" = "foo.example.org"} ] list_of_sans_to_zone = [ for zone, sans in var.zone_to_san : { for san in sans : san => zone } ] # produces a map of SAN => zone # { "*.foo.example.org" = "foo.example.org"} san_to_zone = { for san, zone in merge(flatten([local.list_of_sans_to_zone])...) : san => zone } # A list of just the SANs. Sorted to ensure stability if the map order # changes. sans = sort(keys(local.san_to_zone)) san_to_zone_final = { for san, zone in merge({ (var.domain_name) = var.domain_name }, local.san_to_zone) : san => zone # Skip validating anything specified in skip_validations if ! contains(var.skip_validations, san) # if the san without the "*." wildcard exists in the list of SANs or is # the same as the domain_name, then they will have the same ACM validation # record. # See # https:#docs.aws.amazon.com/acm/latest/userguide/gs-acm-validate-dns.html # for details and examples of how *.example.com and example.com both have # the same CNAME validation record. && ! contains(local.sans, trimprefix(san, "*.")) # if the SAN isnt var.domain name, then check if it's a wildcard of the # domain_name && (san == var.domain_name || trimprefix(san, "*.") != var.domain_name) } # *.foo.example.com => {...} validations = { for validation_option in aws_acm_certificate.cert.domain_validation_options : validation_option.domain_name => validation_option } } # Keyed by SAN, allowing lookup of a zone_id by the SAN used data "aws_route53_zone" "selected" { provider = aws.route53_cert_validator # Also get domain_name for_each = merge({ (var.domain_name) = var.domain_name }, local.san_to_zone) name = each.value } resource "aws_acm_certificate" "cert" { provider = aws.certificate_requester domain_name = var.domain_name subject_alternative_names = local.sans validation_method = "DNS" tags = var.tags lifecycle { create_before_destroy = true } } resource "aws_acm_certificate_validation" "cert" { provider = aws.certificate_requester certificate_arn = aws_acm_certificate.cert.arn # We use an explicit dependency and pass this directly from the # domain_validation_options instead of using the fqdn from # aws_route53_record.cert_validation because we may be skipping the creation # of some route53 records, and we need to provide all validation FQDNs, even # if we do not create them. validation_record_fqdns = aws_acm_certificate.cert.domain_validation_options[*].resource_record_name depends_on = [ aws_route53_record.cert_validation ] } resource "aws_route53_record" "cert_validation" { provider = aws.route53_cert_validator for_each = local.san_to_zone_final zone_id = data.aws_route53_zone.selected[each.key].zone_id name = local.validations[each.key].resource_record_name type = local.validations[each.key].resource_record_type records = [local.validations[each.key].resource_record_value] ttl = 60 }