Architecture and Key Concepts
The setup consists of a private S3 bucket hosting all static website files, which are then served globally via CloudFront distribution with multiple edge locations. This setup focuses on performance, security, and cost efficiency.
Architecture Flow
Browser -> CloudFront(Https) -> Private S3 Bucket
-S3 bucket stores static content such as index.html, CSS, JavaScript, images, and videos.
- The bucket is private, inaccessible directly by users.
- CloudFront, acting as a CDN, sits in front of the bucket and delivers cached content from edge locations nearest to users worldwide.
- Origin Access Control (OAC) securely restricts access so only CloudFront can fetch content from the S3 bucket.
- Bucket policies authorize CloudFront to access objects without making the bucket public.
Benefits of this architecture:
- Security: Keeps S3 bucket private; users never access S3 directly.
- Performance: CloudFront caches content regionally, reducing latency.
- Cost efficiency: Data transfer is regional, lowering bandwidth costs.
- Scalability: Supports global users seamlessly through distributed edge locations.
Core Components Defined:
S3 Bucket (aws_s3_bucket):
Created with a specified name (using variable input).
Configured as private with a Public Access Block resource preventing public access.
Public Access Block (aws_s3_bucket_public_access_block):
Explicitly disables all public access settings ensuring bucket privacy.
resource "aws_s3_bucket_public_access_block" "block" {
bucket = aws_s3_bucket.S3private.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
Origin Access Control (aws_cloudfront_origin_access_control):
Configured for CloudFront to securely access the S3 bucket with signing_behavior set to always and signing_protocol sigv4.
resource "aws_cloudfront_origin_access_control" "OAC" {
name = "Demo-OAC"
description = "Example Policy"
origin_access_control_origin_type = "s3"
signing_behavior = "always"
signing_protocol = "sigv4"
}
Bucket Policy (aws_s3_bucket_policy):
Allows CloudFront service principal to perform s3:GetObject requests on the bucket contents.
Utilizes JSON-encoded IAM policy including a condition restricting access to the CloudFront distribution’s ARN for added security.
resource "aws_s3_bucket_policy" "allow_cf_access" {
bucket = aws_s3_bucket.S3private.id
depends_on = [aws_s3_bucket_public_access_block.block]
policy = jsonencode({
"Statement" : [{
"Sid" : "AllowCloudFrontServicePrincipal",
"Effect" : "Allow",
"Principal" : {
"Service" : "cloudfront.amazonaws.com"
},
"Action" : [
"s3:GetObject",
],
"Resource" : "${aws_s3_bucket.S3private.arn}/*"
"Condition" = {
"StringEquals" = {
"AWS:SourceArn": "arn:aws:cloudfront::559050222413:distribution/E2FIT39W38INIF"
}
}
}]
"Version" = "2012-10-17"
})
}
S3 Objects Upload (aws_s3_object):
Uses for_each with Terraform’s fileset function to upload multiple files from a local www folder.
Sets proper content types dynamically per file extension using a lookup function to ensure correct MIME types.
resource "aws_s3_object" "object" {
bucket = aws_s3_bucket.S3private.id
for_each = fileset("C:\\Users\\maxycomppoint\\Downloads\\Documents\\AWS_TF\\Day14\\www", "**/*")
key = each.value
source = "C:\\Users\\maxycomppoint\\Downloads\\Documents\\AWS_TF\\Day14\\www\\${each.value}"
etag = filemd5("C:\\Users\\maxycomppoint\\Downloads\\Documents\\AWS_TF\\Day14\\www\\${each.value} ")
content_type = lookup({
"html" = "text/html"
"css" = "text/css"
"js" = "application/javascript"
"json" = "application/json",
"png" = "image/png",
"jpg" = "image/jpeg",
"jpeg" = "image/jpeg",
"gif" = "image/gif",
"svg" = "image/svg+xml",
"ico" = "image/x-icon",
"txt" = "text/plain"
}, split(".", each.value)[length(split(".", each.value)) - 1], "application/octet-stream")
}
CloudFront Distribution (aws_cloudfront_distribution):
Defines the distribution sourcing from the S3 bucket using the Origin Access Control.
Configured with a default root object (index.html), HTTPS redirection, minimal and default TTLs for caching, and limited allowed HTTP methods (GET, HEAD).
Uses CloudFront default SSL certificate (custom certificates from ACM left as an exercise).
resource "aws_cloudfront_distribution" "s3_distribution" {
origin {
domain_name = aws_s3_bucket.S3private.bucket_regional_domain_name
origin_access_control_id = aws_cloudfront_origin_access_control.OAC.id
origin_id = local.origin_id
}
enabled = true
is_ipv6_enabled = true
comment = "Some comment"
default_root_object = "index.html"
default_cache_behavior {
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
target_origin_id = local.origin_id
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_200"
restrictions {
geo_restriction {
restriction_type = "none"
}
}
viewer_certificate {
cloudfront_default_certificate = true
}
}
bucket_regional_domain_name not bucket_domain_name — use the regional domain. The older non-regional domain doesn't work correctly with OAC and can cause intermittent errors.
viewer_protocol_policy = "redirect-to-https" — HTTP requests are automatically upgraded to HTTPS. One line. Done.
default_root_object = "index.html" — when someone visits the root URL, CloudFront serves index.html rather than returning a 403.
Troubleshooting Terraform Errors (Important Skill)
The video demonstrates real debugging, including:
Incorrect IAM principals
Wrong resource ARNs
Policy syntax issues
Key lesson:
Terraform errors are part of learning — reading them carefully is essential.
Accessing the Live Website
After terraform apply, Terraform outputs:
CloudFront distribution domain name
Example:
(https://d123abcd.cloudfront.net)
✔ Website served securely over HTTPS
✔ Cached globally
🏁 Conclusion
Day 14 is a major real-world milestone in Terraform learning.
You’ve now built:
A secure static website
A globally cached architecture
A fully automated Terraform project
This project mirrors production-grade AWS setups and prepares you for:
Portfolio projects
Interviews
Real DevOps work

Top comments (0)