Implementing AWS CloudFront with Multiple Origin Cache Behavior using Terraform

In this technical blog post, we will explore how to implement AWS CloudFront with multiple origin cache behavior using Terraform. CloudFront’s multiple origin cache behavior allows you to configure a single CloudFront distribution to fetch content from different origins based on specified conditions. This capability is particularly useful when you need to serve content from multiple sources (such as different S3 buckets or custom origins) through a single CloudFront distribution efficiently.

Multiple origin cache behavior in AWS CloudFront enables a single CloudFront distribution to fetch content from multiple origins based on rules you define. This allows you to consolidate content delivery from various sources, optimizing network latency and improving content availability. By configuring cache behaviors, you can specify which requests CloudFront forwards to which origin, based on request path patterns, headers, query strings, or any combination thereof.

Architecture Overview

Before diving into the implementation details, let’s outline the architecture we’ll be working with:

Architecture

Step 1: Create primary and secondary S3 static websites

We’ll set up two S3 buckets to act as our primary and secondary origins. These buckets will host static websites that CloudFront will fetch content from. Also upload the files. Make sure you upload files to secondary/ in second S3 static website as we will create second cache behaviour with /secondary/* path pattern.

################################################################################
# Create S3 Static Website - primary and secondary
################################################################################
module "s3_primary" { source = "./modules/s3-static-website" bucket_name = var.bucket_name_primary source_files = "webfiles_primary" common_tags = local.common_tags naming_prefix = local.naming_prefix
} module "s3_secondary" { source = "./modules/s3-static-website" bucket_name = var.bucket_name_secondary source_files = "webfiles_secondary" common_tags = local.common_tags naming_prefix = local.naming_prefix
}
Enter fullscreen mode Exit fullscreen mode
################################################################################
# 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 = lookup(var.mime_map, reverse(split(".", each.value))[0], "text/plain") }
Enter fullscreen mode Exit fullscreen mode

Step 2: Create CloudFront distribution with default and path-pattern based cache behavior

Using Terraform, we’ll define a CloudFront distribution that includes multiple cache behaviors. One cache behavior will handle requests to a default origin (typically the primary S3 bucket), while another cache behavior will route requests based on path patterns to the secondary origin (secondary S3 bucket).

################################################################################
# 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" # Primary origin with default cache behavior origin { domain_name = data.aws_s3_bucket.s3_primary.bucket_regional_domain_name origin_id = "s3_primary" 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 = "s3_primary" viewer_protocol_policy = "allow-all" forwarded_values { query_string = false cookies { forward = "none" } } } # Secondary origin with path-pattern based cache behavior origin { domain_name = data.aws_s3_bucket.s3_secondary.bucket_regional_domain_name origin_id = "s3_secondary" origin_access_control_id = aws_cloudfront_origin_access_control.cf-s3-oac.id } ordered_cache_behavior { path_pattern = "/secondary/*" allowed_methods = ["GET", "HEAD"] cached_methods = ["GET", "HEAD"] target_origin_id = "s3_secondary" viewer_protocol_policy = "allow-all" default_ttl = 0 min_ttl = 0 max_ttl = 0 forwarded_values { query_string = true cookies { forward = "all" } } } price_class = "PriceClass_200" 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" })
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Create S3 bucket policy to allow access from CloudFront

To ensure that CloudFront can access the content stored in the S3 buckets securely, we’ll set up a bucket policy on both S3 buckets. This policy will allow CloudFront to fetch objects from the buckets for distribution.

################################################################################
# S3 bucket policy to allow access from cloudfront
################################################################################
module "s3_cf_policy_primary" { source = "./modules/s3-cf-policy" bucket_id = module.s3_primary.static_website_id bucket_arn = module.s3_primary.static_website_arn cloudfront_distribution_arn = module.cloud_front.cloudfront_distribution_arn
} module "s3_cf_policy_secondary" { source = "./modules/s3-cf-policy" bucket_id = module.s3_secondary.static_website_id bucket_arn = module.s3_secondary.static_website_arn cloudfront_distribution_arn = module.cloud_front.cloudfront_distribution_arn
} 
Enter fullscreen mode Exit fullscreen mode
################################################################################
# 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
}
Enter fullscreen mode Exit fullscreen mode

Steps to Run Terraform

Follow these steps to execute the Terraform configuration:

terraform init
terraform plan terraform apply -auto-approve
Enter fullscreen mode Exit fullscreen mode

Upon successful completion, Terraform will provide relevant outputs.

Apply complete! Resources: 16 added, 0 changed, 0 destroyed. Outputs: cloudfront_domain_name = "http://dgs42ejsdgx6n.cloudfront.net"
Enter fullscreen mode Exit fullscreen mode

Testing

Static Website Buckets:

S3 Websites

S3 Bucket Policies:

S3 Bucket Policy A

S3 Bucket Policy B

CloudFront Distribution:

CloudFront Distribution

CloudFront Origins:

Origins

CloudFront Cache Behaviours:

Cache Behaviours

Website Default Origin:

Default Origin Website

Website Secondary Origin:

Secondary Origin Website

Cleanup

Remember to stop AWS components to avoid large bills.

terraform destroy -auto-approve
Enter fullscreen mode Exit fullscreen mode

Conclusion

Implementing AWS CloudFront with multiple origin cache behavior using Terraform provides a scalable and efficient way to manage content delivery from diverse sources. By following the steps outlined and leveraging Terraform’s infrastructure-as-code capabilities, you can achieve robust and flexible content delivery configurations tailored to your application’s needs.

Resources

CoudFront Multiple Origins: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/DownloadDistS3AndCustomOrigins.html

CloudFront Cache Behavior: https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CacheBehavior.html

Github Link: https://github.com/chinmayto/terraform-aws-cloudfront-multiple-origin-cache-behavior


Discover more from Coursity

Subscribe to get the latest posts sent to your email.

Leave a Comment

Your email address will not be published. Required fields are marked *

Discover more from Coursity

Subscribe now to keep reading and get access to the full archive.

Continue reading

Scroll to Top