Terraform: element: argument 1 should be type list, got type string

Created on 29 Mar 2018  ยท  13Comments  ยท  Source: hashicorp/terraform

Terraform Version

0.11.3

Terraform Configuration Files

Part of resources in module:

resource "aws_acm_certificate" "cert" {
  domain_name = "${var.aliases[0]}"
  subject_alternative_names = "${compact(split(",", replace(join(",",var.aliases), var.aliases[0], "")))}"
  validation_method = "DNS"
}

resource "aws_route53_record" "cert_validation" {
  count = "${length(var.aliases)}"
  name = "${element(aws_acm_certificate.cert.domain_validation_options.*.resource_record_name, count.index)}"
  type = "CNAME"
  zone_id = "${data.aws_route53_zone.zone.id}"
  records = ["${element(aws_acm_certificate.cert.domain_validation_options.*.resource_record_value, count.index)}"]
  ttl = 60
}

resource "aws_acm_certificate_validation" "cert" {
  certificate_arn = "${aws_acm_certificate.cert.arn}"
  validation_record_fqdns = ["${aws_route53_record.cert_validation.0.fqdn}"]
}

Expected Behavior


Expect to the attributes from domain_validation_options list (which is an exported attribute of aws_acm_certificate resource). The goal is to be able to create Route53 records for each dns specified in var.aliases as a part of DNS validating the certificate. Getting this to work for just the main domain name is ok, I'm trying to get this to work with SAN certs but cannot find a way to correctly parse these parts to get a DNS validation record setup for each of them. Hopefully it's just a stupid mistake somewhere there but I'm blind to it!

Actual Behavior

Error: Error refreshing state: 1 error(s) occurred:

  • module.cloudfront.module.acm_cert_test_b17g_stage_net.aws_route53_record.cert_validation: 2 error(s) occurred:

  • module.cloudfront.module.acm_cert_test_b17g_stage_net.aws_route53_record.cert_validation[1]: At column 3, line 1: element: argument 1 should be type list, got type string in:

${element(aws_acm_certificate.cert.domain_validation_options.*.resource_record_name, count.index)}

  • module.cloudfront.module.acm_cert_test_b17g_stage_net.aws_route53_record.cert_validation[0]: At column 3, line 1: element: argument 1 should be type list, got type string in:

${element(aws_acm_certificate.cert.domain_validation_options.*.resource_record_name, count.index)}

Steps to Reproduce

  • Try to create ACM cert with X number of SAN names specified using DNS validation.
  • terraform init
  • terraform plan
  • Additional Context

    References

    bug config

    Most helpful comment

    Thanks to #17315, I think I see the solution. Basically the result of a single element of domain_validation_options is a map -- it's not very clear in the documentation where they simply describe it as having more attributes.

    In any case, the way to dereference these sub-elements is with the help of the lookup function:

    resource "aws_acm_certificate" "the_cert" {
        domain_name               = "${var.cert_name}"
        subject_alternative_names = "${var.cert_san_names}"
    
        validation_method = "DNS"
    }
    
    resource "aws_route53_record" "cert_validation_records" {
        ## We'll create one record for the default `domain_name` + one record for each SAN name
        count = "${1 + length(var.cert_san_names)}"
    
        zone_id = "${data.aws_route53_zone.the_zone}"
        name    = "${lookup(aws_acm_certificate.the_cert.domain_validation_options[count.index], "resource_record_name")}"
        type    = "${lookup(aws_acm_certificate.the_cert.domain_validation_options[count.index], "resource_record_type")}"
        ttl     = 60
        records = ["${lookup(aws_acm_certificate.the_cert.domain_validation_options[count.index], "resource_record_value")}"]
    }
    

    This setup worked perfectly for me.

    All 13 comments

    I don't currently have any solutions or advice, but I want to let you know I have the exact same use case & the exact same problem.

    @aegershman ok, well glad I'm not alone then!

    Ditto

    Thanks to #17315, I think I see the solution. Basically the result of a single element of domain_validation_options is a map -- it's not very clear in the documentation where they simply describe it as having more attributes.

    In any case, the way to dereference these sub-elements is with the help of the lookup function:

    resource "aws_acm_certificate" "the_cert" {
        domain_name               = "${var.cert_name}"
        subject_alternative_names = "${var.cert_san_names}"
    
        validation_method = "DNS"
    }
    
    resource "aws_route53_record" "cert_validation_records" {
        ## We'll create one record for the default `domain_name` + one record for each SAN name
        count = "${1 + length(var.cert_san_names)}"
    
        zone_id = "${data.aws_route53_zone.the_zone}"
        name    = "${lookup(aws_acm_certificate.the_cert.domain_validation_options[count.index], "resource_record_name")}"
        type    = "${lookup(aws_acm_certificate.the_cert.domain_validation_options[count.index], "resource_record_type")}"
        ttl     = 60
        records = ["${lookup(aws_acm_certificate.the_cert.domain_validation_options[count.index], "resource_record_value")}"]
    }
    

    This setup worked perfectly for me.

    Oh, and the natural next step is to create the corresponding ACM validation resource, which would look something like this to go along with the sample above:

    resource "aws_acm_certificate_validation" "cert_validation" {
        certificate_arn = "${aws_acm_certificate.the_cert.arn}"
        validation_record_fqdns = ["${aws_route53_record.cert_validation_records.*.fqdn}"]
    }
    

    @ebekker nice! Tried your setup and now works better, or at least I think it will. First run this morning, without the previous errors but the apply did not complete because I got a timeout on certificate_validation after 45 mins.

    "* aws_acm_certificate_validation.cert: Expected certificate to be issued but was in state VALIDATION_TIMED_OUT"

    I can see that the validation records have been correctly created in Route53 in correct zone. Will test a bit more to see if I can get it working. I do need to add support in the module to use different aws provider region, as my default provider is set to eu-central-1 but when using this module it needs to create the certificate in us-east-1 to be applicate for Cloudfront. Could of course be this that is making the certificate_validation not to work in my case.

    Fixed the provider support in the module and now it all works like a charm! thanks again @ebekker for guidance, I was reallt blind there and documentation not really clear.

    Closing the issue.

    @anderssoder what did you end up doing to fix the issue? I am stuck at the point where the validation records are created but validation itself times out. I'm doing everything in the same region.

    One thing that I noticed, if the DNS records are not created almost immediately after the cert, sometimes the ACM checking will be delayed -- at first I thought this was tied to the negative caching TTL configured in the zone's SOA record, but in many cases that value is set at 24 hours (i.e. Route 53's default SOA uses this) so that wasn't the case.

    Based on observations, it seems that if the DNS records needed to validate the cert are not there in the first few minutes, then it can take up 2 hours for ACM to validate the records.

    Also, deleting and recreating the ACM cert or the DNS records or the ACM validation resource didn't seem to impact this. It seems like the delay indicated global DNS caching behavior inside of whatever infrastructure ACM runs on.

    @bennycornelissen Are you creating the aws_acm_certificate and the aws_acm_certificate_validation in us-east-1?

    The default SOA record created by route53 has the final field set to around 24 hours ... this field historically was used for minimum TTL, it switched to mean negative caching (about 20 ~ 15 years ago). So how long should a mid stream / local resolver should hold on to record with a result of NXDOMAIN or SERVERFAIL ... unfortunately there are a lot of hocky examples out there that seem to think that once you have not seen something, you should continue to not see it for days.

    Check you zone SOA if the the last number is 86400 then any errors or NXDOMAINS are gonna persist for 24 hours ... I typically drop this down into the 5s ~ 30s range as I typically do not want negative answers to have a longer lifetime than a positive answer.

    I landed here with a similar issue and eventually solved it using locals and flatten(). Details can be found on stackoverflow.

    I'm going to lock this issue because it has been closed for _30 days_ โณ. This helps our maintainers find and focus on the active issues.

    If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

    Was this page helpful?
    0 / 5 - 0 ratings