We defined below data structure (variable) to manage Route 53 hosted zone delegation. We use subdomain hosted zone delegated to an account (hsdevel,hsstage) with base domain hosted zone in another account (shared), e.g. as depicted here: https://serverless-stack.com/chapters/share-route-53-domains-across-aws-accounts.html.
For this delegation to work the host zones must be created on env one accounts and records created in the shared account pointing to Name Servers of defined subdomain host zones.
Data Structure variable
hosted_zones_for_delegation = {
"domain1.net" = {
hsdevel = {
"hz1" = {
"hsdevel1.domain1.net" = {
comment = "..."
tags = {
"Environment" = "hsdevel"
}
}
}
"hz2" = {
"hsdevel2.domain1.net" = {
comment = "..."
tags = {
"Environment" = "hsdevel"
}
}
}
}
hsstage = {
"hz1" = {
"hsstage.domain1.net" = {
comment = "..."
tags = {
"Environment" = "hsstage"
}
}
}
}
}
"domain2.net" = {
hsdevel = {
"hz1" = {
"hsdevel.domain2.net" = {
comment = "..."
tags = {
"Environment" = "hsdevel"
}
}
}
}
hsstage = {
"hz1" = {
"hsstage.domain2.net" = {
comment = "..."
tags = {
"Environment" = "hsstage"
}
}
}
}
}
}
As every subdomain hosted zone (in account hsstage and hsdevel) will have own Name Servers list (dynamically assigned by aws) that must be referenced on Route 53 records in the base hosted zone accuont (shared) we need to create a single hosted zone struct like below to loop on to create hosted zones (in hsdevel, hsstage accounts) and related records (in shared account), to do so we use this flattening code:
hosted_zones_for_delegation_with_parents_list = flatten([
for hzparentname, hzcfg in local.hosted_zones_for_delegation : [
for accountname, accounthzcfg in hzcfg : [
for hzmap, hzmapelement in accounthzcfg : [
for hzname, hzcfg in hzmapelement : {
"account" = accountname
"deleg_zone" = hzmapelement
"deleg_zone_name" = hzname
"deleg_zone_parent_name" = hzparentname
}
]
]
]
])
hosted_zones_for_delegation_with_parents = { for hz in local.hosted_zones_for_delegation_with_parents_list : "${hz.account}_${hz.deleg_zone_name}" => hz }
We get variable like this for new flattened data:
debug1 = {
"hsdevel_hsdevel.domain2.net" = {
"account" = "hsdevel"
"deleg_zone" = {
"hsdevel.domain2.net" = {
"comment" = "..."
"tags" = {
"Environment" = "hsdevel"
}
}
}
"deleg_zone_name" = "hsdevel.domain2.net"
"deleg_zone_parent_name" = "domain2.net"
}
"hsdevel_hsdevel1.domain1.net" = {
"account" = "hsdevel"
"deleg_zone" = {
"hsdevel1.domain1.net" = {
"comment" = "..."
"tags" = {
"Environment" = "hsdevel"
}
}
}
"deleg_zone_name" = "hsdevel1.domain1.net"
"deleg_zone_parent_name" = "domain1.net"
}
...
}
We use for_each on “hosted_zones_for_delegation_with_parents” to create hosted zone in, e.g. in hsdevel account we’ll have (module.route53_hosted_zones_hsdevel), obtaining this data structure:
debug2 = {
"hsdevel_hsdevel.domain2.net" = {
"this_route53_zone_name_servers" = {
"hsdevel.domain2.net" = [
"ns-.....org",
"ns-.....com",
"ns-.....co.uk",
"ns-....net",
]
}
"this_route53_zone_zone_id" = {
"hsdevel.domain2.net" = "Z0... K0"
}
}
"hsdevel_hsdevel1.domain1.net" = {
"this_route53_zone_name_servers" = {
"hsdevel1.domain1.net" = [
"ns-.....org",
"ns-.....com",
"ns-.....co.uk",
"ns-....net",
]
}
"this_route53_zone_zone_id" = {
"hsdevel1.domain1.net" = "Z0...2U"
}
}
"hsdevel_hsdevel2.domain1.net" = {
"this_route53_zone_name_servers" = {
"hsdevel2.domain1.net" = [
"ns-.....org",
"ns-.....com",
"ns-.....co.uk",
"ns-....net", ]
}
"this_route53_zone_zone_id" = {
"hsdevel2.domain1.net" = "Z0...TJ"
}
}
On record creation in shared account (see code below):
### NOTE: provider block does not support interpolation so we need to manage each account separately instead of using plain for_each for all accounts
module "route53_records_for_delegation_hsdevel" {
source = "..."
for_each = {
for hz, hzinfo in local.hosted_zones_for_delegation_with_parents :
hz => hzinfo
if length(regexall("hsdevel",hz)) > 0
}
providers = {
aws = aws.shared
}
zone_name = each.value.deleg_zone_parent_name
records = [
{
name = "hsdevel"
type = "NS"
ttl = 300
records = module.route53_hosted_zones_hsdevel[each.key].this_route53_zone_name_servers[each.value.deleg_zone]
},
]
depends_on = [module.route53_hosted_zones_hsdevel]
}
We get error:
Error: Invalid index
on account_shared.tf line 273, in module "route53_records_for_delegation_hsdevel":
273: records = module.route53_hosted_zones_hsdevel[each.key].this_route53_zone_name_servers[each.value.deleg_zone]
|----------------
| each.key is "hsdevel_hsdevel2.domain1.net"
| each.value.deleg_zone is object with 1 attribute "hsdevel2.domain1.net"
| module.route53_hosted_zones_hsdevel is object with 3 attributes
The given key does not identify an element in this collection value: string
required.
-
It is possible to convert object attribute in a string to avoid this error and potentially allowing manipulation with string function (extract domani, …, e.g instead of statically define
name = "hsdevel"extract hsdevel synamically fromm data strucrture) ? (this simplify a lot also data structure variable definition) -
It is possible to print resulting struct create in for_each to be sure of the data on which for_rach works (e.g. as output value) ?
for_each = {
for hz, hzinfo in local.hosted_zones_for_delegation_with_parents :
hz => hzinfo
if length(regexall("devel",hz)) > 0 # to select only spec accout hosted zones
}
-
There is a chance to have nested for_each support in terraform ? It’ll simplify a lot infra configuration with complex data structure allowing infra description more close to objects hierarchy with related properties
-
There is a chance for provider block interpolation support ?