Static Delivery: Cloud CDN caching a Cloud Storage Bucket
Scenario
You have a folder of static frontend assets (images, CSS, JS) that need to be delivered to global users with extremely low latency. Instead of serving them directly from a Cloud Storage bucket in one region, you will place a Global HTTP Load Balancer with Cloud CDN in front of the bucket.
- Create a google_storage_bucket for your assets, and grant `allUsers` the `roles/storage.objectViewer` role to make it public.
- Create a google_compute_backend_bucket mapped to the storage bucket, and set enable_cdn = true.
- Create the Load Balancing frontend: google_compute_url_map, google_compute_target_http_proxy, and google_compute_global_forwarding_rule.
- Output the global IP address of the CDN.
Additional Context
Backend Buckets vs Backend Services: In GCP Load Balancing, if you are routing traffic to VMs or Cloud Run, you use a `backend_service` (which requires a health check). If you are routing traffic to an S3/GCS bucket, you use a special `backend_bucket` resource (which natively integrates with Cloud Storage and requires no health check).
Enabling CDN: Once traffic hits the `backend_bucket`, if `enable_cdn = true`, Google's edge nodes intercept the request, check their local cache, and only forward the request down to the actual Cloud Storage bucket if it's a cache miss.
variables.tf
variable "project_id" { type = string }
variable "gcp_region" { type = string; default = "us-central1" }main.tf
provider "google" {
project = var.project_id
}
resource "random_id" "suffix" { byte_length = 4 }
# 1. The underlying Storage Bucket
resource "google_storage_bucket" "assets" {
name = "cdn-assets-${random_id.suffix.hex}"
location = "US" # multi-region
force_destroy = true
}
# Make bucket contents public (required for Backend Buckets without complex signed URLs)
resource "google_storage_bucket_iam_member" "public_read" {
bucket = google_storage_bucket.assets.name
role = "roles/storage.objectViewer"
member = "allUsers"
}
# 2. Add an index.html so there's something to serve
resource "google_storage_bucket_object" "index" {
name = "index.html"
bucket = google_storage_bucket.assets.name
content = "Hello from Cloud CDN Edge Cache!
"
content_type = "text/html"
}
# 3. The Backend Bucket (ties the bucket to the Load Balancer, enables CDN cache)
resource "google_compute_backend_bucket" "cdn_backend" {
name = "assets-backend-bucket"
bucket_name = google_storage_bucket.assets.name
enable_cdn = true
}
# 4. The URL Map (routes requests to the backend bucket)
resource "google_compute_url_map" "cdn_map" {
name = "cdn-url-map"
default_service = google_compute_backend_bucket.cdn_backend.id
}
# 5. Target Proxy & Global Forwarding Rule (The frontend IP)
resource "google_compute_target_http_proxy" "http_proxy" {
name = "cdn-http-proxy"
url_map = google_compute_url_map.cdn_map.id
}
resource "google_compute_global_forwarding_rule" "http_rule" {
name = "cdn-frontend-ip"
target = google_compute_target_http_proxy.http_proxy.id
port_range = "80"
load_balancing_scheme = "EXTERNAL_MANAGED"
}outputs.tf
output "cdn_ip_address" {
value = google_compute_global_forwarding_rule.http_rule.ip_address
}Workflow Commands
terraform init
terraform apply -var="project_id=my-gcp-project-123"
# It takes ~5 minutes for GCP Load Balancers to propagate DNS and routes globally.
# Then curl the specific asset:
curl http://$(terraform output -raw cdn_ip_address)/index.html
# Look for x-goog-cache status headers
curl -I http://$(terraform output -raw cdn_ip_address)/index.html
terraform destroy -var="project_id=my-gcp-project-123"Apply complete! Resources: 7 added, 0 changed, 0 destroyed. Outputs: cdn_ip_address = "34.111.12.34"

