Menu

How to automate DNS and SSL certificates with Terraform and AWS

Apr 9, 2020 9:35:58 AM

When building web infrastructure, it used to be a really slow and often expensive process to get an SSL certificate, along with DNS records and overhead to keep the certificate from expiring. That isn't the case anymore.

AWS has made SSL certificates a whole lot easier, cheaper and faster than it used to be by becoming a certificate authority and issuing SSL certificates with Amazon Certificate Manager (ACM).

Certificates AWS generates can ONLY be used on AWS services, such as AWS ALB, but they have got some compelling reasons to use them, such as the certificates are publicly signed, free and renewed automatically.

AWS has also made DNS quick and easy to automate with AWS Route53 providing the ability to manage DNS records via automation even if they are just a short lived server, such as a demo spun up for an hour.

Prior to 2018, we could use Terraform to create DNS records, but there was a manual step to get ACM to work. This was that in order to use ACM, we used to have to prove the domain ownership manually by creating the certificate in AWS ACM, then adding the DNS record or responding to the email they send.

Turns out that in early 2018 Hashicorp started to work on adding a Terraform feature to enable full automation of ACM: aws_acm_certificate_validation. Using this, we can now automate the entire process of creating a free, publicly signed SSL certificate, even when the route53 zone is in a different AWS account.

I've created a little bit of example code in github that anyone is welcome to use as a basis for setting up AWS ACM. It is demonstrating Terraform connecting to two different AWS accounts and also defines a minimal ALB to demo the certificate. If your route53 zone is in the same account, you can just remove the lines "provider = aws.account_route53" in every resource to use the example for a single AWS account.

The demo includes two providers, as it assumes that the route53 zone is in a different AWS account. If you just use one account, then you only need one provider. 

Public Github project URL: https://github.com/nelg/terraform-aws-acmdemo 

In this SSL ACM Terraform demo we:

  1. look up the route53 zone
  2. create an ACM certificate
  3. create a route53 record to validate the ACM certificate
  4. get AWS ACM to check route53 for the validation certificate
  5. create a route53 record that matches the certificate name and points at an ALB
  6. print out the URL to test it with

The remainder of the github project is just minimal configuration needed to demo this working, so it creates a minimal ALB in a VPC.

Read more about AWS

# This data source looks up the public DNS zone
data "aws_route53_zone" "public" {
name = var.demo_dns_zone
private_zone = false
provider = aws.account_route53
}
# This creates an SSL certificate
resource "aws_acm_certificate" "myapp" {
domain_name = aws_route53_record.myapp.fqdn
validation_method = "DNS"
lifecycle {
create_before_destroy = true
}
}
# This is a DNS record for the ACM certificate validation to prove we own the domain
#
# This example, we make an assumption that the certificate is for a single domain name so can just use the first value of the
# domain_validation_options. It allows the terraform to apply without having to be targeted.
# This is somewhat less complex than the example at https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate_validation
# - that above example, won't apply without targeting
resource "aws_route53_record" "cert_validation" {
allow_overwrite = true
name = tolist(aws_acm_certificate.myapp.domain_validation_options)[0].resource_record_name
records = [ tolist(aws_acm_certificate.myapp.domain_validation_options)[0].resource_record_value ]
type = tolist(aws_acm_certificate.myapp.domain_validation_options)[0].resource_record_type
zone_id = data.aws_route53_zone.public.id
ttl = 60
provider = aws.account_route53
}
# This tells terraform to cause the route53 validation to happen
resource "aws_acm_certificate_validation" "cert" {
certificate_arn = aws_acm_certificate.myapp.arn
validation_record_fqdns = [ aws_route53_record.cert_validation.fqdn ]
}
# Standard route53 DNS record for "myapp" pointing to an ALB
resource "aws_route53_record" "myapp" {
zone_id = data.aws_route53_zone.public.zone_id
name = "${var.demo_dns_name}.${data.aws_route53_zone.public.name}"
type = "A"
alias {
name = aws_alb.mylb.dns_name
zone_id = aws_alb.mylb.zone_id
evaluate_target_health = false
}
provider = aws.account_route53
}
output "testing" {
value = "Test this demo code by going to https://${aws_route53_record.myapp.fqdn} and checking your have a valid SSL cert"
}
view raw ssl_cert.tf hosted with ❤ by GitHub

 

You May Also Like

These Stories on AWS

Subscribe by Email

Comments (2)