Remote State File Output Values

Hi, I have a TF script that creates an AWS Simple Email Service (SES) domain verification entry, i.e. aws_ses_domain_identity and aws_ses_domain_dkim. The DKIM tokens are declared as an output, i.e. aws_ses_domain_dkim.northrow_domain_dkim.dkim_tokens. I am then reading the output as a remote state file, i.e. data “terraform_remote_state” “qa-common”.

When attempting to create the Route 53 records to authorise the SES domain, I’ve not been able to establish the exact syntax of the output. I am attempting to use the example given in the Terraform AWS documentation: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ses_domain_dkim#attributes-reference

The following script fails:

resource "aws_route53_record" "northrow_dkim" {
  count   = 3
  zone_id = var.zone_id
  name    = "${element(data.terraform_remote_state.qa-common.northrow_domain_dkim, count.index)}._domainkey"
  type    = "CNAME"
  ttl     = "600"
  records = ["${element(data.terraform_remote_state.qa-common.northrow_domain_dkim, count.index)}.dkim.amazonses.com"]
}

The error is:

│ Error: Unsupported attribute
│
│   on Route53.tf line 4, in resource "aws_route53_record" "northrow_dkim":
│    4:   name    = "${element(data.terraform_remote_state.qa-common.northrow_domain_dkim, count.index)}._domainkey"
│
│ This object has no argument, nested block, or exported attribute named "northrow_domain_dkim".
╵
╷
│ Error: Unsupported attribute
│
│   on Route53.tf line 7, in resource "aws_route53_record" "northrow_dkim":
│    7:   records = ["${element(data.terraform_remote_state.qa-common.northrow_domain_dkim, count.index)}.dkim.amazonses.com"]
│
│ This object has no argument, nested block, or exported attribute named "northrow_domain_dkim".

If I add dkim_tokens to the path:

resource "aws_route53_record" "northrow_dkim" {
  count   = 3
  zone_id = var.zone_id
  name    = "${element(data.terraform_remote_state.qa-common.northrow_domain_dkim.dkim_tokens, count.index)}._domainkey"
  type    = "CNAME"
  ttl     = "600"
  records = ["${element(data.terraform_remote_state.qa-common.northrow_domain_dkim.dkim_tokens, count.index)}.dkim.amazonses.com"]
}

The error is:

│ Error: Unsupported attribute
│
│   on Route53.tf line 4, in resource "aws_route53_record" "northrow_dkim":
│    4:   name    = "${element(data.terraform_remote_state.qa-common.northrow_domain_dkim.dkim_tokens, count.index)}._domainkey"
│
│ This object has no argument, nested block, or exported attribute named "northrow_domain_dkim".
╵
╷
│ Error: Unsupported attribute
│
│   on Route53.tf line 7, in resource "aws_route53_record" "northrow_dkim":
│    7:   records = ["${element(data.terraform_remote_state.qa-common.northrow_domain_dkim.dkim_tokens, count.index)}.dkim.amazonses.com"]
│
│ This object has no argument, nested block, or exported attribute named "northrow_domain_dkim".

Any help would be appreciated.

Hi @glenn.comiskey,

It looks like this configuration is written for Terraform v0.11 or earlier, but you’re applying it with a more recent version of Terraform.

If this is an entirely new configuration and you’re intentionally using the latest version of Terraform, the following would be the modern way to write the configuration you shared here:

resource "aws_route53_record" "northrow_dkim" {
  count = 3

  zone_id = var.zone_id
  name    = "${data.terraform_remote_state.qa-common.outputs.northrow_domain_dkim[count.index]}._domainkey"
  type    = "CNAME"
  ttl     = "600"
  records = [
    "${data.terraform_remote_state.qa-common.outputs.northrow_domain_dkim[count.index]}.dkim.amazonses.com"
  ]
}

The specific changes I made here are:

  • The terraform_remote_state data source returns all of the output values all together as an object in data.terraform_remote_state.qa-common.outputs, rather than a separate attribute for each one, as described in the Terraform v0.12 upgrade guide.
  • From Terraform v0.12 onwards, the element function is only for situations where you want to “wrap around” (modulo the index) if the given index is out of bounds for the length of the list. I guessed that you intended for each record to have a distinct hostname and so you wouldn’t need that behavior, though you can keep using element if you did intend to use the “wrap-around” behavior.

Since it seems like your northrow_domain_dkim here functions as a set of unique strings, a possible further change you could make here is to use resource for_each to automatically declare one record per element of that set, rather than hard-coding count = 3:

resource "aws_route53_record" "northrow_dkim" {
  for_each = toset(data.terraform_remote_state.qa-common.outputs.northrow_domain_dkim)

  zone_id = var.zone_id
  name    = "${each.value}._domainkey"
  type    = "CNAME"
  ttl     = "600"
  records = [
    "${each.value}.dkim.amazonses.com"
  ]
}

The toset call here is a type conversion from list(string) to set(string), which is an unordered set of unique strings and is one of the value types for_each can work with. This’d be appropriate only if all of the elements of that list are distinct from one another, which I assume must be true in this case because IIRC Route53 requires the name argument to be unique across all records in a particular zone.

Along with automatically detecting the intended number of instances, this also means that Terraform will identify the records by their basename rather than their indexes within the list, so if you add or remove elements from northrow_domain_dkim in future Terraform will understand that as adding or removing only the corresponding aws_route53_record.northrow_dkim instances.

However, this strategy would be appropriate only if Terraform will always be able to resolve data.terraform_remote_state.qa-common.outputs.northrow_domain_dkim during the planning step. That will be true unless the configuration for data "terraform_remote_state" "qa-common" includes references to attributes of other resources that might appear as (known after apply) in the plan when they’ve not been created yet.

I can’t thank you enough for your help apparentlymart. The pointers you’ve given me have not only helped get over my immediate issue, but helped point me in right direction with the use of Terraform. Being a newbie to Terraform I’m currently just pulling code examples from where I can get them, as in this case regarding Route 53, as an issue arises. Your help with the code, the explanation and the advice has been an immense boost to my learning. Thanks again.