1

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]}
Colm Prunty
  • 1,595
  • 1
  • 11
  • 29
  • 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 Mar 08 '18 at 10:35
  • Yeah my bad, they should both be `name` actually – Colm Prunty Mar 08 '18 at 10:36
  • 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 Mar 08 '18 at 10:39
  • See edits above – Colm Prunty Mar 08 '18 at 10:52
  • Is this a similar issue to https://stackoverflow.com/questions/50067317/terraform-creating-and-validating-multiple-acm-certificates/50258144 ? – JinnKo May 09 '18 at 17:31

4 Answers4

4

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
}
Colm Prunty
  • 1,595
  • 1
  • 11
  • 29
  • Seeing your answer, I've only just spotted your splat is in the wrong place. I've edited my answer to point that out too but the splat should normally follow the `resource_type.resource_name` part like `aws_instance.web_server.*.private_ip` – ydaetskcoR Mar 08 '18 at 18:27
  • On second thoughts I might be wrong because the `aws_acm_certificate` resource has a very peculiar API. I suspect that is most of the issue here but haven't tried the DNS validation on the ACM certs with Terraform yet. – ydaetskcoR Mar 08 '18 at 18:43
  • Yeah I was placing it where the 0 was in the example at the top of my question. – Colm Prunty Mar 08 '18 at 21:24
0

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.

ydaetskcoR
  • 53,225
  • 8
  • 158
  • 177
mcfinnigan
  • 11,442
  • 35
  • 28
  • Why wouldn't you link directly to the [interpolation docs](https://www.terraform.io/docs/configuration/interpolation.html) which is what that seems to be a dump of? – ydaetskcoR Mar 08 '18 at 10:22
  • Also `list[index]` is a valid format. There is a difference between them though as `element(list, index)` will loop back through the list when the index is greater than the amount of elements in the list. – ydaetskcoR Mar 08 '18 at 10:25
  • Oh I did try this too `* aws_route53_record.cert_validation[2]: 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)} ` – Colm Prunty Mar 08 '18 at 10:30
0

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.

ydaetskcoR
  • 53,225
  • 8
  • 158
  • 177
0
resource "aws_route53_record" "cert_validation" {
  name = "${aws_acm_certificate.cert.domain_validation_options.*.resource_record_name[0]}"
  type = "${aws_acm_certificate.cert.domain_validation_options.*.resource_record_type[0]}"

  zone_id = "${data.aws_route53_zone.zone.id}"
  records = ["${aws_acm_certificate.cert.domain_validation_options.*.resource_record_value[0]}"]
  ttl = 60
}

Ref: https://github.com/hashicorp/terraform-provider-aws/issues/14447