Last active
November 20, 2023 21:56
-
-
Save chancez/dfaaf799b98698839d65ebba55db7d44 to your computer and use it in GitHub Desktop.
Support creating a DNS validated ACM certificate containing SANs for multiple different hosted zones
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module "combined_acm_certificate" { | |
source = "../../modules/acm_certificate_dns_validated_multi_zone" | |
domain_name = "infra.example.com" | |
zone_to_san = { | |
"infra.example.com" = [ | |
"*.infra.example.com", | |
"*.dev.infra.example.com", | |
"*.staging.infra.example.com", | |
"*.production.infra.example.com", | |
] | |
"foo.test.com" = [ | |
"*.foo.test.com" | |
] | |
"foo-dev.test.com" = [ | |
"*.foo-dev.test.com" | |
] | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
variable "domain_name" { | |
type = string | |
description = "A domain name for which the certificate should be issued." | |
} | |
variable "zone_to_san" { | |
type = map(list(string)) | |
description = "A mapping of hosted zone name to SANs." | |
} | |
variable "skip_validations" { | |
type = list(string) | |
description = "A list of SANs to skip validation for. For when validations already exist for the SAN. Include both the wildcard and base domain of the wildcard if your SANs includes both." | |
default = [] | |
} | |
variable "tags" { | |
type = map(string) | |
default = {} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment