1
votes

I'm trying to make an SSL cert with terraform for multiple DNS records, following the docs here: https://www.terraform.io/docs/providers/aws/r/acm_certificate_validation.html

For the Route 53 records is gives this example:

resource "aws_route53_record" "cert_validation" {
  name = "${aws_acm_certificate.cert.domain_validation_options.0.resource_record_name}"
  type = "${aws_acm_certificate.cert.domain_validation_options.0.resource_record_type}"
  zone_id = "${data.aws_route53_zone.zone.id}"
  records = ["${aws_acm_certificate.cert.domain_validation_options.0.resource_record_value}"]
  ttl = 60
}

Where the 0 in the name and type refer to the single DNS entry they've provided. If I add several subject_alternative_names to the aws_acm_certificate and add several manual aws_route53_record with the 0 replaced by 1 2 etc, it works the way I want.

My question is, can I do this in one go using Terraform's count. I've tried these two things with count = 5:

name = "${aws_acm_certificate.cert.domain_validation_options.*.resource_record_name[count.index]}"

This complains that it's getting a string and not a list

name = "${aws_acm_certificate.cert.domain_validation_options.count.index.resource_record_name}"

This gives all of them the same name, and it's just "5".

Edit:

Setup:

resource "aws_route53_record" "cert_validation" {
    count = 5
    name = "${aws_acm_certificate.cert.domain_validation_options.*.resource_record_name[count.index]}"
    type = "CNAME"
    zone_id = "myzoneid"
    records = ["${aws_acm_certificate.cert.domain_validation_options.*.resource_record_value[count.index]}"]
    ttl = 60
}

Errors:

* aws_route53_record.cert_validation: 5 error(s) occurred:

* aws_route53_record.cert_validation[4]: At column 95, line 1: invalid index operation into non-indexable type: TypeString in:

${aws_acm_certificate.cert.domain_validation_options.*.resource_record_value[count.index]}
* aws_route53_record.cert_validation[2]: At column 94, line 1: invalid index operation into non-indexable type: TypeString in:

${aws_acm_certificate.cert.domain_validation_options.*.resource_record_name[count.index]}
* aws_route53_record.cert_validation[3]: At column 94, line 1: invalid index operation into non-indexable type: TypeString in:

${aws_acm_certificate.cert.domain_validation_options.*.resource_record_name[count.index]}
* aws_route53_record.cert_validation[0]: At column 95, line 1: invalid index operation into non-indexable type: TypeString in:

${aws_acm_certificate.cert.domain_validation_options.*.resource_record_value[count.index]}
* aws_route53_record.cert_validation[1]: At column 94, line 1: invalid index operation into non-indexable type: TypeString in:

${aws_acm_certificate.cert.domain_validation_options.*.resource_record_name[count.index]}
3
I could be wrong here but should that first attempt (after count = 5) be records = ...? Considering the name parameter takes a string and the records parameter takes a list that would make sense, especially with your second attempt below that. - ydaetskcoR
Yeah my bad, they should both be name actually - Colm Prunty
That doesn't align with your error message then. records takes a list so you need to wrap it in square brackets to coerce it into a list. name takes a string so you don't need to do anything. Can you post the actual error you are getting? - ydaetskcoR
See edits above - Colm Prunty
Is this a similar issue to stackoverflow.com/questions/50067317/… ? - JinnKo

3 Answers

4
votes

After a bunch more trial and error, the solution was this:

resource "aws_route53_record" "cert_validation" {
    count = 5
    name = "${lookup(aws_acm_certificate.cert.domain_validation_options[count.index], "resource_record_name")}"
    type = "CNAME"
    zone_id = "myzoneid"
    records = ["${lookup(aws_acm_certificate.cert.domain_validation_options[count.index], "resource_record_value")}"]
    ttl     = 60
}
0
votes

The syntax is:

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

That is, you're using the element(arr, idx) lookup function to get the actual value for the index of interest.

Here's a useful cheat sheet of terraform functions.

0
votes

The aws_route53_record resource records parameter takes a list of strings so if you try to provide a straight string it will fail.

You were nearly there with the initial attempt and just need to force it into a list like you do with the second attempt and put the splat in the right place:

records = ["${aws_acm_certificate.cert.*.domain_validation_options.resource_record_name[count.index]}"]

Note that this is using splat indexing to fetch all the resources and then grabbing the index using the list[index] syntax. It's also possible to use the element(list, index) syntax like this:

records = ["${element(aws_acm_certificate.cert.*.domain_validation_options.resource_record_name, count.index)}"]

The second syntax allows you to loop back through the list so if you have something like:

variable "foo_list" {
  default = [
    "a",
    "b",
    "c",
  ]
}

Then "${element(var.foo_list, 3)}" will return "a" while "${var.foo_list[3]}" while error with an index out of range exception.