In a previous blog post, we delved into the mechanics of hosting a static website using Amazon S3. Now, we're taking it up a notch by integrating Amazon CloudFront, a Content Delivery Network (CDN), into our architecture. By doing so, we aim to optimize website performance and elevate user experience to new heights. In this post, we'll walk through the process of seamlessly integrating CloudFront with S3 using Terraform modules.
What is CloudFront:
Amazon CloudFront is a web service that speeds up distribution of your static and dynamic web content, such as .html, .css, .js, and image files, to your users. CloudFront delivers your content through a worldwide network of data centers called edge locations. When a user requests content that you're serving with CloudFront, the request is routed to the edge location that provides the lowest latency (time delay), so that content is delivered with the best possible performance.
If the content is already in the edge location with the lowest latency, CloudFront delivers it immediately.
If the content is not in that edge location, CloudFront retrieves it from an origin that you've defined—such as an Amazon S3 bucket, a MediaPackage channel, or an HTTP server (for example, a web server) that you have identified as the source for the definitive version of your content.
CloudFront speeds up the distribution of your content by routing each user request through the AWS backbone network to the edge location that can best serve your content.
Architecture Diagram:
Let's start by outlining the architecture we'll be working with:
Step 1: Create an S3 bucket with a unique name and host the static website by uploading files.
####################################################
# S3 static website bucket
####################################################
resource "aws_s3_bucket" "s3-static-website" {
bucket = var.bucket_name
tags = merge(var.common_tags, {
Name = "${var.naming_prefix}-s3-bucket"
})
}
####################################################
# S3 public access settings
####################################################
resource "aws_s3_bucket_public_access_block" "static_site_bucket_public_access" {
bucket = aws_s3_bucket.s3-static-website.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
####################################################
# S3 bucket static website configuration
####################################################
resource "aws_s3_bucket_website_configuration" "static_site_bucket_website_config" {
bucket = aws_s3_bucket.s3-static-website.id
index_document {
suffix = "index.html"
}
error_document {
key = "error.html"
}
}
####################################################
# Upload files to S3 Bucket
####################################################
resource "aws_s3_object" "provision_source_files" {
bucket = aws_s3_bucket.s3-static-website.id
# webfiles/ is the Directory contains files to be uploaded to S3
for_each = fileset("${var.source_files}/", "**/*.*")
key = each.value
source = "${var.source_files}/${each.value}"
content_type = each.value
}
Step 2: Configure a CloudFront distribution to serve as the CDN for our website.
data "aws_s3_bucket" "selected_bucket" {
bucket = var.s3_bucket_id
}
####################################################
# Create AWS Cloudfront distribution
####################################################
resource "aws_cloudfront_origin_access_control" "cf-s3-oac" {
name = "CloudFront S3 OAC"
description = "CloudFront S3 OAC"
origin_access_control_origin_type = "s3"
signing_behavior = "always"
signing_protocol = "sigv4"
}
resource "aws_cloudfront_distribution" "cf-dist" {
enabled = true
default_root_object = "index.html"
origin {
domain_name = data.aws_s3_bucket.selected_bucket.bucket_regional_domain_name
origin_id = var.s3_bucket_id
origin_access_control_id = aws_cloudfront_origin_access_control.cf-s3-oac.id
}
default_cache_behavior {
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
target_origin_id = var.s3_bucket_id
forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
viewer_protocol_policy = "allow-all"
min_ttl = 0
default_ttl = 3600
max_ttl = 86400
}
price_class = "PriceClass_All"
restrictions {
geo_restriction {
restriction_type = "whitelist"
locations = ["IN", "US", "CA"]
}
}
viewer_certificate {
cloudfront_default_certificate = true
}
tags = merge(var.common_tags, {
Name = "${var.naming_prefix}-cloudfront"
})
}
Step 3: Update the S3 Bucket policy to allow access from CloudFront.
To ensure that our S3 bucket only allows access through CloudFront, we'll update its bucket policy accordingly.
####################################################
# S3 bucket policy to allow access from cloudfront
####################################################
data "aws_iam_policy_document" "s3_bucket_policy" {
statement {
actions = ["s3:GetObject"]
resources = ["${var.bucket_arn}/*"]
principals {
type = "Service"
identifiers = ["cloudfront.amazonaws.com"]
}
condition {
test = "StringEquals"
variable = "AWS:SourceArn"
values = [var.cloudfront_distribution_arn]
}
}
}
resource "aws_s3_bucket_policy" "static_site_bucket_policy" {
bucket = var.bucket_id
policy = data.aws_iam_policy_document.s3_bucket_policy.json
}
Steps to Run Terraform
Follow these steps to execute the Terraform configuration:
terraform init
terraform plan
terraform apply -auto-approve
Upon successful completion, Terraform will provide relevant outputs.
Apply complete! Resources: 9 added, 0 changed, 0 destroyed.
Outputs:
cloudfront_domain_name = "http://d1rwkmekbjnbkd.cloudfront.net"
Testing the outcome
S3 Bucket
Block public access:
Static Website setting:
CloudFront Distribution:
CloudFront Distribution Origin as S3 with Origin Access Control OAC:
S3 Bucket Policy to allow access from cloudfront
Using cloudfront domain name to access S3 static website:
Website accessed from country which is not in allowlist (used soft VPN)
Cache invalidation occurred after TTL timeout and now website has different image (after static website content was updated)
Cleanup
Remember to stop AWS components to avoid large bills.
terraform destroy -auto-approve
Notes:
- Cache Invalidation can be forced from console for a cloudfront distribution
- Deprecated Origin Access Identity OAI also can be used instead of Origin Access Control OAC
Incorporating Amazon CloudFront into our static website architecture significantly enhances performance and improves user experience.
In the next module, we'll extend our exploration to leverage CloudFront with EC2 servers, further optimizing content delivery for dynamic web applications.
Resources:
GitHub Link: https://github.com/chinmayto/terraform-aws-cloudfront-s3
AWS CloudFront: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Introduction.html
Top comments (0)