Published on: Sat Nov 12 2022
When working with Amazon Cloudfront, you need to start off by creating a “distribution”.
These distribution contain configurations that tell Cloudfront how to deliver the content, where to get it from etc.
In this guide, we will be going over the steps on how to achieve this using Terraform!
Let’s get right into it.
To setup the sanbox for the upcoming tutorial to learn cloudfront policies, we will be creating a basic architecture with Amazon Cloudfront, Lambda and Cloudwatch.
The Cloudfront distribution will sit at the edge locations and act as the gateway to our Lambda (origin server).
Then we will also have logs of the forwarded requests that are addded to Cloudwatch logs.
Illustration of the sandbox architecture with Cloudfront and Lambda
The Viewer (client) request comes in
Cloudfront forwards the request to the origin server (Lambda URL + Lambda)
The logs from the request are added into Cloudwatch Logs
The Lambda function returns a response
The Viewer (client) receives the response
Before you start going through the tutorial, make sure you are using the starter - amazon-cloudfront-policies-starter.
This streamlines some of the things like building the lambda functions and writing out all the boilerplate files.
It will be the base from which we will build from!
Now onto setting up the infrastructure.
Since S3 buckets need to be globally unique, you will need to change the name.
This S3 bucket is for holding the assets of our Lambda functions.
So, within infra/main.tf
, make the edit on the S3 resource name:
Add the following changes:
// infra/main.tf
resource "aws_s3_bucket" "lambda_bucket" {
bucket = "<insert-name-here>"
acl = "private"
}
Now Lambda function supports an option to expose an URL, so we can use our function as our “origin server” for testing the request origin policy.
Add the following changes:
// infra/main.tf
resource "aws_lambda_function_url" "origin" {
function_name = module.lambda_origin.lambda[0].function_name
qualifier = "origin-server-fn"
# For testing purposes we won’t have authorization
authorization_type = "NONE"
}
Now that we have our lambda function and url, its time to setup our cloudfront distribution.
Add the following changes:
// infra/main.tf
resource "aws_cloudfront_distribution" "cf_distribution" {
origin {
# This is required because "domain_name" needs to be in a specific format
domain_name = replace(replace(aws_lambda_function_url.origin.function_url, "https://", ""), "/", "")
origin_id = module.lambda_origin.lambda[0].function_name
custom_origin_config {
https_port = 443
http_port = 80
origin_protocol_policy = "https-only"
origin_ssl_protocols = ["TLSv1.2"]
}
}
}
In all Cloudfront distribution, and for every origin, you need a default cache behavior.
Let’s set that up.
Add the following changes:
// infra/main.tf
resource "aws_cloudfront_distribution" "cf_distribution" {
origin {
# This is required because "domain_name" needs to be in a specific format
domain_name = replace(replace(aws_lambda_function_url.origin.function_url, "https://", ""), "/", "")
origin_id = module.lambda_origin.lambda[0].function_name
custom_origin_config {
https_port = 443
http_port = 80
origin_protocol_policy = "https-only"
origin_ssl_protocols = ["TLSv1.2"]
}
}
default_cache_behavior {
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
target_origin_id = module.lambda_origin.lambda[0].function_name
forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
viewer_protocol_policy = "redirect-to-https"
min_ttl = local.min_ttl
default_ttl = local.default_ttl
max_ttl = local.max_ttl
}
}
When creating the Cloudfront distribution, there are a few options that are required.
We are just going to focus on the minimum options that we need to create a distribution.
Be sure to check the documentation for all the other options.
Add the following changes:
// infra/main.tf
resource "aws_cloudfront_distribution" "cf_distribution" {
origin {
# This is required because "domain_name" needs to be in a specific format
domain_name = replace(replace(aws_lambda_function_url.origin.function_url, "https://", ""), "/", "")
origin_id = module.lambda_origin.lambda[0].function_name
custom_origin_config {
https_port = 443
http_port = 80
origin_protocol_policy = "https-only"
origin_ssl_protocols = ["TLSv1.2"]
}
}
default_cache_behavior {
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
target_origin_id = module.lambda_origin.lambda[0].function_name
viewer_protocol_policy = "redirect-to-https"
min_ttl = local.min_ttl
default_ttl = local.default_ttl
max_ttl = local.max_ttl
}
price_class = var.cf_price_class
enabled = true
is_ipv6_enabled = true
comment = "origin request policy test"
default_root_object = "index.html"
restrictions {
geo_restriction {
restriction_type = "none"
}
}
tags = {
Environment = "production"
}
viewer_certificate {
cloudfront_default_certificate = true
}
}
The outputs will show us the information of the generated resources (cloudfront, lambda urls) we have defined when they are created.
// infra/ouputs.tf
output "function_alias_name_origin" {
description = "Name of the function alias"
value = module.lambda_origin.alias[0].name
}
output "lambda_url" {
description = "Lambda URL"
value = aws_lambda_function_url.origin.function_url
}
output "cf_distribution_domain_url" {
value = "https://${aws_cloudfront_distribution.cf_distribution.domain_name}"
}
Now we have all our definition in Terraform, all that is left is to generate it.
Run the following:
// This will re-generate the assets
pnpm run generate-assets --filter "@function/*"
export AWS_ACCESS_KEY_ID=<your-key>
export AWS_SECRET_ACCESS_KEY=<your-secret>
export AWS_DEFAULT_REGION=us-east-1
terraform init
terraform plan
terraform apply -auto-approve
If the infrastructure applied successfully then you should see something like this:
Illustration of the Terraform outputs
Curl:
curl -vvv "<cf_distribution_domain_url>"
Postman:
Illustration of using Postman for the request
Apart from the response from cloudfront, be sure to check the logs of your lambda function (our origin server).
Make you are able to see it as it will be useful later when we start to integrate the origin request policies.
Illustration of the Lambda function logs/blockquote>
For reference, here is the repository of completed tutorial - Github: amazon-cloudfront-create-distribution.
Congrats! You’ve just setup a Cloudfront distribution that uses a Lambda function as the server to respond to requests.
In practice, a more common approach is pointing the Cloudfront distribution to an Amazon S3 Bucket (There likely will be a tutorial on this as well!).
For our use case of learning about cloudfront policies, we’ll use this setup as our sandbox!
If you aren’t sure what cloudfront policies are or need a refresher, then be sure to check out my article on it 👉 Amazon Cloudfront: An overview of policies.
And... that’s all for now, stay tuned for more!
If you found this helpful or learned something new, please do share this article with a friend or co-worker 🙏❤️ (Thanks!)
Then consider signing up to get notified when new content arrives!