Terraform Hands-On: Setting Up a CDN with AWS CloudFront, S3, and Route 53 using Terraform

Introduction

I will walk you through setting up a Content Delivery Network (CDN) on AWS using CloudFront, an S3 bucket, and Route 53 for DNS management. We will use Terraform to automate the infrastructure setup. This guide includes the Terraform configuration, step-by-step instructions for applying the configuration, and screenshots from the AWS console.

Prerequisites AWS Account Terraform installed GitHub repository Hashnode account Project Structure Our project will have the following structure:

├── main.tf

├── provider.tf

├── variables.tf

├── terraform.tfvars

├── outputs.tf

Step-by-Step Guide

Create the Terraform Files.

  1. provider.tf

    This file specifies the AWS provider and the region.

provider "aws" {
  region = var.region
}
  1. main.tf

    This file contains the main configuration for the S3 bucket, CloudFront distribution, and Route 53 record.

resource "aws_s3_bucket" "bucket" {
  bucket = var.bucket_name
}

resource "aws_s3_bucket_website_configuration" "website" {
  bucket = aws_s3_bucket.bucket.id

  index_document {
    suffix = "index.html"
  }

  error_document {
    key = "error.html"
  }
}

resource "aws_s3_bucket_policy" "bucket_policy" {
  bucket = aws_s3_bucket.bucket.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Principal = "*"
        Action   = "s3:GetObject"
        Resource = "${aws_s3_bucket.bucket.arn}/*"
      },
    ]
  })
}

resource "aws_cloudfront_distribution" "cdn" {
  origin {
    domain_name = "${aws_s3_bucket.bucket.bucket_regional_domain_name}"
    origin_id   = "s3-bucket-origin"

    s3_origin_config {
      origin_access_identity = aws_cloudfront_origin_access_identity.origin_access_identity.cloudfront_access_identity_path
    }
  }

  enabled             = true
  is_ipv6_enabled     = true
  comment             = "CDN for ${var.bucket_name}"
  default_root_object = "index.html"

  default_cache_behavior {
    allowed_methods  = ["GET", "HEAD"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "s3-bucket-origin"

    forwarded_values {
      query_string = false
      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "redirect-to-https"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  price_class = "PriceClass_100"

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }

  aliases = [var.domain_name]
}

resource "aws_cloudfront_origin_access_identity" "origin_access_identity" {
  comment = "Origin access identity for ${aws_s3_bucket.bucket.id}"
}

resource "aws_route53_record" "cdn" {
  zone_id = var.hosted_zone_id
  name    = var.domain_name
  type    = "A"

  alias {
    name                   = aws_cloudfront_distribution.cdn.domain_name
    zone_id                = aws_cloudfront_distribution.cdn.hosted_zone_id
    evaluate_target_health = false
  }
}
  1. variable.tf

    This file defines the variables used in the configuration.

variable "region" {
  description = "The AWS region to create resources in"
  type        = string
  default     = "us-east-2"
}

variable "bucket_name" {
  description = "The name of the S3 bucket"
  type        = string
}

variable "domain_name" {
  description = "The domain name for the CloudFront distribution"
  type        = string
}

variable "hosted_zone_id" {
  description = "The Route 53 hosted zone ID"
  type        = string
}
  1. terraform.tfvars

    This file provides values for the variables.

region       = "us-east-2"
bucket_name  = "unique-buc-2412-website-21"
domain_name  = "cdn.yourdomain.com"
hosted_zone_id = "YOUR_HOSTED_ZONE_ID"

dc

  1. outputs.tf

    This file defines the outputs for the project.

output "s3_bucket_name" {
  value = aws_s3_bucket.bucket.bucket
}

output "cloudfront_distribution_id" {
  value = aws_cloudfront_distribution.cdn.id
}

output "cloudfront_domain_name" {
  value = aws_cloudfront_distribution.cdn.domain_name
}

output "cdn_url" {
  value = "https://${aws_cloudfront_distribution.cdn.domain_name}"
}

Steps to Apply the Terraform Configuration Initialize Terraform. Open your terminal and run the following command to initialize Terraform:

terraform init

Plan the Changes:

terraform plan

Apply the Changes:

terraform apply

Confirm with yes when prompted.

Verify the Setup

Go to the AWS Management Console and verify the following:

S3 Bucket: Ensure the bucket is created and has public access enabled via the bucket policy.

CloudFront Distribution: Verify the distribution is created and associated with the S3 bucket.

Route 53 Record: Confirm the DNS record is created and points to the CloudFront distribution.

Screenshots

Output the CDN URL

After running terraform apply, note the CDN URL provided in the output. You can use this URL to access your content delivered through CloudFront.

Conclusion

In this post, we've set up a CDN using AWS CloudFront, S3, and Route 53 with Terraform. This configuration helps in delivering static content with low latency and high availability. By automating the infrastructure setup, we ensure consistency and ease of management.

cvds