avatarGuillermo Musumeci

Summary

This article explains how to create Route 53 records from AWS cross-accounts using Terraform.

Abstract

The article discusses a method for creating records in a Route 53 Hosted Zone located on a different AWS account, which can be used as a reference to implement multi-account solutions in AWS with Terraform or share Route 53 domains across multiple AWS Accounts. The author notes that there are several ways to do this, but they found this way the simplest one to implement. The article provides code examples for defining multiple AWS accounts in the provider file, creating Route 53 records in the cross-account, creating an Application Load Balancer, and creating an SSL certificate using ACM.

Opinions

  • The author believes that this method is the simplest way to implement multi-account solutions in AWS with Terraform or share Route 53 domains across multiple AWS Accounts.
  • The author provides code examples to demonstrate their method, which suggests that they believe this method is effective and efficient.
  • The author notes that there are several ways to do this, which suggests that they are open to other methods and are not claiming that their method is the only way to achieve this goal.

How to Create Route 53 Records from AWS Cross-Accounts with Terraform

Over the last few weeks, I wrote a couple of stories on how to use Terraform to create internal and external load balancers and use ACM to create SSL certificates and write records in Route 53 (see below).

However, these two examples work only when we have the Route 53 public hosted zone in the same AWS account.

In this story, we will learn how to create records in a Route 53 Hosted Zone located on a different AWS account (usually called cross-account).

This story can be used as a reference to implement multi-account solutions in AWS with Terraform or share Route 53 domains across multiple AWS Accounts.

Note: There are several ways to do this, however, I found this way the simplest one to implement.

Defining Multiple AWS Accounts in the Provider File

A typical provider file should look like this:

# Terraform Provider
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.0"
    }
  }
}
# AWS Provider Credentials
provider "aws" {
  access_key = var.aws_access_key
  secret_key = var.aws_secret_key
  region     = var.aws_region
}

To be able to access another AWS account, in this case, we are going to call it main, we need to add a second provider with an alias. So the provider file will look like this:

# Terraform Provider
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.0"
    }
  }
}
# AWS Provider
provider "aws" {
  access_key = var.aws_access_key
  secret_key = var.aws_secret_key
  region     = var.aws_region
}
# Main AWS Provider
provider "aws" {
  alias      = "main"
  access_key = var.aws_access_key_main
  secret_key = var.aws_secret_key_main
  region     = var.aws_region_main
}

Creating Route 53 Records in the Cross-Account

Then, for this example, we are going to create a Load Balancer and an ACM SSL certificate in the Application AWS Account and Route 53 Records for the load balancer and ACM validation in the Main AWS Account.

Creating the Application Load Balancer

The code below will:

  • Create an Internal Application Load Balancer
  • Create a Load Balancer Target Group for HTTP
  • Attach EC2 Instances to the Application Load Balancer Target Group
  • Create an Application Load Balancer Listener for HTTP.

All these tasks are executed in the AWS Application Account.

# Create an Application Load Balancer
resource "aws_lb" "linux-alb" {
  name               = "linux-alb"
  internal           = true
  load_balancer_type = "application"
  security_groups    = [aws_security_group.linux-alb-sg.id]
  subnets            = local.ec2_subnet_list
  enable_deletion_protection = false
  enable_http2               = false
}
# Create a Load Balancer Target Group for HTTP
resource "aws_lb_target_group" "linux-alb-target-group-http" {  
  name     = "linux-alb-tg-http"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.vpc.id
  
  deregistration_delay = 60
  stickiness {
    type = "lb_cookie"
  }
  health_check {
    path                = "/"
    port                = 80
    healthy_threshold   = 3
    unhealthy_threshold = 3
    timeout             = 10
    interval            = 30
    matcher             = "200,301,302"
  }
}
# Attach EC2 Instances to Application Load Balancer Target Group
resource "aws_alb_target_group_attachment" "linux-alb-target-group-http-attach" {
  count = var.ec2_count
  target_group_arn = aws_lb_target_group.linux-alb-target-group-http.arn
  target_id        = aws_instance.linux-server[count.index].id
  port             = 80
}
# Create the Application Load Balancer Listener
resource "aws_lb_listener" "linux-alb-listener-http" {  
  depends_on = [
    aws_lb.linux-alb,
    aws_lb_target_group.linux-alb-target-group-http
  ]
  
  load_balancer_arn = aws_lb.linux-alb.arn
  port              = 80
  protocol          = "HTTP"
  
  default_action {    
    target_group_arn = aws_lb_target_group.linux-alb-target-group-http.arn
    type             = "forward"  
  }
}

Creating Amazon Route 53 A Record for the Load Balancer DNS record

Here, we create a reference to the Amazon Route 53 Public Zone, and then, using that data, we create a Route 53 DNS record for the load balancer. Both tasks are executed on our AWS Main Account.

# Reference to the Route 53 Main Public Zone
data "aws_route53_zone" "public-zone" {
  provider = aws.main
  
  name         = var.public_dns_name
  private_zone = false
}
# Create Route 53 A Record for the Load Balancer in the Main Zone
resource "aws_route53_record" "linux-alb-a-record" {
  provider = aws.main
  depends_on = [aws_lb.linux-alb]
  zone_id = data.aws_route53_zone.public-zone.zone_id
  name    = "${var.dns_hostname}.${var.public_dns_name}"
  type    = "A"
  alias {
    name                   = aws_lb.linux-alb.dns_name
    zone_id                = aws_lb.linux-alb.zone_id
    evaluate_target_health = true
  }
}

Creating the SSL Certificate using ACM

In this step, we will create the SSL certificate and the DNS Records in Route 53 for the ACM validation.

The resource “aws_acm_certificate” will create the ACM certificate in the AWS Application Account.

Then the resource “aws_route53_record” will create the Route 53 Validation Record on the AWS Main Account.

Finally, the resource “aws_acm_certificate_validation”, will create the ACM Certificate Validation in the AWS Application Account.

# Create Certificate
resource "aws_acm_certificate" "linux-alb-certificate" {
  domain_name       = "${var.dns_hostname}.${var.public_dns_name}"
  validation_method = "DNS"
}
# Create AWS Route 53 Certificate Validation Record in the Main Zone
resource "aws_route53_record" "linux-alb-certificate-validation-record" {
  provider = aws.main
  for_each = {
    for dvo in aws_acm_certificate.linux-alb-certificate.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         = data.aws_route53_zone.public-zone.zone_id
}
# Create Certificate Validation
resource "aws_acm_certificate_validation" "linux-certificate-validation" {
  certificate_arn = aws_acm_certificate.linux-alb-certificate.arn
  validation_record_fqdns = [for record in aws_route53_record.linux-alb-certificate-validation-record : record.fqdn]
}

Creating the Application Load Balancer Listener for HTTPS

Finally, we will create a listener for the port 443 and HTTPS protocol to forward traffic to the HTTP target group, in the AWS Application Account and we will link the SSL certificate created in the previous steps.

resource "aws_alb_listener" "linux-alb-listener-https" {
  depends_on = [
    aws_acm_certificate.linux-alb-certificate,
    aws_route53_record.linux-alb-certificate-validation-record,
    aws_acm_certificate_validation.linux-certificate-validation
  ]
  load_balancer_arn = aws_lb.linux-alb.arn
  port              = 443
  protocol          = "HTTPS"
  certificate_arn   = aws_acm_certificate.linux-alb-certificate.arn
  
  default_action {
    target_group_arn = aws_lb_target_group.linux-alb-target-group-http.arn
    type = "forward"
  }
}

Creating the Input Definition Variables File

In the last step, we will create the input definition variables file “terraform.tfvars” and add the following code to the file.

Then we add the AWS credentials for both our AWS Application Account and AWS Main Account, and we are ready to go!

# Application Definition 
app_name        = "kopicloud" # Do NOT enter any spaces
app_environment = "dev"       # Dev, Test, Staging, Prod, etc
# Network
vpc_cidr              = "10.11.0.0/16"
private_subnet_cidr_1 = "10.11.1.0/24"
private_subnet_cidr_2 = "10.11.2.0/24"
private_subnet_cidr_3 = "10.11.3.0/24"
public_subnet_cidr    = "10.11.4.0/24"
# AWS Settings
aws_region     = "eu-west-1"
aws_access_key = "complete-this"
aws_secret_key = "complete-this"
# AWS Settings Main
aws_region_main     = "eu-west-1"
aws_access_key_main = "complete-this"
aws_secret_key_main = "complete-this"
# DNS
public_dns_name = "kopicloud.com"
dns_hostname    = "lbmultitest"
# Linux Virtual Machine
ec2_count              = 2
linux_instance_type    = "t2.micro"
linux_root_volume_size = 20
linux_root_volume_type = "gp2"
linux_data_volume_size = 10
linux_data_volume_type = "gp2"

The full code is available at https://github.com/KopiCloud/terraform-aws-ec2-internal-alb-acm-multi-account

And that’s all, folks. If you liked this story, please show your support by 👏 this story. Thank you for reading!

Route 53
AWS
Terraform
Cross Account
Multi Account
Recommended from ReadMedium