diff --git a/README.md b/README.md
index 202ebc1..48a7ef7 100644
--- a/README.md
+++ b/README.md
@@ -81,6 +81,37 @@ module "acm" {
```
+## Usage with Route53 DNS validation & different zones
+
+```hcl
+module "acm" {
+ source = "terraform-aws-modules/acm/aws"
+ version = "~> 3.3"
+
+ domain_name = "my-domain.com"
+ zone_id = "Z2ES7B9AZ6SHAE"
+
+ subject_alternative_names = [
+ "*.my-domain.com",
+ "app.sub.my-domain.com",
+ {
+ name = "my-other-domain.com"
+ zone_id = "Z06730ESICABNX"
+ },
+ {
+ name = "*.my-other-domain.com"
+ zone_id = "Z06730ESICABNX"
+ }
+ ]
+
+ wait_for_validation = true
+
+ tags = {
+ Name = "my-domain.com"
+ }
+}
+```
+
## Examples
- [Complete example with DNS validation (recommended)](https://github.com/terraform-aws-modules/terraform-aws-acm/tree/master/examples/complete-dns-validation)
diff --git a/examples/dns-validation-multiple-zones/README.md b/examples/dns-validation-multiple-zones/README.md
new file mode 100644
index 0000000..ce43937
--- /dev/null
+++ b/examples/dns-validation-multiple-zones/README.md
@@ -0,0 +1,59 @@
+# ACM example with Route53 DNS validation and multiple zones
+
+Configuration in this directory creates a new ACM certificate which has SAN's that need a different Route53 Zone compared to `var.domain_name` to be validated.
+
+
+## Usage
+
+To run this example you need to execute:
+
+```bash
+$ terraform init
+$ terraform plan
+$ terraform apply
+```
+
+Note that this example may create resources which cost money. Run `terraform destroy` when you don't need these resources.
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 0.12.26 |
+| [aws](#requirement\_aws) | >= 2.53 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | >= 2.53 |
+
+## Modules
+
+| Name | Source | Version |
+|------|--------|---------|
+| [acm](#module\_acm) | ../../ | n/a |
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [aws_route53_zone.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_zone) | resource |
+| [aws_route53_zone.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/route53_zone) | data source |
+
+## Inputs
+
+No inputs.
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [acm\_certificate\_arn](#output\_acm\_certificate\_arn) | The ARN of the certificate |
+| [acm\_certificate\_domain\_validation\_options](#output\_acm\_certificate\_domain\_validation\_options) | A list of attributes to feed into other resources to complete certificate validation. Can have more than one element, e.g. if SANs are defined. Only set if DNS-validation was used. |
+| [acm\_certificate\_validation\_emails](#output\_acm\_certificate\_validation\_emails) | A list of addresses that received a validation E-Mail. Only set if EMAIL-validation was used. |
+| [distinct\_domain\_names](#output\_distinct\_domain\_names) | List of distinct domains names used for the validation. |
+| [validation\_domains](#output\_validation\_domains) | List of distinct domain validation options. This is useful if subject alternative names contain wildcards. |
+| [validation\_route53\_record\_fqdns](#output\_validation\_route53\_record\_fqdns) | List of FQDNs built using the zone domain and name. |
+
diff --git a/examples/dns-validation-multiple-zones/main.tf b/examples/dns-validation-multiple-zones/main.tf
new file mode 100644
index 0000000..b40346a
--- /dev/null
+++ b/examples/dns-validation-multiple-zones/main.tf
@@ -0,0 +1,44 @@
+locals {
+ primary_domain = "terraform-aws-modules.modules.tf"
+ extra_domain = "terraform-aws-modules.extra-modules.tf"
+
+ # Removing trailing dot from domain - just to be sure :)
+ primary_domain_name = trimsuffix(local.primary_domain, ".")
+ extra_domain_name = trimsuffix(local.extra_domain, ".")
+}
+
+data "aws_route53_zone" "primary_domain" {
+ name = local.primary_domain_name
+ private_zone = false
+}
+
+data "aws_route53_zone" "extra_domain" {
+ name = local.extra_domain_name
+ private_zone = false
+}
+
+
+module "acm" {
+ source = "../../"
+
+ domain_name = local.primary_domain_name
+ zone_id = data.aws_route53_zone.primary_domain.zone_id
+
+ subject_alternative_names = [
+ "*.${local.primary_domain_name}",
+ {
+ name = "alerts.${local.extra_domain_name}"
+ zone_id = data.aws_route53_zone.extra_domain.zone_id
+ },
+ {
+ name = "*.alerts.${local.extra_domain_name}"
+ zone_id = data.aws_route53_zone.extra_domain.zone_id
+ }
+ ]
+
+ wait_for_validation = true
+
+ tags = {
+ Name = local.primary_domain_name
+ }
+}
diff --git a/examples/dns-validation-multiple-zones/outputs.tf b/examples/dns-validation-multiple-zones/outputs.tf
new file mode 100644
index 0000000..1eb07eb
--- /dev/null
+++ b/examples/dns-validation-multiple-zones/outputs.tf
@@ -0,0 +1,29 @@
+output "acm_certificate_arn" {
+ description = "The ARN of the certificate"
+ value = module.acm.acm_certificate_arn
+}
+
+output "acm_certificate_domain_validation_options" {
+ description = "A list of attributes to feed into other resources to complete certificate validation. Can have more than one element, e.g. if SANs are defined. Only set if DNS-validation was used."
+ value = module.acm.acm_certificate_domain_validation_options
+}
+
+output "acm_certificate_validation_emails" {
+ description = "A list of addresses that received a validation E-Mail. Only set if EMAIL-validation was used."
+ value = module.acm.acm_certificate_validation_emails
+}
+
+output "validation_route53_record_fqdns" {
+ description = "List of FQDNs built using the zone domain and name."
+ value = module.acm.validation_route53_record_fqdns
+}
+
+output "distinct_domain_names" {
+ description = "List of distinct domains names used for the validation."
+ value = module.acm.distinct_domain_names
+}
+
+output "validation_domains" {
+ description = "List of distinct domain validation options. This is useful if subject alternative names contain wildcards."
+ value = module.acm.validation_domains
+}
diff --git a/examples/dns-validation-multiple-zones/variables.tf b/examples/dns-validation-multiple-zones/variables.tf
new file mode 100644
index 0000000..e69de29
diff --git a/examples/dns-validation-multiple-zones/versions.tf b/examples/dns-validation-multiple-zones/versions.tf
new file mode 100644
index 0000000..67d15ec
--- /dev/null
+++ b/examples/dns-validation-multiple-zones/versions.tf
@@ -0,0 +1,10 @@
+terraform {
+ required_version = ">= 0.12.26"
+
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 2.53"
+ }
+ }
+}
diff --git a/main.tf b/main.tf
index 17bcb86..69f8766 100644
--- a/main.tf
+++ b/main.tf
@@ -1,10 +1,33 @@
locals {
create_certificate = var.create_certificate && var.putin_khuylo
+ # Normalize var.subject_alternative_names to a list of maps: [{name = NAME, zone_id = ZONE_ID}]
+ ## var.subject_alternative_names is a list of mixed elements: Strings and Maps of Strings
+ ## string elements use the string as the "name" and use var.zone_id as their "zone_id"
+ ## map elements passthrough their "name" and "zone_id" values (if zone_id not specified use var.zone_id)
+ subject_alternative_names = [
+ for element in var.subject_alternative_names :
+ {
+ name = try(tostring(element["name"]), tostring(element))
+ zone_id = try(tostring(element["zone_id"]), var.zone_id)
+ }
+ ]
+
+ # Create a map from domain name (minus wildcard) to zone id
+ ## The extra flatten([]) is to resolve a bug in terraform 0.12.26 (keeping backwards compatibility)
+ ## https://github.com/hashicorp/terraform/issues/22404
+ domainname_to_zoneid = merge(flatten([[
+ for element in concat([{ name = var.domain_name, zone_id = var.zone_id }], local.subject_alternative_names) :
+ {
+ replace(element.name, "*.", "") = element.zone_id
+ }
+ ]])...)
+
# Get distinct list of domains and SANs
- distinct_domain_names = distinct(
- [for s in concat([var.domain_name], var.subject_alternative_names) : replace(s, "*.", "")]
- )
+ distinct_domain_names = distinct([
+ for s in concat([var.domain_name], local.subject_alternative_names[*].name) :
+ replace(s, "*.", "")
+ ])
# Get the list of distinct domain_validation_options, with wildcard
# domain names replaced by the domain name
@@ -19,7 +42,7 @@ resource "aws_acm_certificate" "this" {
count = local.create_certificate ? 1 : 0
domain_name = var.domain_name
- subject_alternative_names = var.subject_alternative_names
+ subject_alternative_names = local.subject_alternative_names[*].name
validation_method = var.validation_method
options {
@@ -45,7 +68,7 @@ resource "aws_acm_certificate" "this" {
resource "aws_route53_record" "validation" {
count = local.create_certificate && var.validation_method == "DNS" && var.create_route53_records && var.validate_certificate ? length(local.distinct_domain_names) : 0
- zone_id = var.zone_id
+ zone_id = local.domainname_to_zoneid[element(local.validation_domains, count.index)["domain_name"]]
name = element(local.validation_domains, count.index)["resource_record_name"]
type = element(local.validation_domains, count.index)["resource_record_type"]
ttl = var.dns_ttl
diff --git a/variables.tf b/variables.tf
index 681fbaf..509b0e2 100644
--- a/variables.tf
+++ b/variables.tf
@@ -36,7 +36,7 @@ variable "domain_name" {
variable "subject_alternative_names" {
description = "A list of domains that should be SANs in the issued certificate"
- type = list(string)
+ type = any # list(string | map(string))
default = []
}