Terraform Recipes: CloudFront TLS certificates
At the end of the previous article, I briefly hinted at various intricacies of provisioning a TLS certificate for your CloudFront distribution. Let’s briefly have a look at what you can expect and how to deal with it!
At the end of the previous article, I briefly hinted at various intricacies of provisioning a TLS certificate for your CloudFront distribution. Let’s briefly have a look at what you can expect and how to deal with it!
In general, you will likely encounter two issues if you want to serve your CloudFront traffic over HTTPS: while most AWS services (for example, Elastic Load Balancer) require the certificates to be issued in the same region as the service itself is provisioned, the situation is quite different for CloudFront as that service is region-less. For CloudFront, all certificates must be issued in the North Virginia (us-east-1
) region.
The second issue is that before Amazon Certificate Manager turns your certificate request into a valid certificate, it requires you to validate the request either by e-mail or by provisioning a special DNS record. That means that if you try assigning the certificate to a CloudFront distribution while the generation is still in process (or the validation is pending), you’ll be met with a swift failure. But there’s an easy way around both issues!
Requesting freedom certificates 🦅
Let’s deal with the regional issue first. This is how you can force a region for a single Terraform resource, even if the rest of your Terraform resources reside in another region:
###################################
# ACM Certificate
###################################
resource "aws_acm_certificate" "cf_gitbook" {
provider = aws.virginia
domain_name = aws_s3_bucket.gitbook.bucket
validation_method = "DNS"
tags = {
Name = "${local.name_prefix}cf_gitbook"
Environment = terraform.workspace
}
lifecycle {
create_before_destroy = true
}
}
But where does the aws.virginia
value come from? Good question! You have to define a provider alias like so:
###################################
# Providers
###################################
provider "aws" {
version = "~> 2.0"
region = "ap-northeast-1"
}
provider "aws" {
alias = "virginia"
version = "~> 2.0"
region = "us-east-1"
}
Automatically validating certificate request ✅
As you can see in the snippet above, I chose to go for the DNS validation method – that’s because unlike with the e-mail validation method, it can be done automatically and the progress can be tracked by Terraform – that sounds like something we’d precisely want.
###################################
# Route 53 Record
###################################
resource "aws_route53_record" "cf_gitbook_validation" {
zone_id = aws_route53_zone.public[0].zone_id
name = aws_acm_certificate.cf_gitbook.domain_validation_options.0.resource_record_name
type = aws_acm_certificate.cf_gitbook.domain_validation_options.0.resource_record_type
records = [aws_acm_certificate.cf_gitbook.domain_validation_options.0.resource_record_value]
ttl = 300
}
With the record in place, we can track the progress of certificate issuance like so:
###################################
# ACM Certificate Validation
###################################
resource "aws_acm_certificate_validation" "cf_gitbook" {
provider = aws.virginia
certificate_arn = aws_acm_certificate.cf_gitbook.arn
validation_record_fqdns = [aws_route53_record.cf_gitbook_validation.fqdn]
}
We need to also specify the provider alias we created earlier as the aws_acm_certificate.cf_gitbook
only exists in the us-east-1
region. Fortunately, Route 53 is a global service, so we won’t run into any issues related to accessing resources from two different regions in one resource.
Putting it all together 🧩
But what was the purpose of creating the aws_acm_certificate_validation
? After all, it does not show anywhere in AWS Console, as it’s a virtual resource.
Well, while aws_acm_certificate
resource will show up in Terraform as created as soon as certificate issuance request is accepted, aws_acm_certificate_validation
will only register as created once validation and issuance is finished and the certificate is ready to be used. aws_acm_certificate_validation
resources can be used like so:
###################################
# CloudFront
###################################
resource "aws_cloudfront_distribution" "gitbook" {
…
viewer_certificate {
acm_certificate_arn = aws_acm_certificate_validation.cf_gitbook.certificate_arn
ssl_support_method = "sni-only"
minimum_protocol_version = "TLSv1.2_2018"
}
}
With the code above, the CloudFront distribution will start provisioning only once the certificate is ready to be used, exactly as we want it.