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 
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.