Creating multiple DNS names by messing with Cloudfront, Route53 and ACM

I created my blog’s infrastructure in AWS with this repository as base. It served me well, but the time as come to tweak it a little bit.

In this post I will explain how I went from having only the DNS name available to having, and as well. Before reading this make sure you understand this explanation on the link above.

When I first approached the problem, in my head I was like: Well, basically all I have to get this done is creating some CNAME or ALIAS records in Route53. But when I created them I got errors from CloudFront. I found out that I also needed to configure Cloudfront’s aliases with the new domain names, and to cope with that I also needed to update my websites certificate to contain * and *

I won’t post the full code because I keep my infra code in the same repo as I keep the blog, but I will explain the changes I’ve made.

It all starts with this variable.

variable "alternative_domain_names" {
  description = ""
  type        = map(list(string))
  default = {
    "*"      = ["", ""]
    "*" = [""]

The keys

Are the alternative domain names to add to my certificate. Notice the subject_alternative_names, and the multi cert_validation resource, which will cope with our alternative domain names, two in this case (*, *

# The following resources generate an SSL certificate and
# validate it.
resource "aws_acm_certificate" "cert" {
  domain_name               = var.domain_name
  subject_alternative_names = keys(var.alternative_domain_names)
  validation_method         = "DNS"

resource "aws_route53_record" "cert_validation" {

  count = length(keys(var.alternative_domain_names))

  name    = aws_acm_certificate.cert.domain_validation_options[count.index].resource_record_name
  type    = aws_acm_certificate.cert.domain_validation_options[count.index].resource_record_type
  records = [aws_acm_certificate.cert.domain_validation_options[count.index].resource_record_value]
  ttl     = 60

resource "aws_acm_certificate_validation" "cert" {
  certificate_arn         = aws_acm_certificate.cert.arn
  validation_record_fqdns = aws_route53_record.cert_validation.*.fqdn

The values

Will be used to update the cloudfront’s aliases variable and Route53 records. (ALIAS are not as clean as CNAMES, in my opinion, but I ended up using because they are free).

resource "aws_cloudfront_distribution" "cdn" {
  aliases             = concat(flatten(values(var.alternative_domain_names)), [var.domain_name])

resource "aws_route53_record" "alias" {

  for_each = toset(concat(flatten(values(var.alternative_domain_names)), [var.domain_name]))
  zone_id  =
  name     = each.value
  type     = "A"

  alias {
    name                   = aws_cloudfront_distribution.cdn.domain_name
    zone_id                = aws_cloudfront_distribution.cdn.hosted_zone_id
    evaluate_target_health = false

In the end this ends up being quite a simple change although I spent quite some time dealing with minor issues, trying to understand what was missing (being noob to AWS and Terraform does not make things easier). The aws docs where a good reference.

Anyway, this is my solution and I’m happy with it, hope this helps someone.

· aws, s3, cloudfront, route53, acm, terraform