Terraform can provision CDN infrastructure, but it’s not just about creating resources; it’s about defining a declarative, auditable, and repeatable system for delivering content globally.
Let’s see it in action. Imagine we’re setting up a basic CDN for a static website hosted on S3.
provider "aws" {
region = "us-east-1"
}
resource "aws_s3_bucket" "static_site" {
bucket = "my-unique-static-website-bucket-12345"
acl = "public-read"
website {
index_document = "index.html"
error_document = "error.html"
}
tags = {
Name = "My Static Website Bucket"
}
}
resource "aws_cloudfront_distribution" "s3_distribution" {
origin {
domain_name = aws_s3_bucket.static_site.bucket_regional_domain_name
origin_id = "my-s3-origin"
}
enabled = true
is_ipv6_enabled = true
comment = "CDN for my static website"
default_root_object = "index.html"
aliases = ["www.example.com"]
default_cache_behavior {
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "my-s3-origin"
forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
viewer_protocol_policy = "redirect-to-https"
min_ttl = 0
default_ttl = 3600
max_ttl = 86400
}
# Optional: Add custom error responses
custom_error_response {
error_code = 403
response_code = 404
error_caching_min_ttl = 100
response_page_path = "/error.html"
}
# Optional: Configure logging
logging_config {
bucket = "my-cdn-logs-bucket-12345.s3.amazonaws.com"
prefix = "my-website-logs/"
}
viewer_certificate {
cloudfront_default_certificate = true
ssl_support_method = "sni-only"
}
}
This Terraform code defines an AWS S3 bucket to hold our static files and a CloudFront distribution to serve them. The aws_cloudfront_distribution resource is where the CDN magic happens.
The origin block tells CloudFront where to fetch content from – in this case, our S3 bucket. domain_name points to the S3 bucket’s website endpoint, and origin_id is a logical name for this origin.
The default_cache_behavior is crucial. It dictates how CloudFront handles requests:
allowed_methodsandcached_methodsdefine which HTTP methods CloudFront can pass to the origin and cache. For static sites,GETandHEADare typical.forwarded_valuescontrols what parts of the request (query strings, cookies, headers) are inspected and forwarded to the origin. For simple static sites, forwarding none of these often improves cache hit ratios.viewer_protocol_policy = "redirect-to-https"ensures all traffic is served over HTTPS.min_ttl,default_ttl, andmax_ttldefine the Time To Live for cached objects, controlling how long CloudFront holds onto a file before checking with the origin for updates.
The aliases block lets you associate custom domain names (like www.example.com) with your CloudFront distribution. This means users see your domain in the URL, not the default CloudFront domain.
The viewer_certificate block configures SSL/TLS. For simplicity, cloudfront_default_certificate = true uses a certificate managed by AWS for your CloudFront domain. If you were using custom domains, you’d typically import your own ACM certificate here.
Terraform handles the entire lifecycle. Running terraform apply creates these resources. terraform destroy tears them down cleanly. This means you have an exact, version-controlled record of your CDN setup.
What most people don’t realize is that CloudFront’s caching isn’t just about headers; it’s also deeply influenced by the origin’s response headers. If your S3 bucket or origin server doesn’t send appropriate Cache-Control or Expires headers, CloudFront will use its own TTLs, which might not be optimal. You can also configure CloudFront to respect or ignore origin cache headers via the forwarded_values block’s headers setting and the cache_policy attribute in newer Terraform versions.
The next step is often integrating dynamic content or implementing advanced caching strategies like query string or cookie forwarding.