Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Terraform: Referencing resources created in for_each in another resource

When I had a single hosted zone it was easy for me to create the zone and then create the NS records for the zone in the delegating account by referencing the hosted zone by name.

  • Edit To try to avoid confusion this is what I wanted to achieve but for multiple hosted zones and the owner of the domain is a management account: https://dev.to/arswaw/create-a-subdomain-in-amazon-route53-in-2-minutes-3hf0

Now I need to create multiple hosted zones and pass the nameserver records back to the parent account and I am not sure if its possible (or how if it is) to reference multiple resources. Reading this probably doesn't make a whole lot of sense, so the code below is as far as I have got.

I now have a for_each loop which will loop over a list of strings and create a hosted zone for each string, and I want to then create corresponding NS records in another account, notice that I am using a separate provider provider = aws.management_account to connect to the management account and this works fine for a single hosted zone.

I do not know how to reference the hosted zones, is there some syntax for this or is my approach wrong?

resource "aws_route53_zone" "public_hosted_zone" {
  for_each = local.aws_zones
  name     = "${each.value}.${var.domain}"
}

resource "aws_route53_record" "ns_records" {
  for_each        = local.aws_zones
  provider        = aws.management_account
  allow_overwrite = true
  name            = "${each.value}.${var.domain}"
  ttl             = 30
  type            = "NS"
  zone_id         = data.aws_ssm_parameter.public_hosted_zone_id.value

  records = [
    aws_route53_zone.public_hosted_zone.name_servers[0], # Here is my old code which works for a single hosted zone but I cannot work out how to reference multiples created above
    aws_route53_zone.public_hosted_zone.name_servers[1],
    aws_route53_zone.public_hosted_zone.name_servers[2],
    aws_route53_zone.public_hosted_zone.name_servers[3]
  ]
}
like image 439
berimbolo Avatar asked Aug 28 '20 21:08

berimbolo


2 Answers

Since your local.aws_zones is set ["dev", "test", "qa"], your aws_route53_zone.public_hosted_zone will be a map with keys "dev", "test", "qa".

Therefore, to use it in your aws_route53_record, you can try:

resource "aws_route53_record" "ns_records" {
  for_each        = local.aws_zones

  # other attributes

  records = aws_route53_zone.public_hosted_zone[each.key].name_servers
}
like image 190
Marcin Avatar answered Sep 20 '22 13:09

Marcin


In this case it seems like your intent is to say "there is one zone per entry in local.aws_zones and then one NS recordset per zone".

In situations like that, a concise way to write that down is to use one resource as the for_each of the next, like this:

resource "aws_route53_zone" "public_hosted_zone" {
  for_each = local.aws_zones

  name = "${each.value}.${var.domain}"
}

resource "aws_route53_record" "ns_records" {
  provider = aws.management_account
  for_each = aws_route53_zone.public_hosted_zone

  allow_overwrite = true
  name            = each.value.name
  ttl             = 30
  type            = "NS"
  zone_id         = data.aws_ssm_parameter.public_hosted_zone_id.value
  records         = each.value.name_servers
}

By using for_each inside resource "aws_route53_zone" "public_hosted_zone" you make aws_route53_zone.public_hosted_zone be a map from zone keys to zone objects, which is itself compatible with the expectations of for_each and so you can pass that map downstream to another resource.

An important advantage of this approach is that each.value in the resource "aws_route53_record" "ns_records" block is the corresponding zone object directly, and so we can write each.value.name to get the derived name argument from the zone without having to duplicate that expression again, and we can write each.value.name_servers to get the exported set of nameservers for each of the zones. That then avoids writing more complex expressions to look up zones by each.key, because Terraform has already done the necessary lookup to populate each.value.

like image 30
Martin Atkins Avatar answered Sep 23 '22 13:09

Martin Atkins