Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

aws_acm_certificate seems to have changed its state output possibly due to a provider update- am I doing this wrong?

Terraform v0.12.12
+ provider.aws v3.0.0
+ provider.template v2.1.2

Before I was doing this:

resource "aws_route53_record" "derps" {
  name    = aws_acm_certificate.mycert[0].resource_record_name
  type    = aws_acm_certificate.mycert[0].resource_record_type
  zone_id = var.my_zone_id
  records = aws_acm_certificate.mycert[0].resource_record_value
  ttl     = 60
}

And that worked fine for me about a week ago.

I just did a plan and got an error:

records = [aws_acm_certificate.mycert.domain_validation_options[0].resource_record_value]

This value does not have any indices.

Now I don't pin provider versions, so I'm assuming I pulled a newer version and the resource changed.

After fighting with this and realizing it's not a list (even though when doing show state it sure looked like one) I am now doing this to make it a list:

resource "aws_route53_record" "derps" {
  name    = sort(aws_acm_certificate.mycert.domain_validation_options[*].resource_record_name)[0]
  type    = sort(aws_acm_certificate.mycert.domain_validation_options[*].resource_record_type)[0]
  zone_id = var.my_zone_id
  records = [sort(aws_acm_certificate.mycert.domain_validation_options[*].resource_record_value)[0]]
  ttl     = 60
} 

This resulted in no changes which is good. But if I use the example for doing this from the docs they now use for_each: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate_validation

resource "aws_route53_record" "example" {
  for_each = {
    for dvo in aws_acm_certificate.example.domain_validation_options : dvo.domain_name => {
      name    = dvo.resource_record_name
      record  = dvo.resource_record_value
      type    = dvo.resource_record_type
      zone_id = dvo.domain_name == "example.org" ? data.aws_route53_zone.example_org.zone_id : data.aws_route53_zone.example_com.zone_id
    }
  }

  allow_overwrite = true
  name            = each.value.name
  records         = [each.value.record]
  ttl             = 60
  type            = each.value.type
  zone_id         = each.value.zone_id
}

resource "aws_acm_certificate_validation" "example" {
  certificate_arn         = aws_acm_certificate.example.arn
  validation_record_fqdns = [for record in aws_route53_record.example : record.fqdn]
}

Is the above the correct way to do this now? Am I going to run into issues doing it the way I currently am? Doing it like above would result in a destroy/recreate (i guess I could import it myself but that's painful).

Is doing it my way not going to result in unexpected diffs?

Edit

So, more specific for my issue. This is what I see when I look at the state:

terraform state show aws_acm_certificate.mycert

    ...
    domain_name               = "*.mydom.com"
    domain_validation_options = [
        {
            domain_name           = "*.mydom.com"
            resource_record_name  = "_11111111111.mydom.com."
            resource_record_type  = "CNAME"
            resource_record_value = "_1111111111.11111111.acm-validations.aws."
        },
        {
            domain_name           = "mydom.com"
            resource_record_name  = "_11111111111.mydom.com."
            resource_record_type  = "CNAME"
            resource_record_value = "_1111111111.111111111.acm-validations.aws."
        },
    ]
    ... 

By using sort I'm effectively using count which of course results in a destroy/recreate if the order changes. But in my case I think that's unlikely?? I also don't fully understand the difference between just using the values from the wildcard validation config and using both of them.

like image 293
red888 Avatar asked Aug 03 '20 18:08

red888


3 Answers

This is the same problem we were facing just now.. we use for_each to define hosting for multiple sites according to provided local variables, now, since we already use for_each we can't use it for workaround of their changes.. unfortunate.

I didn't want to go with sort so I checked what Jimmy wrote but it didn't work for this case due to output being index, I fixed it by using [0] instead of [*]:

resource "aws_route53_record" "cert_validation" {
  for_each = local.web_pages
  allow_overwrite = true
  name            = tolist(aws_acm_certificate.cert[each.key].domain_validation_options)[0].resource_record_name
  type            = tolist(aws_acm_certificate.cert[each.key].domain_validation_options)[0].resource_record_type
  records         = [tolist(aws_acm_certificate.cert[each.key].domain_validation_options)[0].resource_record_value]
  zone_id         = var.aws_hosted_zone
  ttl             = 60
}

works for us now ;)

like image 116
pduchnovsky Avatar answered Oct 17 '22 14:10

pduchnovsky


The AWS Terraform provider was recently upgraded to version 3.0. This version comes with a list of breaking changes. I recommend consulting the AWS provider 3.0 upgrade guide.

The issue you are encountering is because the domain_validation_options attribute is now a set instead of a list. From that guide:

Since the domain_validation_options attribute changed from a list to a set and sets cannot be indexed in Terraform, the recommendation is to update the configuration to use the more stable resource for_each support instead of count

I recommend using the new foreach syntax, as the upgrade guide recommends, in order to avoid unexpected diffs. The guide states that you will need to use terraform state mv to move the old configuration state to the new configuration, in order to prevent the resources from being recreated.

like image 12
Mark B Avatar answered Oct 17 '22 14:10

Mark B


These 3 sets of code below work. (I used Terraform v0.15.0)

*The difference between 1st and 2nd is [0] and .0

1st:

resource "aws_route53_record" "myRecord" {
  zone_id = aws_route53_zone.myZone.zone_id
  name    = tolist(aws_acm_certificate.myCert.domain_validation_options)[0].resource_record_name
  type    = tolist(aws_acm_certificate.myCert.domain_validation_options)[0].resource_record_type
  records = [tolist(aws_acm_certificate.myCert.domain_validation_options)[0].resource_record_value]
  ttl = "60"
  allow_overwrite = true
}

2nd:

resource "aws_route53_record" "myRecord" {
  zone_id = aws_route53_zone.myZone.zone_id
  name    = tolist(aws_acm_certificate.myCert.domain_validation_options).0.resource_record_name
  type    = tolist(aws_acm_certificate.myCert.domain_validation_options).0.resource_record_type
  records = [tolist(aws_acm_certificate.myCert.domain_validation_options).0.resource_record_value]
  ttl = "60"
  allow_overwrite = true
}

3rd:

resource "aws_route53_record" "myRecord" {
  for_each = {
    for dvo in aws_acm_certificate.myCert.domain_validation_options : dvo.domain_name => {
      name   = dvo.resource_record_name
      record = dvo.resource_record_value
      type   = dvo.resource_record_type
    }
  }

  name            = each.value.name
  records         = [each.value.record]
  ttl             = 60
  type            = each.value.type
  zone_id         = aws_route53_zone.myZone.zone_id
  allow_overwrite = true
}
like image 4
Kai - Kazuya Ito Avatar answered Oct 17 '22 12:10

Kai - Kazuya Ito