0

I want to create multiple acm certificates and route53 records for its validation, just cant figure out how can I reference to all acm resources created by for_each in route53 resource block which is looping in acm resource to get all the DNS _validation attributes, code is working fine if I will set one cert in variable and reference it directly with name, but how can I loop to all domain names for referencing it in for loop? Issue is this line

for dvo in aws_acm_certificate.web[for i in keys(var.certificates) : i]

which is returning

The index operator must end with a closing bracket ("]").

Adding a second bracket like this

for dvo in aws_acm_certificate.web[[for i in keys(var.certificates) : i]]

returns error

│
│   on main.tf line 21, in resource "aws_route53_record" "domain_validation":
│   21:     for dvo in aws_acm_certificate.web[[for i in keys(var.certificates) : i]].domain_validation_options : dvo.domain_name => {
│     ├────────────────
│     │ aws_acm_certificate.web is object with 2 attributes
│     │ var.certificates is object with 2 attributes
│
│ The given key does not identify an element in this collection value: string
│ required.```
resource "aws_acm_certificate" "web" {
  for_each = var.certificates
  domain_name       = "${replace(each.key, var.search_period, var.replace_period)}"
  subject_alternative_names = each.value.subject_alternative_names
  validation_method = "DNS"
}


resource "aws_route53_record" "domain_validation" {
  for_each = var.dns_validation ? {
    for dvo in aws_acm_certificate.web[[for i in keys(var.certificates) : i]].domain_validation_options : dvo.domain_name => {
      name   = dvo.resource_record_name
      record = dvo.resource_record_value
      type   = dvo.resource_record_type
    }
  } : {}

  allow_overwrite = true
  name            = each.value.name #aws_acm_certificate.web[each.key].domain_validation_options.0.resource_record_name
  records         = [each.value.record] #aws_acm_certificate.web[each.key].domain_validation_options.0.resource_record_value
  ttl             = 60
  type            = each.value.type #aws_acm_certificate.web[each.key].domain_validation_options.0.resource_record_type
  zone_id         = data.aws_route53_zone.selected[[for i in keys(var.certificates) : i]].zone_id
}

var.certificates looks like this:

certificates  = {
        "xxx.com" = {
            subject_alternative_names = ["*.xxx.com","*.dev.xxx.com","*.stage.xxx.com","*.preprod.xxx.com"]
        },
        "zzz.com" = {
            subject_alternative_names = ["*.dev.zzz.com","*.zzz.com"]
        },


       
    }


aws_acm_certificate.web in console looks like this:

``` 
> aws_acm_certificate.web
{
  "test.com" = {
    "arn" = "arn:aws:acm:eu-west-1:584637875403:certificate/a6fed-01c6-4f2c-ad87-59c04877bd0b"
    "certificate_authority_arn" = ""
    "certificate_body" = tostring(null)
    "certificate_chain" = tostring(null)
    "domain_name" = "test.com"
    "domain_validation_options" = toset([
      {
        "domain_name" = "*.test.com"
        "resource_record_name" = "_d5e2266fa07c911501b806f3d19e.test.com."
        "resource_record_type" = "CNAME"
        "resource_record_value" = "_fcc7913c9269201f77625b7f71ec.ltyvprtsjl.acm-validations.aws."
      },
      {
        "domain_name" = "*.dev.test.com"
        "resource_record_name" = "_f2aa63aabaae8cd721bf0143dee6.dev.test.com."
        "resource_record_type" = "CNAME"
        "resource_record_value" = "_f461eca5849d2a3e218dea91955.ltyvprtsjl.acm-validations.aws."
      },
      {
        "domain_name" = "*.preprod.test.com"
        "resource_record_name" = "_2747174805245587c6f9811a1180.preprod.test.com."
        "resource_record_type" = "CNAME"
        "resource_record_value" = "_7d3ccdf1006b12074ebcbc9c3d1.ltyvprtsjl.acm-validations.aws."
      },
      {
        "domain_name" = "*.stage.test.com"
        "resource_record_name" = "_6f571d29f334dcccfe098a2371c.stage.test.com."
        "resource_record_type" = "CNAME"
        "resource_record_value" = "_9c9657a4839d827d1ff6db0ffd0.ltyvprtsjl.acm-validations.aws."
      },
      {
        "domain_name" = "test.com"
        "resource_record_name" = "_d5e2266fa07c91bad71501bd19e.test.com."
        "resource_record_type" = "CNAME"
        "resource_record_value" = "_fcc7913c926926efc05b7f71ec.ltyvprtsjl.acm-validations.aws."
      },
    ])
    "id" = "arn:aws:acm:eu-west-1:584637875403:certificate/a6fed5981c6-4f2c-ad87-59c04877bd0b"
    "options" = tolist([
      {
        "certificate_transparency_logging_preference" = "ENABLED"
      },
    ])
    "private_key" = (sensitive)
    "status" = "ISSUED"
    "subject_alternative_names" = toset([
      "*.test.com",
      "*.dev.test.com",
      "*.preprod.test.com",
      "*.stage.test.com",
    ])
    "tags" = tomap({})
    "tags_all" = tomap({})
    "validation_emails" = tolist([])
    "validation_method" = "DNS"
  }
}
``` 

1 Answers1

5

You have to flatten your aws_acm_certificate.web first. For example:

locals {
  certificate_web_flat = merge([
    for hzone, certs in aws_acm_certificate.web:   {
        for domain_validation_option in certs.domain_validation_options:
          "${hzone}-${domain_validation_option.domain_name}" => {
            "hzone" = hzone
            "domain_name" = domain_validation_option.domain_name
             resource_record_name   = domain_validation_option.resource_record_name
             resource_record_value = domain_validation_option.resource_record_value
             resource_record_type   = domain_validation_option.resource_record_type            
          }
      }
  ]...)
}

then

resource "aws_route53_record" "domain_validation" {
  for_each = var.dns_validation ? local.certificate_web_flat : {}

  allow_overwrite = true
  name            = each.value.resource_record_name
  records         = [each.value.resource_record_value] 
  ttl             = 60
  type            = each.value.resource_record_type 
  zone_id         = data.aws_route53_zone.selected[each.value.hzone].zone_id
}
Marcin
  • 215,873
  • 14
  • 235
  • 294
  • its not working as inr ecord i need domain validation options, I tried ``` resource "aws_route53_record" "domain_validation" { for_each = var.dns_validation ? aws_acm_certificate.web : {} allow_overwrite = true name = each.value.domain_validation_options.resource_record_name ``` but I am getting each.value.domain_validation_options is set of object with 5 elements │ │ Can't access attributes on a set of objects. Did you mean to access an │ attribute across all elements of the set? – Lado Golijashvili Sep 04 '22 at 11:22
  • @LadoGolijashvili In that case you have to update your question and add what exactly is your `aws_acm_certificate.web` data. – Marcin Sep 04 '22 at 11:24
  • @Marcin there's an error in your answer. The `for_each` should be: `for_each = var.dns_validation ? aws_acm_certificate.web.domain_validation_options : {}` But `aws_acm_certificate.web` is a set, so I think you'll need to flatten that into a list of options? – Mark B Sep 04 '22 at 11:32
  • @Marcin my comment wasn't fully correct, see my update. – Mark B Sep 04 '22 at 11:34
  • @LadoGolijashvili I mean the `aws_acm_certificate.web`, not `var.certificates`. – Marcin Sep 04 '22 at 11:35
  • @MarkB I thinks so to. Thus I ask OP for what exactly `aws_acm_certificate.web` looks like. – Marcin Sep 04 '22 at 11:36
  • @marcin dont get what you want, whole aws_acm_certificate.web code is in question, also corrected answer is not working, getting below error: on main.tf line 20, in resource "aws_route53_record" "domain_validation": │ 20: for_each = var.dns_validation ? aws_acm_certificate.web.domain_validation_options : {} │ │ Because aws_acm_certificate.web has "for_each" set, its attributes must be │ accessed on specific instances. │ │ For example, to correlate with indices of a referring resource, use: │ aws_acm_certificate.web[each.key] – Lado Golijashvili Sep 04 '22 at 11:38
  • @LadoGolijashvili You have to `output` `aws_acm_certificate.web` to show how it looks like? It is map of maps? map or lists? What does your code actually produce? You have to iterate over the output of your command. Its difficult to guess the output, if you are not going to provide it. – Marcin Sep 04 '22 at 11:40
  • @LadoGolijashvili I modified the answer. – Marcin Sep 04 '22 at 11:51
  • 1
    works now thank you very much, @Marcin just you have typo in records value, must be resource in place of ource, cant edit it because of minimum characters in edit restriction – Lado Golijashvili Sep 04 '22 at 11:59
  • @LadoGolijashvili No problem. Just fixed the typo. – Marcin Sep 04 '22 at 12:00