How to read values of resource created by Provider A in step that needs Provider B

I am creating an ACM certificate one AWS provider, but in a later step, need to use a different provider to create DNS entries because the DNS zone is in a different AWS account.

aws_acm_certificate is created with provider aws.us-east-1 while the route53 entries need to be created with aws.accountwithzone

So the certificate create looks like

resource "aws_acm_certificate" "foo" {
  provider    = aws.us-east-1
  domain_name = var.domain
  validation_method = "DNS"

  options {
    certificate_transparency_logging_preference = "ENABLED"
  }

  lifecycle {
    create_before_destroy = true
  }
}

The validation records entry is

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

  allow_overwrite = true
  name            = each.value.name
  records         = [each.value.record]
  ttl             = 60
  type            = each.value.type
  zone_id         = aws_route53_zone.foo.zone_id
  provider        = aws.accountwithzone
}

When running this, we get an error stating

Error: AccessDenied: User: arn:aws:iam::ouraccount:user/tfclouduser is not authorized to access this resource status code: 403, request id: request-id-content-redacted

If I hardcode the list of entries in the for_each loop instead of trying to read for dvo in aws_acm_certificate.foo.domain_validation_options then everything works as expected.

Hi @WTPascoe,

I’m not exactly sure what’s happening here, but the for_each expression is kind of suspect. The fact that aws_acm_certificate.foo.domain_validation_options is a computed attribute means that using it in the for_each expression like this is going to prevent Terraform from being able to apply the config without manual intervention of some sort. Now the for_each value probably doesn’t need to be hard-coded, how is that data generated by the resource, and can you collect the same values from a source known before the aws_acm_certificate is created?

The error shown however isn’t related to the computed values being used in the for_each expression. I’m not familiar with the aws resources used here, would you get the exact same error if you assigned the wrong provider like provider = aws.us-east-1?

If that’s the same error as using the wrong provider, can you show how the providers are configured, and verify you are using a current Terraform relase?

Thanks!

The for_each is taken directly from the aws_acm_certificate resource documentation at Terraform Registry

If we change the zone_id to one within the account and set the provider to aws.us-east-1 it all works fine. This was our working config before we needed to move the DNS for validation to a different AWS account.

Terraform release is 1.2.5

Provider config in the stack that uses the module is

provider "aws" {
  region              = "eu-central-1"
  allowed_account_ids = ["1"]
  access_key          = var.aws_access_key_id-1
  secret_key          = var.aws_secret_access_key-1
}

provider "aws" {
  alias               = "us-east-1"
  region              = "us-east-1"
  allowed_account_ids = ["1"]
  access_key          = var.aws_access_key_id-1
  secret_key          = var.aws_secret_access_key-1
}

provider "aws" {
  alias               = "accountwithzone"
  region              = "us-east-1"
  access_key          = var.aws_access_key_id-2
  secret_key          = var.aws_secret_access_key-2
  allowed_account_ids = ["2"]
}

Then in main.tf we do

module "stack" {
  providers = {
    aws.eu        = aws
    aws.us-east-1 = aws.us-east-1
    aws.accountwithzone = aws.accountwithzone
  }

Finally, in the module we have a provider.tf with

terraform {
  required_providers {
    aws = {
      source                = "hashicorp/aws"
      version               = ">= 2.7.0"
      configuration_aliases = [aws.eu, aws.us-east-1, aws.accountwithzone]
    }
  }
}

Oh nice, it looks like aws_acm_certificate is actually planning the value correctly, so using the domain_name as the for_each key will work here. That however means there should be no reason hard-coding the for_each works differently than using the config shown :confused:

I’m going to see if I can reproduce any unusual behavior here. Is this failing during the plan or apply step?

It fails during apply.

When I say hardcoding the values works, I don’t mean aws_acm_certificate.cutr-ai.domain_validation_options.resource_record_name, I mean the literal value - e.g. _ff543879a534.foo.com

The second provider has access to create records in AWS Account 2.

Is it expected that Provider 1 can create a resource, and that Provider 2 would be able to read it?

Hmm, maybe I’ve misunderstood what you are trying to do here. Whether one provider can read resource created by another provider is both dependent on the provider configuration and the behavior of the provider itself. I guess it also depends on what you mean by “read” here, Terraform can read the state from a resource created by one provider and insert that into the configuration of a resource used by another provider. The providers however may not have access to the actual resources created by the others.

You mention that it works if you change the zone_id, what resource exactly is returning the error? What is the config for aws_route53_zone.foo?

I’ve got to the bottom of this and it’s a PEBKAC situation.

The permission denied was occurring because we already had a resource defined with a name (e.g. resource "aws_route53_record" "foo") and we were changing the provider on that.

While I changed foo to foo-new and applied, other things also referenced this.

The eventual solution was to

  • comment out the associated aws_acm_certificate_validation resource
  • rename foo to foo-new with new provider
  • Plan and apply
  • Rename foo-new to foo and uncomment out the aws_acm_certificate_validation resource
  • Plan and apply

In the end, in code, the only change to the file is the addition of the provider within the aws_route53_record resource block, but this set of steps resolves the ownership complaints.