Setting up Amazon CloudFront to serve static content involves creating a distribution, configuring your origin (e.g., S3 bucket), and updating your DNS to route traffic through CloudFront
We will be exploring out how to achieve that in this guide using terraform.
Prepare your static site
We will create a simple static site based on react and typescript.
use this command to achieve the same. You need to have npm
command set up to proceed.
1
| npm create vite@latest my-app -- --template react-ts
|
Switch to the app and install dependencies
To test the site, use this command to run the dev version.
You should be able to browse to http://localhost:5173/
and see the app running in browser.
Let’s deploy it
Build prod version of the site using this command
The result is a ./dist
folder with the production build.
To confirm that the static files are working fine, we can use the python -m http.server
command, assuming python is installed.
1
2
3
| $ python -m http.server -d ./dist
Serving HTTP on :: port 8000 (http://[::]:8000/) ...
|
Then visit http://localhost:8000/
and you should see the app running.
Create S3 bucket
We will use S3 to store the content.
- We will create an s3 bucket
- We will upload our static files (HTML, CSS, JS, images, etc.) to the S3 bucket.
- Then we ensure our bucket policy allows access to the files. You can either make the bucket public or configure CloudFront to use an origin access control (OAC) for secure access.
Set up terraform provider
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
| terraform {
backend "s3" {
bucket = "citizix-platform-dev-terraform-state"
key = "e092590-stuff/envs/dev.tfstate"
region = "us-west-2"
}
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
locals {
aws_region = "us-west-2"
map_migrated = "migP3D39S7ZWQ"
name = "et-test-platform"
env = "dev"
vpc_id = "vpc-08f4c5661c06dcc87"
vpc_cidr_block = "10.10.0.0/19"
private_subnets = [
"subnet-04a79d40938495c72",
"subnet-05d868c7adfaaf084"
]
public_subnets = [
"subnet-0f955317ceee01151",
"subnet-054297e268e519d34"
]
tags = {
terraform = "yes"
environment = local.env
owner = "backend-team"
map-migrated = local.map_migrated
}
}
provider "aws" {
region = local.aws_region
# Make it faster by skipping something
skip_metadata_api_check = true
skip_region_validation = true
skip_credentials_validation = true
}
|
Set up the s3 bucket using terraform
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| module "onboarding_bucket" {
source = "git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git?ref=v4.2.1"
bucket = local.bucket_name
force_destroy = true
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
attach_policy = true
policy = jsonencode({
Version = "2008-10-17",
Id = "PolicyForCloudFrontPrivateContent",
Statement = [
{
Sid = "AllowCloudFrontServicePrincipal",
Effect = "Allow",
Principal = {
Service = "cloudfront.amazonaws.com"
},
Action = "s3:GetObject",
Resource = "arn:aws:s3:::${local.bucket_name}/*",
Condition = {
StringEquals = {
"AWS:SourceArn" = module.onboarding_cdn.cloudfront_distribution_arn
}
}
}
]
})
tags = local.tags
}
|
Create Cloudfront Distribution
Terraform code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
| module "onboarding_cdn" {
source = "git@github.com:terraform-aws-modules/terraform-aws-cloudfront.git?ref=v3.4.1"
aliases = [local.domain_name]
comment = "${local.env} citizix onboarding data"
enabled = true
is_ipv6_enabled = true
price_class = "PriceClass_All"
retain_on_delete = false
wait_for_deployment = false
default_root_object = "index.html"
create_origin_access_control = true
origin_access_control = {
onboarding_data = {
description = "CloudFront access to ${local.env} citizix onboarding data"
origin_type = "s3"
signing_behavior = "always"
signing_protocol = "sigv4"
}
}
origin = {
onboarding_data = {
domain_name = module.onboarding_bucket.s3_bucket_bucket_regional_domain_name
origin_access_control = "onboarding_data"
}
}
default_cache_behavior = {
target_origin_id = "onboarding_data"
viewer_protocol_policy = "redirect-to-https"
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
compress = true
query_string = true
}
custom_error_response = [{
error_code = 404
response_code = 200
response_page_path = "/"
}, {
error_code = 403
response_code = 200
response_page_path = "/"
}]
viewer_certificate = {
acm_certificate_arn = aws_acm_certificate.onboarding_cert.arn
minimum_protocol_version = "TLSv1.2_2021"
ssl_support_method = "sni-only"
}
tags = local.tags
}
|
Create ACM certificate
To use AWS Certificate manager, use this configuration:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| provider "aws" {
alias = "us_east_1"
region = "us-east-1"
}
resource "aws_acm_certificate" "onboarding_cert" {
provider = aws.us_east_1
domain_name = local.domain_name
validation_method = "DNS"
lifecycle {
create_before_destroy = true
}
tags = local.tags
}
|
Applying changes
Initialize terraform
Then apply the changes
Type yes to confirm
Copy content to the bucket
1
2
3
4
5
| aws s3 rm s3://prod-citizix-static-data/ --recursive
aws s3 cp --recursive ./dist/ s3://prod-citizix-static-data/
aws s3 ls s3://prod-citizix-static-data/
|
Visit your url https://dev-start.citizix.com/
and you should now access your content.
You can set up cicd to deploy your changes to production.