Here’s an example of using Terraform to define resources to host static content in a Google Cloud Storage bucket, fronted by a Cloud Load Balancer with a custom URL and SSL certificate. This example uses some other advanced features, such as Google Secrets and a map variable to define the SSL certificates. It’s pulled from a larger project, so this block of code isn’t guaranteed to run as-is. At a minimum, you’ll need to define the variables and set values.
# Use Google-provided module to define a bucket
module "cloud_storage_buckets" {
source = "terraform-google-modules/cloud-storage/google"
version = "2.1.0"
project_id = var.project
prefix = var.bucket_prefix
location = var.bucket_location
names = var.bucket_names
set_viewer_roles = true
viewers = [
"allUsers"
]
website = {
main_page_suffix = "index.html"
not_found_page = "index.html"
}
}
# External IP Address for load balancer
resource "google_compute_global_address" "static" {
name = "static"
description = "Static external IP address for hosting"
}
# SSL Certificates
data "google_secret_manager_secret_version" "cert" {
for_each = var.ssl_map
secret = each.value.tls_cert
}
data "google_secret_manager_secret_version" "key" {
for_each = var.ssl_map
secret = each.value.tls_key
}
resource "google_compute_ssl_certificate" "default" {
for_each = var.ssl_map
name_prefix = each.key
description = each.value.description
private_key = data.google_secret_manager_secret_version.key[each.key].secret_data
certificate = data.google_secret_manager_secret_version.cert[each.key].secret_data
lifecycle {
create_before_destroy = true
}
}
# SSL Policies
resource "google_compute_ssl_policy" "tls12_modern" {
name = "${var.environment}-static-ssl-policy"
profile = "MODERN"
min_tls_version = "TLS_1_2"
}
# HTTPS load balancer for backend bucket
resource "google_compute_backend_bucket" "static" {
name = "${var.environment}-us-static-backend"
description = "Backend storage bucket for statick static files"
bucket_name = "${var.environment}-us-static"
# bucket_name = module.cloud_storage_buckets.google_storage_bucket.buckets["static"].name
enable_cdn = false
}
resource "google_compute_url_map" "static" {
name = "${var.environment}-static-load-balancer"
default_service = google_compute_backend_bucket.static.id
}
resource "google_compute_target_https_proxy" "static" {
for_each = var.ssl_map
name = "${var.environment}-static-${each.key}"
url_map = google_compute_url_map.static.id
ssl_certificates = [google_compute_ssl_certificate.default[each.key].id]
ssl_policy = google_compute_ssl_policy.tls12_modern.id
}
resource "google_compute_global_forwarding_rule" "static" {
for_each = var.ssl_map
name = "${var.environment}-static-${each.key}"
target = google_compute_target_https_proxy.static[each.key].id
port_range = "443"
ip_address = google_compute_global_address.static.id
}
# Partial HTTP load balancer redirects to HTTPS
resource "google_compute_url_map" "static_http" {
name = "${var.environment}-static-http-redirect"
default_url_redirect {
https_redirect = true
strip_query = false
}
}
resource "google_compute_target_http_proxy" "static" {
name = "${var.environment}-static-http-proxy"
url_map = google_compute_url_map.static_http.id
}
resource "google_compute_global_forwarding_rule" "static_http" {
name = "${var.environment}-static-forwarding-rule-http"
target = google_compute_target_http_proxy.static.id
port_range = "80"
ip_address = google_compute_global_address.static.id
}
for_each and the SSL map
You may be confused by the for_each construct if you’re new to Terraform. This parameter refers to a map variable, which is defined like this:
ssl_map = {
my_cert_2022 = {
tls_cert = "my-tls-cert",
tls_key = "my-tls-key",
description = "mysite.com wildcard issued 2022-08 expires 2023-08"
}
}
The for_each construct causes Terraform to iterate over the keys in the map and define multiple copies of the resource (there is only one key in this case). Other parameters in the resource can use either the key itself or the values. I have mixed feelings about for_each. It’s a great shortcut, but also makes the code harder to read.
Next Steps
This code block could easily be made into a module, and that’s what you should do if you plan to use it more than once. The module should accept an array of SSL certs. The module would also hide the confusing aspects of the for_each.
Ideally, I’d like Google’s load balancer module to support a static backend configuration (probably as a submodule).