Home > Software engineering >  Better handling of multiple loops within 1 resource with Terraform
Better handling of multiple loops within 1 resource with Terraform

Time:06-24

I'm trying to create multiple CNAME resources based on multiple A record resources and am struggling to find a way to achieve this.

I currently have the following in my variable.tfvars file:

hostnames = [
  "webserver1",
  "webserver2"
]
domain = domain.com
subdomain_addresses = [
  "www-1",
  "www-2",
  "www-3"
]

I have the following output from my ec2 instance module which is based off the length of var.hostnames: (in this instance there will be 2 private IPs)

output "server_ip_addresses" {
  value = aws_instance.this.*.private_ip
}

I currently have the following in my resource.tf file:

resource "aws_route53_record" "instance_record" {
  for_each = toset(var.hostnames)

  zone_id = data.aws_route53_zone.route53_zone.zone_id
  name    = "${each.value}.${var.domain}"
  type    = "A"
  ttl     = "300"
  records = module.ec2_instance.server_ip_addresses
}

resource "aws_route53_record" "webservice_records" {
  for_each = toset(var.subdomain_addresses)

  zone_id = data.aws_route53_zone.route53_zone.zone_id
  name    = "${each.value}.${var.domain}"
  type    = "CNAME"
  ttl     = "300"
  records = [values(aws_route53_record.instance_record)[*].name]
}

What I need is a way to have the webservice_records resource create a subdomain for each entry in var.subdomain_addresses but associate each subdomain with each aws_route53_record.instance_record.name output.

So for example instance_record creates the following A records:

  • webserver1.domain.com = 10.1.1.18
  • webserver2.domain.com = 10.1.1.19

and webservice_records creates the following CNAMEs:

  • www-1.domain.com = webserver1.domain.com, webserver2.domain.com
  • www-2.domain.com = webserver1.domain.com, webserver2.domain.com
  • www-3.domain.com = webserver1.domain.com, webserver2.domain.com

I am currently getting the following error:

╷
│ Error: Incorrect attribute value type
│ 
│   on resource.tf line 62, in resource "aws_route53_record" "webservice_records":
│   62:   records = [values(aws_route53_record.instance_record)[*].name]
│     ├────────────────
│     │ aws_route53_record.instance_record is object with 2 attributes
│ 
│ Inappropriate value for attribute "records": element 0: string required.
╵

Does someone have a better idea of how this can be achieved?

CodePudding user response:

you can use the setproduct function to achieve that output like below:

so it would be like something like this to create a map:

locals {
  hostnames = [
    "webserver1",
    "webserver2"
  ]
  domain = "domain.com"
  subdomain_addresses = [
    "www-1",
    "www-2",
    "www-3"
  ]
  webserver_list = [ for web_list in setproduct(local.hostnames, [local.domain]) :  join(".",web_list) ]
  cname_map = {for sub_domain in local.subdomain_addresses: sub_domain => local.webserver_list}
}


resource "aws_route53_record" "cnames" {
  for_each = local.cname_map

  zone_id = data.aws_route53_zone.route53_zone.zone_id
  name    = each.key
  type    = "CNAME"
  ttl     = "300"
  records = each.value
}

cname_map looks like this:

 cname_map= {
       www-1 = [
           "webserver1.domain.com",
           "webserver2.domain.com",
        ]
       www-2 = [
           "webserver1.domain.com",
           "webserver2.domain.com",
        ]
       www-3 = [
           "webserver1.domain.com",
           "webserver2.domain.com",
        ]
    }
  • Related