Terraform multiple region aws_ses_domain_identity - amazon-web-services

I want to create a aws_ses_domain_identity resource in multiple regions but as far as I can see, this is only possible by changing the region of the AWS provider.
I've attempted to use a for_each with no luck. I then want to create a aws_route53_record from the verification_tokens. I suspect this also won't work.
Ultimately, I'm aiming for creating an SES domain identity and corresponding Route 53 verification records for the regions specified in a variable (ses_regions).
Code:
provider "aws" {
alias = "eu-central-1"
region = "eu-central-1"
}
provider "aws" {
alias = "us-west-2"
region = "us-west-2"
}
variable "ses_regions" {
description = "The aws region in which to operate"
default = {
region1 = "us-west-2"
region2 = "eu-central-1"
}
}
resource "aws_ses_domain_identity" "example" {
for_each = var.ses_regions
provider = each.value
domain = var.ses_domain
}
resource "aws_route53_record" "example_amazonses_verification_record" {
for_each = aws_ses_domain_identity.example.verification_token
zone_id = var.zone_id
name = "_amazonses.${var.ses_domain}"
type = "TXT"
ttl = "600"
records = each.value
}
Error:
Error: Invalid provider configuration reference
on .terraform/modules/ses/main.tf line 8, in resource "aws_ses_domain_identity" "example":
8: provider = aws.each.value
The provider argument requires a provider type name, optionally followed by a
period and then a configuration alias.

Related

How to set the terraform provider.other_region variable?

Looking at this example:
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/elasticache_global_replication_group
The secondary region is referencing the aws.other_region variable, however the aws provider spec does not have an 'other_region' field
When I try to set that manually to 'us-west-1' for example it fails with failed to install provider
other_region provider can be defined like.
provider "aws" {
region = "us-west-2"
}
provider "aws" {
alias = "other_region"
region = "us-west-1"
}
Then you can use it on resources like.
resource "aws_elasticache_replication_group" "secondary" {
provider = aws.other_region
replication_group_id = "example-secondary"
}

Validate the certificates from 2 different accounts in AWS Terraform

I have an individual(dev) account and the certificate is created based on the below module. The validation takes place in the root account (i.e., prod account) so when I go to the route 53 of the root account and add the domain entries (create record) and copy the CVALUE of the domain and paste it over there, then my individual account certificate gets validated and changed from "Pending Validation" to "Issued".
I want the below tf to add the entries (domain name and CVALUE) to the root account so that while terraform apply the certificate gets validated. Right now, I am manually adding the CVALUE entries to the root account for validation.
public_dns.tf
resource "aws_route53_zone" "public" {
name = var.domain
}
resource "aws_acm_certificate" "elb_cert" {
domain_name = var.domain
subject_alternative_names = ["*.${var.domain}"]
validation_method = "DNS"
}
resource "aws_route53_record" "cert_validation" {
for_each = {
for dvo in aws_acm_certificate.elb_cert.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.public.zone_id
}
resource "aws_acm_certificate_validation" "elb_cert" {
count = var.certify_domain ? 1 : 0
certificate_arn = aws_acm_certificate.elb_cert.arn
validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn]
}
To accomplish what you want, you will need to use multiple aws providers (alias).
One for the root account and one for the dev account, as you will be managing resources in two different accounts.
it will look something like this:
provider "aws" {
alias = "prod"
***
}
provider "aws" {
alias = "dev"
***
}
resource "aws_******"{
provider = aws.prod
***
***
}
resource "aws_******"{
provider = aws.dev
***
***
}
Official documentation
Medium article about it

Terraform - Multiple accounts with multiple environments (regions)

I am developing the infrastructure (IaC) I want to have in AWS with Terraform. To test, I am using an EC2 instance.
This code has to be able to be deployed across multiple accounts and **multiple regions (environments) per developer **. This is an example:
account-999
developer1: us-east-2
developer2: us-west-1
developerN: us-east-1
account-666:
Staging: us-east-1
Production: eu-west-2
I've created two .tfvars variables, account-999.env.tfvars and account-666.env.tfvars with the following content:
profile="account-999" and profile="account-666" respectively
This is my main.tf which contains the aws provider with the EC2 instance:
provider "aws" {
version = "~> 2.0"
region = "us-east-1"
profile = var.profile
}
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"]
}
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
tags = {
Name = "HelloWorld"
}
}
And the variable.tf file:
variable "profile" {
type=string
}
variable "region" {
description = "Region by developer"
type = map
default = {
developer1 = "us-west-2"
developer2 = "us-east-2"
developerN = "ap-southeast-1"
}
}
But I'm not sure if I'm managing it well. For example, the region variable only contains the values of the account-999 account. How can I solve that?
On the other hand, with this structure, would it be possible to implement modules?
You could use a provider alias to accomplish this. More info about provider aliases can be found here.
provider "aws" {
region = "us-east-1"
}
provider "aws" {
alias = "west"
region = "us-west-2"
}
resource "aws_instance" "foo" {
provider = aws.west
# ...
}
Another way to look at is, is by using terraform workspaces. Here is an example:
terraform workspace new account-999
terraform workspace new account-666
Then this is an example of your aws credentials file:
[account-999]
aws_access_key_id=xxx
aws_secret_access_key=xxx
[account-666]
aws_access_key_id=xxx
aws_secret_access_key=xxx
A reference to that account can be used within the provider block:
provider "aws" {
region = "us-east-1"
profile = "${terraform.workspace}"
}
You could even combine both methods!

Tell Terraform to ignore Route53 resource in different workspace

I currently have 2 workspaces within Terraform, one for Prod and one for Dev.
In prod my Terraform code creates a Route53 entry and then add's a cert as a CNAME to the Route53 hosted zone and then attaches the cert to my load balancer.
resource "aws_acm_certificate" "default" {
domain_name = "www.test.uk"
validation_method = "DNS"
}
resource "aws_route53_record" "validation" {
name = aws_acm_certificate.default.domain_validation_options[0].resource_record_name
type = aws_acm_certificate.default.domain_validation_options[0].resource_record_type
zone_id = "Z0725470IF9R8J77LPTU"
records = [
aws_acm_certificate.default.domain_validation_options[0].resource_record_value]
ttl = "60"
}
resource "aws_acm_certificate_validation" "default" {
certificate_arn = aws_acm_certificate.default.arn
validation_record_fqdns = [
aws_route53_record.validation.fqdn,
]
}
When I switch my workspace to dev and run terraform apply it tries to creates this Route53 entry again and errors. Is there a way to tell Terraform to ignore this?
I tried adding a count of 0 but it gave me this error
Error: Missing resource instance key
on alb.tf line 12, in resource "aws_route53_record" "validation":
12: type =
aws_acm_certificate.default.domain_validation_options[0].resource_record_type
Because aws_acm_certificate.default has "count" set, its attributes
must be accessed on specific instances.
For example, to correlate with indices of a referring resource, use:
aws_acm_certificate.default[count.index]
Error: Missing resource instance key
on alb.tf line 15, in resource "aws_route53_record" "validation":
15:
aws_acm_certificate.default.domain_validation_options[0].resource_record_value]
Because aws_acm_certificate.default has "count" set, its attributes
must be accessed on specific instances.
For example, to correlate with indices of a referring resource, use:
aws_acm_certificate.default[count.index]
The best solution I've come up with is to comment out the Route53 stuff when I run terraform apply in the staging workspace, this obviously isn't an ideal solution.
Untested below but I think you can use a conditional (based on your workspace name) and use count to create (or not create) the resources.
locals {
create_me = terraform.workspace == "dev" ? 0 : 1
}
resource "aws_acm_certificate" "default" {
count = local.create_me
domain_name = "www.test.uk"
validation_method = "DNS"
}
resource "aws_route53_record" "validation" {
count = local.create_me
name = aws_acm_certificate.default.domain_validation_options[count.index].resource_record_name
type = aws_acm_certificate.default.domain_validation_options[count.index].resource_record_type
zone_id = "Z0725470IF9R8J77LPTU"
records = [
aws_acm_certificate.default.domain_validation_options[count.index].resource_record_value]
ttl = "60"
}
resource "aws_acm_certificate_validation" "default" {
count = local.create_me
certificate_arn = aws_acm_certificate.default[count.index].arn
validation_record_fqdns = [
aws_route53_record.validation[count.index].fqdn,
]
}

Terraform with API-Gateway, Route53, and SSL Certification interdependency problem

I can't seem to get an SSL certificate from ACM working on API-Gateway, Route53, using terraform. There seems to be an interdependency problem.
data "aws_route53_zone" "root_domain" {
name = "${var.route53_root_domain_name}"
private_zone = false
}
# The domain name to use with api-gateway
resource "aws_api_gateway_domain_name" "domain_name" {
domain_name = "${var.route53_sub_domain_name}"
certificate_arn = "${aws_acm_certificate.cert.arn}"
}
resource "aws_route53_record" "sub_domain" {
name = "${var.route53_sub_domain_name}"
type = "A"
zone_id = "${data.aws_route53_zone.root_domain.zone_id}"
alias {
name = "${aws_api_gateway_domain_name.domain_name.cloudfront_domain_name}"
zone_id = "${aws_api_gateway_domain_name.domain_name.cloudfront_zone_id}"
evaluate_target_health = false
}
}
resource "aws_acm_certificate" "cert" {
# api-gateway / cloudfront certificates need to use the us-east-1 region
provider = "aws.cloudfront-acm-certs"
domain_name = "${var.route53_sub_domain_name}"
validation_method = "DNS"
lifecycle {
create_before_destroy = true
}
}
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 = "${aws_route53_record.sub_domain.zone_id}"
records = ["${aws_acm_certificate.cert.domain_validation_options.0.resource_record_value}"]
ttl = 60
}
resource "aws_acm_certificate_validation" "cert" {
# api-gateway / cloudfront certificates need to use the us-east-1 region
provider = "aws.cloudfront-acm-certs"
certificate_arn = "${aws_acm_certificate.cert.arn}"
validation_record_fqdns = ["${aws_route53_record.cert_validation.fqdn}"]
}
The problem appears to be that:
aws_api_gateway_domain_name requires aws_acm_certificate
aws_acm_certificate has to be validated, so step 3
aws_route53_record.cert_validation requires aws_route53_record.sub_domain
aws_route53_record.subdomain requires aws_api_gateway_domain_name
Go to 1
Everytime I try to use the configuration given, I get the following error:
aws_api_gateway_domain_name.domain_name: Error creating API Gateway
Domain Name: BadRequestException: Unable to associate certificate
arn:aws:acm:us-east-1:yyyy:certificate/zzzz with CloudFront. This
error may prevent the domain name audit-log.taspli.com from being used
in API Gateway for up to 40 minutes. Please ensure the certificate
domain name matches the requested domain name, and that this user has
permission to call cloudfront:UpdateDistribution on '*' resources.
status code: 400, request id: xxxx
I seem to have fixed the problem by adding the certificate validation records to the root domain instead of the sub domain. Therefore breaking the cyclic dependency.
The problem appears to be that the sub domain can't be created without the certificate and the certificate can't be validated without the sub domain. So the situation is stuck and unresolvable.
You could manually create the sub domain, but then whats the point in automation if you have to make manual efforts to solve problems.
So I tried adding the cert validation records to the root. Suddenly it starts to work, because the root domain is something that is created externally to the project. A sort of global infrastructure project which can be handled externally. Then your individual projects can hang off of that infrastructure on a case-by-case basis.
Here is the terraform configuration which worked:
data "aws_route53_zone" "root_domain" {
name = "${var.route53_root_domain_name}"
private_zone = false
}
# The domain name to use with api-gateway
resource "aws_api_gateway_domain_name" "domain_name" {
domain_name = "${var.route53_sub_domain_name}"
certificate_arn = "${aws_acm_certificate.cert.arn}"
}
resource "aws_route53_record" "sub_domain" {
name = "${var.route53_sub_domain_name}"
type = "A"
zone_id = "${data.aws_route53_zone.root_domain.zone_id}"
alias {
name = "${aws_api_gateway_domain_name.domain_name.cloudfront_domain_name}"
zone_id = "${aws_api_gateway_domain_name.domain_name.cloudfront_zone_id}"
evaluate_target_health = false
}
}
resource "aws_acm_certificate" "cert" {
# api-gateway / cloudfront certificates need to use the us-east-1 region
provider = "aws.cloudfront-acm-certs"
domain_name = "${var.route53_sub_domain_name}"
validation_method = "DNS"
}
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.root_domain.zone_id}"
records = ["${aws_acm_certificate.cert.domain_validation_options.0.resource_record_value}"]
ttl = 60
}
resource "aws_acm_certificate_validation" "cert" {
# api-gateway / cloudfront certificates need to use the us-east-1 region
provider = "aws.cloudfront-acm-certs"
certificate_arn = "${aws_acm_certificate.cert.arn}"
validation_record_fqdns = ["${aws_route53_record.cert_validation.fqdn}"]
timeouts {
create = "45m"
}
}
#Christopher Thomas thank you for your initial answer
With current terraform version it needs some modifications in the certificate blocks and I propose to set a provider in case your main region is not us-east-1. Since the changes are too long for a comment I add another answer for now
# api-gateway / cloudfront certificates need to use the us-east-1 region
provider "aws" {
alias = "virginia"
region = "us-east-1"
}
resource "aws_acm_certificate" "cert" {
provider = aws.virginia
domain_name = local.api_subdomain
validation_method = "DNS"
}
resource "aws_acm_certificate_validation" "cert" {
provider = aws.virginia
certificate_arn = "${aws_acm_certificate.cert.arn}"
validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn]
}
resource "aws_route53_record" "cert_validation" {
for_each = {
for dvo in aws_acm_certificate.cert.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]
type = each.value.type
zone_id = data.aws_route53_zone.root_domain.zone_id
ttl = 60
}
resource "aws_acm_certificate_validation" "cert" {
provider = aws.virginia
certificate_arn = "${aws_acm_certificate.cert.arn}"
validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn]
}
# Finally Hook up Custom api domain name with api stage
resource "aws_api_gateway_base_path_mapping" "avxapi_base_path_mapping" {
api_id = aws_api_gateway_rest_api.xxxxxx.id
stage_name = aws_api_gateway_stage.xxxx.stage_name
domain_name = aws_api_gateway_domain_name.xxxx.domain_name
}
For me I have changed the aws_route53_record.cert_validation resource and it worked:
resource "aws_route53_record" "cert_validation" {
name = "${tolist(aws_acm_certificate.cert.domain_validation_options)[0].resource_record_name}"
type = "${tolist(aws_acm_certificate.cert.domain_validation_options)[0].resource_record_type}"
zone_id = "${aws_route53_record.sub_domain.zone_id}"
records = ["${tolist(aws_acm_certificate.cert.domain_validation_options)[0].resource_record_value}"]
ttl = 60
}