π§± Architecture Overview
High-level design:
Users β CloudFront (HTTPS + Edge Cache)
β
Private S3 Bucket
Key principles:
S3 bucket is NOT public
Only CloudFront can read objects
CloudFront caches content globally
Terraform manages everything
1οΈβ£ Why Not Use S3 Public Website Hosting Directly?
Direct S3 hosting problems:
Bucket must be public
No edge caching
Higher latency
Security risks
CloudFront solves this:
β
Global edge locations
β
HTTPS by default
β
Lower latency
β
Reduced S3 costs
β
Private S3 bucket
2οΈβ£ Creating a Private S3 Bucket with Terraform
Terraform creates the bucket and blocks all public access.
resource "aws_s3_bucket" "site" {
bucket = var.bucket_name
}
resource "aws_s3_bucket_public_access_block" "site" {
bucket = aws_s3_bucket.site.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
π This ensures no direct public access to S3.
3οΈβ£ Origin Access Control (OAC) β Secure CloudFront Access
AWS now recommends Origin Access Control (OAC) instead of the deprecated OAI.
resource "aws_cloudfront_origin_access_control" "oac" {
name = "s3-oac"
origin_access_control_origin_type = "s3"
signing_behavior = "always"
signing_protocol = "sigv4"
}
_
β Only CloudFront can access S3
β Modern & secure approach_
4οΈβ£ S3 Bucket Policy for CloudFront Access
The bucket policy allows only CloudFront to read objects.
resource "aws_s3_bucket_policy" "site_policy" {
bucket = aws_s3_bucket.site.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = {
Service = "cloudfront.amazonaws.com"
}
Action = "s3:GetObject"
Resource = "${aws_s3_bucket.site.arn}/*"
Condition = {
StringEquals = {
"AWS:SourceArn" = aws_cloudfront_distribution.site.arn
}
}
}]
})
}
_π This prevents:
Direct S3 access
Unauthorized reads
_
5οΈβ£ Uploading Static Files Using Terraform (for_each + fileset)
Terraform uploads HTML, CSS, JS files dynamically.
resource "aws_s3_object" "files" {
for_each = fileset("${path.module}/site", "*/")
bucket = aws_s3_bucket.site.id
key = each.value
source = "${path.module}/site/${each.value}"
etag = filemd5("${path.module}/site/${each.value}")
}
Why this matters:
No manual uploads
Files tracked in Terraform
MD5 ensures cache correctness
6οΈβ£ Creating the CloudFront Distribution
CloudFront configuration includes:
Private S3 origin
HTTPS redirection
Caching behavior
Default root object
resource "aws_cloudfront_distribution" "site" {
enabled = true
default_root_object = "index.html"
origin {
domain_name = aws_s3_bucket.site.bucket_regional_domain_name
origin_id = "s3-origin"
origin_access_control_id = aws_cloudfront_origin_access_control.oac.id
}
default_cache_behavior {
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
viewer_protocol_policy = "redirect-to-https"
target_origin_id = "s3-origin"
}
viewer_certificate {
cloudfront_default_certificate = true
}
}
β HTTPS enabled
β Fast global delivery
7οΈβ£ 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.
8οΈβ£ 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)