ScanSkill
Sign up for daily dose of tech articles at your inbox.
Loading

Configure AWS CloudFront CDN With Certificate Using Terraform

Configure AWS CloudFront CDN With Certificate Using Terraform
Configure AWS CloudFront CDN With Certificate Using Terraform

AWS CloudFront is a content delivery network(CDN) service that delivers web content using different global edge locations. In this tutorial, we’ll create the AWS Cloudfront CDN, request a certificate, and validate it using Terraform IaC.

So, let’s dive into configuring CloudFront CDN with certificate using terraform:

PREREQUISITES:

  • Terraform installed (For this tutorial I’ll be using Terraform V1.1.6)
  • Domain name registered in Route53
  • Exported AWS account programmatic credentials as environment variables (AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY)

How To Configure AWS CloudFront CDN With Certificate Using Terraform:

Step-1: Create S3 Bucket

In the first step, let’s create an S3 bucket that will be used as an origin in CloudFront distributions. In which all the files and assets will be stored. OR if you have already set up a bucket, you can use the bucket name directly in your configuration.

cloudfront_s3.tf:

resource "aws_s3_bucket" "product_bucket" {
  bucket = "${var.bucket_name}"
  acl = "public-read"
  policy = <<POLICY
{
  "Version":"2012-10-17",
  "Statement":[{
    "Sid":"PublicReadForGetBucketObjects",
      "Effect":"Allow",
      "Principal": "*",
      "Action":"s3:GetObject",
      "Resource":["arn:aws:s3:::${var.bucket_name}/*"
      ]
    }
  ]
}
POLICY
}

Step-2: Certificate for CloudFront Distribution

In this part, we’re going to create a certificate for CNAME used in our CloudFront distribution. For this, make sure you have a custom domain configured.

To create a certificate for CloudFront, you have to request or import the certificate in the us-east-1 region(as mentioned here). But you can use any region if you’re using load balancer as your CloudFront origin.

Since I’m using a different region than us-east-1 for CloudFront distribution, I’ll be using the alias method to validate and create resources in a different region.

Requesting Certificate:

cloudfront_certificate.tf:

provider "aws" {
  alias   = "us_east"
  region  = "us-east-1"
#   profile = var.profile  # if you're using profile for aws credentials
}

resource "aws_acm_certificate" "cloudfront_cdn" {
  provider  = aws.us_east
  domain_name = "*.cdn.${var.domain_name}"
  validation_method = "DNS"

  tags = {
      name = "certificate for cloudfront distribution"
  }

  lifecycle {
    create_before_destroy = true
  }
}

Validating Certificate:

For this tutorial, we’re going to create a certificate, validate it using DNS because DNS validation is faster and simpler. And also add records into the Route53 hosted zone.

When you request the certificate, ACM returns a CNAME for you to insert in your hosted zone. Then, it pings that domain and verifies that the value it returns matches what it expects. CNAME has an array of domain_validation_options with four fields:

  • domain (domain name)
  • resourse_record_name (DNS record name)
  • resourse_record_type (type of record: “CNAME”)
  • resourse_record_value (the value that CNAME points to)

Then, validate certificate using aws_acm_certificate_validation specifying certificate’s ARN and CNAME record’s fully qualified domain name(fqdn):

in cloudfront_certificate.tf:

data "aws_route53_zone" "zone" {
  name         = var.domain_name
  private_zone = false
}

# DNS validation
resource "aws_route53_record" "record_validation" {
  zone_id = data.aws_route53_zone.zone.id
  name    = tolist(aws_acm_certificate.cloudfront_cdn.domain_validation_options)[0].resource_record_name
  type    = tolist(aws_acm_certificate.cloudfront_cdn.domain_validation_options)[0].resource_record_type
  records = [tolist(aws_acm_certificate.cloudfront_cdn.domain_validation_options)[0].resource_record_value]
  ttl     = var.ttl
  allow_overwrite = true
}

# Certificate validation
resource "aws_acm_certificate_validation" "certificate_validation" {
  provider                = aws.us_east
  certificate_arn         = aws_acm_certificate.cloudfront_cdn.arn
  validation_record_fqdns = [aws_route53_record.record_validation.fqdn]
}

Add A record to route53 hosted zone:

In cloudfront_certificate.tf:

resource "aws_route53_record" "a_record" {
    zone_id = var.hosted_zone_id
    name  = "${var.route53_record_name}.${var.domain_name}"
    type  = "A"

    alias {
        name = aws_cloudfront_distribution.product_s3_distribution.domain_name
        zone_id = var.alias_zone_id
        evaluate_target_health = false
    }

    depends_on = [
      aws_cloudfront_distribution.product_s3_distribution
    ]
}

Also Read :

Step-3: CloudFront Distribution Set Up

Now, set up the bucket we just created as the origin in CloudFront distribution.

main.tf:

# Add product cloudfront distribution
resource "aws_cloudfront_distribution" "product_s3_distribution" {
  origin {
    domain_name = "${var.bucket_name}.s3.amazonaws.com"
    origin_id   = var.bucket_name
  }

  enabled             = true   # enable CloudFront distribution
  is_ipv6_enabled     = true
  comment             = "CloudFront distribution for staging"

  aliases = ["${var.route53_record_name}.${var.domain_name}"]
  default_cache_behavior {
		# HTTPS requests we permit the distribution to serve
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = var.bucket_name

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0        # min time for objects to live in the distribution cache
    default_ttl            = 3600     # default time for objects to live in the distribution cache
    max_ttl                = 86400    # max time for objects to live in the distribution cache
  }

  restrictions {
    geo_restriction {
        restriction_type = "none"
    }
  }

  viewer_certificate {
    # cloudfront_default_certificate = true   # use this if you don't have certificate
    acm_certificate_arn = aws_acm_certificate.cloudfront_cdn.arn
    ssl_support_method = "sni-only"
  }

  depends_on = [aws_acm_certificate.cloudfront_cdn]
}

All required variables:

variables.tf:

variable "bucket_name" {
    default = "example-product-media-bucket"
}

variable "hosted_zone_id" {
    type    = string
    default = "Z089411011X86LT4AC" # import from route53 hosted zone
}

variable "hosted_zone" {
    type = string
    default = "example.com"
}

variable "domain_name" {
    description = "Domain name for staging"
    default = "example.com"
}

variable "route53_record_name" {
    description = "Record name for cloudfront distribution"
    default = "dev.cdn" # dev.cdn, <app_name>.cdn, etc.
}

variable "alias_zone_id" {Conclusion
    description = "Hardcoded zonde_id for all cloudfront distribution"
    default = "Z2FDTNDATAQYW2" # FIXED VALUE
}

variable "ttl" {
    default = 60
}

Note: Here alias_zone_id is a hardcoded constant as it’s a fixed constant used for all CloudFront distributions.

Now run plan command and apply changes:

terraform plan
terraform apply

Step-4: Testing

You can test the performance, for this you can use a free online tool: tool.keycdn.com/performance:

Without CDN:

configure aws cloudfront using terraform
configure aws cloudfront using terraform

Here, for an object in S3 bucket, it took 1.24 seconds to receive its first byte in Bangalore India from the server(eu-central-1).

With CDN:

Performance test with CloudFront CDN

When using CloudFront distribution, it took just 57.16 milliseconds to receive its first byte in Bangalore India from the server(frankfurt eu-central-1).

That’s all!

Conclusion

In this article, we covered the AWS CloudFront CDN With Certificate Using Terraform. And also tested with free online tool.
Also, check out my article on how to deploy static website on s3. And you can set that s3 as an origin for CloudFront to serve your static website.

Similar article; ElasticSearch on AWS EC2 using Terraform
Also read my article on AWS-CDK: How To Set Up AWS CDK With Python

Thank you!

Sign up for daily dose of tech articles at your inbox.
Loading