Automate Cloud CDN setup with Terraform

Use the following examples to deploy sample backends with Cloud CDN enabled.

If you are new to using Terraform for Google Cloud, see Get started with Terraform.

Using a Cloud Storage bucket

The following sample configures Cloud CDN with a public Cloud Storage bucket. The sample also includes load balancing and caching configurations. The configurations can be modified, as required.

For details about each section of the sample, see Set up a backend bucket.

# Cloud Storage bucket
resource "random_id" "bucket_prefix" {
  byte_length = 8
}

resource "google_storage_bucket" "default" {
  name                        = "${random_id.bucket_prefix.hex}-my-bucket"
  location                    = "us-east1"
  uniform_bucket_level_access = true
  storage_class               = "STANDARD"
  // delete bucket and contents on destroy.
  force_destroy = true
  // Assign specialty files
  website {
    main_page_suffix = "index.html"
    not_found_page   = "404.html"
  }
}


# make bucket public
resource "google_storage_bucket_iam_member" "default" {
  bucket = google_storage_bucket.default.name
  role   = "roles/storage.objectViewer"
  member = "allUsers"
}

resource "google_storage_bucket_object" "index_page" {
  name    = "index-page"
  bucket  = google_storage_bucket.default.name
  content = <<-EOT
    
    

Congratulations on setting up Google Cloud CDN with Storage backend!

EOT } resource "google_storage_bucket_object" "error_page" { name = "404-page" bucket = google_storage_bucket.default.name content = <<-EOT

404 Error: Object you are looking for is no longer available!

EOT } # image object for testing, try to access http:///test.jpg resource "google_storage_bucket_object" "test_image" { name = "test-object" # Uncomment and add valid path to an object. # source = "/path/to/an/object" # content_type = "image/jpeg" # Delete after uncommenting above source and content_type attributes content = "Data as string to be uploaded" content_type = "text/plain" bucket = google_storage_bucket.default.name } # reserve IP address resource "google_compute_global_address" "default" { name = "example-ip" } # forwarding rule resource "google_compute_global_forwarding_rule" "default" { name = "http-lb-forwarding-rule" ip_protocol = "TCP" load_balancing_scheme = "EXTERNAL" port_range = "80" target = google_compute_target_http_proxy.default.id ip_address = google_compute_global_address.default.id } # http proxy resource "google_compute_target_http_proxy" "default" { name = "http-lb-proxy" url_map = google_compute_url_map.default.id } # url map resource "google_compute_url_map" "default" { name = "http-lb" default_service = google_compute_backend_bucket.default.id } # backend bucket with CDN policy with default ttl settings resource "google_compute_backend_bucket" "default" { name = "cat-backend-bucket" description = "Contains beautiful images" bucket_name = google_storage_bucket.default.name enable_cdn = true cdn_policy { cache_mode = "CACHE_ALL_STATIC" client_ttl = 3600 default_ttl = 3600 max_ttl = 86400 negative_caching = true serve_while_stale = 86400 } }

Using MIG and custom headers

You can use Terraform resources to bring up an external HTTP load balancer with a Cloud CDN backend service and custom request and response headers.

For information about the load balancer setup, see the primary setup guide.

# VPC
resource "google_compute_network" "default" {
  name                    = "l7-xlb-network"
  provider                = google-beta
  auto_create_subnetworks = false
}

# backend subnet
resource "google_compute_subnetwork" "default" {
  name          = "l7-xlb-subnet"
  provider      = google-beta
  ip_cidr_range = "10.0.1.0/24"
  region        = "us-central1"
  network       = google_compute_network.default.id
}

# reserved IP address
resource "google_compute_global_address" "default" {
  provider = google-beta
  name     = "l7-xlb-static-ip"
}

# forwarding rule
resource "google_compute_global_forwarding_rule" "default" {
  name                  = "l7-xlb-forwarding-rule"
  provider              = google-beta
  ip_protocol           = "TCP"
  load_balancing_scheme = "EXTERNAL"
  port_range            = "80"
  target                = google_compute_target_http_proxy.default.id
  ip_address            = google_compute_global_address.default.id
}

# http proxy
resource "google_compute_target_http_proxy" "default" {
  name     = "l7-xlb-target-http-proxy"
  provider = google-beta
  url_map  = google_compute_url_map.default.id
}

# url map
resource "google_compute_url_map" "default" {
  name            = "l7-xlb-url-map"
  provider        = google-beta
  default_service = google_compute_backend_service.default.id
}

# backend service with custom request and response headers
resource "google_compute_backend_service" "default" {
  name                    = "l7-xlb-backend-service"
  provider                = google-beta
  protocol                = "HTTP"
  port_name               = "my-port"
  load_balancing_scheme   = "EXTERNAL"
  timeout_sec             = 10
  enable_cdn              = true
  custom_request_headers  = ["X-Client-Geo-Location: {client_region_subdivision}, {client_city}"]
  custom_response_headers = ["X-Cache-Hit: {cdn_cache_status}"]
  health_checks           = [google_compute_health_check.default.id]
  backend {
    group           = google_compute_instance_group_manager.default.instance_group
    balancing_mode  = "UTILIZATION"
    capacity_scaler = 1.0
  }
}

# instance template
resource "google_compute_instance_template" "default" {
  name         = "l7-xlb-mig-template"
  provider     = google-beta
  machine_type = "e2-small"
  tags         = ["allow-health-check"]

  network_interface {
    network    = google_compute_network.default.id
    subnetwork = google_compute_subnetwork.default.id
    access_config {
      # add external ip to fetch packages
    }
  }
  disk {
    source_image = "debian-cloud/debian-12"
    auto_delete  = true
    boot         = true
  }

  # install nginx and serve a simple web page
  metadata = {
    startup-script = <<-EOF1
      #! /bin/bash
      set -euo pipefail

      export DEBIAN_FRONTEND=noninteractive
      apt-get update
      apt-get install -y nginx-light jq

      NAME=$(curl -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/hostname")
      IP=$(curl -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/ip")
      METADATA=$(curl -f -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/attributes/?recursive=True" | jq 'del(.["startup-script"])')

      cat < /var/www/html/index.html
      
      Name: $NAME
      IP: $IP
      Metadata: $METADATA
      
EOF EOF1 } lifecycle { create_before_destroy = true } } # health check resource "google_compute_health_check" "default" { name = "l7-xlb-hc" provider = google-beta http_health_check { port_specification = "USE_SERVING_PORT" } } # MIG resource "google_compute_instance_group_manager" "default" { name = "l7-xlb-mig1" provider = google-beta zone = "us-central1-c" named_port { name = "http" port = 8080 } version { instance_template = google_compute_instance_template.default.id name = "primary" } base_instance_name = "vm" target_size = 2 } # allow access from health check ranges resource "google_compute_firewall" "default" { name = "l7-xlb-fw-allow-hc" provider = google-beta direction = "INGRESS" network = google_compute_network.default.id source_ranges = ["130.211.0.0/22", "35.191.0.0/16"] allow { protocol = "tcp" } target_tags = ["allow-health-check"] }