diff --git a/examples/external-lb-backend-bucket/gcp-logo.svg b/examples/external-lb-backend-bucket/gcp-logo.svg new file mode 100644 index 00000000..45c38c4e --- /dev/null +++ b/examples/external-lb-backend-bucket/gcp-logo.svg @@ -0,0 +1,142 @@ + \ No newline at end of file diff --git a/examples/external-lb-backend-bucket/main.tf b/examples/external-lb-backend-bucket/main.tf new file mode 100644 index 00000000..f394f7be --- /dev/null +++ b/examples/external-lb-backend-bucket/main.tf @@ -0,0 +1,54 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "lb-frontend" { + source = "terraform-google-modules/lb-http/google//modules/frontend" + version = "~> 12.0" + + project_id = var.project_id + name = "global-lb-fe-bucket" + url_map_input = module.lb-backend.backend_service_info +} + +module "lb-backend" { + source = "terraform-google-modules/lb-http/google//modules/backend" + version = "~> 12.0" + + project_id = var.project_id + name = "global-lb-be-bucket" + backend_bucket_name = module.gcs.name + enable_cdn = true +} + +module "gcs" { + source = "terraform-google-modules/cloud-storage/google//modules/simple_bucket" + version = "~> 10.0" + + project_id = var.project_id + location = "us-central1" + name = "gcs-bucket" + force_destroy = true + iam_members = [{ member = "allUsers", role = "roles/storage.objectViewer" }] +} + +// The image object in Cloud Storage. +// Note that the path in the bucket matches the paths in the url map path rule above. +resource "google_storage_bucket_object" "image" { + name = "assets/gcp-logo.svg" + content = file("./gcp-logo.svg") + content_type = "image/svg+xml" + bucket = module.gcs.name +} diff --git a/examples/external-lb-backend-bucket/outputs.tf b/examples/external-lb-backend-bucket/outputs.tf new file mode 100644 index 00000000..456878ac --- /dev/null +++ b/examples/external-lb-backend-bucket/outputs.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "load-balancer-ip" { + value = module.lb-frontend.external_ip +} diff --git a/examples/external-lb-backend-bucket/variables.tf b/examples/external-lb-backend-bucket/variables.tf new file mode 100644 index 00000000..419e3a19 --- /dev/null +++ b/examples/external-lb-backend-bucket/variables.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "project_id" { + type = string +} diff --git a/metadata.yaml b/metadata.yaml index ddf68ac9..078d7fb0 100644 --- a/metadata.yaml +++ b/metadata.yaml @@ -52,6 +52,8 @@ spec: location: examples/cross-project-mig-backend - name: dynamic-backend location: examples/dynamic-backend + - name: external-lb-backend-bucket + location: examples/external-lb-backend-bucket - name: gke-node-port location: examples/https-gke/gke-node-port - name: https-gke diff --git a/modules/backend/README.md b/modules/backend/README.md index c8b59193..bd6f5555 100644 --- a/modules/backend/README.md +++ b/modules/backend/README.md @@ -7,6 +7,7 @@ This module creates `google_compute_backend_service` resource and its dependenci | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | affinity\_cookie\_ttl\_sec | Lifetime of cookies in seconds if session\_affinity is GENERATED\_COOKIE. | `number` | `null` | no | +| backend\_bucket\_name | The name of GCS bucket which serves the traffic. | `string` | `""` | no | | cdn\_policy | Cloud CDN configuration for this BackendService. |
object({
cache_mode = optional(string)
signed_url_cache_max_age_sec = optional(string)
default_ttl = optional(number)
max_ttl = optional(number)
client_ttl = optional(number)
negative_caching = optional(bool)
serve_while_stale = optional(number)
bypass_cache_on_request_headers = optional(list(string))
negative_caching_policy = optional(object({
code = optional(number)
ttl = optional(number)
}))
cache_key_policy = optional(object({
include_host = optional(bool)
include_protocol = optional(bool)
include_query_string = optional(bool)
query_string_blacklist = optional(list(string))
query_string_whitelist = optional(list(string))
include_http_headers = optional(list(string))
include_named_cookies = optional(list(string))
}))
}) | `{}` | no |
| compression\_mode | Compress text responses using Brotli or gzip compression. | `string` | `"DISABLED"` | no |
| connection\_draining\_timeout\_sec | Time for which instance will be drained (not accept new connections, but still work to finish started). | `number` | `null` | no |
diff --git a/modules/backend/main.tf b/modules/backend/main.tf
index bbae368b..ede11825 100644
--- a/modules/backend/main.tf
+++ b/modules/backend/main.tf
@@ -14,8 +14,13 @@
* limitations under the License.
*/
+locals {
+ is_backend_bucket = var.backend_bucket_name != null && var.backend_bucket_name != ""
+}
+
resource "google_compute_backend_service" "default" {
provider = google-beta
+ count = !local.is_backend_bucket ? 1 : 0
project = var.project_id
name = var.name
@@ -310,3 +315,52 @@ resource "google_compute_firewall" "allow_proxy" {
protocol = "tcp"
}
}
+
+resource "google_compute_backend_bucket" "default" {
+ provider = google-beta
+ count = local.is_backend_bucket ? 1 : 0
+
+ project = var.project_id
+ name = var.name
+ bucket_name = var.backend_bucket_name
+ enable_cdn = var.enable_cdn
+
+ description = var.description
+
+ # CDN policy configuration, if CDN is enabled
+ dynamic "cdn_policy" {
+ for_each = var.enable_cdn ? [1] : []
+ content {
+ cache_mode = var.cdn_policy.cache_mode
+ signed_url_cache_max_age_sec = var.cdn_policy.signed_url_cache_max_age_sec
+ default_ttl = var.cdn_policy.default_ttl
+ max_ttl = var.cdn_policy.max_ttl
+ client_ttl = var.cdn_policy.client_ttl
+ negative_caching = var.cdn_policy.negative_caching
+ serve_while_stale = var.cdn_policy.serve_while_stale
+
+ dynamic "negative_caching_policy" {
+ for_each = var.cdn_policy.negative_caching_policy != null ? [1] : []
+ content {
+ code = var.cdn_policy.negative_caching_policy.code
+ ttl = var.cdn_policy.negative_caching_policy.ttl
+ }
+ }
+
+ dynamic "cache_key_policy" {
+ for_each = var.cdn_policy.cache_key_policy != null ? [1] : []
+ content {
+ query_string_whitelist = var.cdn_policy.cache_key_policy.query_string_whitelist
+ include_http_headers = var.cdn_policy.cache_key_policy.include_http_headers
+ }
+ }
+
+ dynamic "bypass_cache_on_request_headers" {
+ for_each = var.cdn_policy.bypass_cache_on_request_headers != null && try(length(var.cdn_policy.bypass_cache_on_request_headers), 0) > 0 ? toset(var.cdn_policy.bypass_cache_on_request_headers) : []
+ content {
+ header_name = bypass_cache_on_request_headers.value
+ }
+ }
+ }
+ }
+}
diff --git a/modules/backend/metadata.yaml b/modules/backend/metadata.yaml
index ca4082f6..91525b18 100644
--- a/modules/backend/metadata.yaml
+++ b/modules/backend/metadata.yaml
@@ -44,6 +44,8 @@ spec:
location: examples/cross-project-mig-backend
- name: dynamic-backend
location: examples/dynamic-backend
+ - name: external-lb-backend-bucket
+ location: examples/external-lb-backend-bucket
- name: gke-node-port
location: examples/https-gke/gke-node-port
- name: https-gke
@@ -176,6 +178,16 @@ spec:
version: ">= 0.13"
spec:
outputExpr: "{\"region\": location, \"service_name\": service_name, \"type\": \"cloud-run\", \"service_version\": \"\"}"
+ - name: backend_bucket_name
+ description: The name of GCS bucket which serves the traffic.
+ varType: string
+ defaultValue: ""
+ connections:
+ - source:
+ source: github.com/terraform-google-modules/terraform-google-cloud-storage//modules/simple_bucket
+ version: ">= 8.0"
+ spec:
+ outputExpr: name
- name: iap_config
description: Settings for enabling Cloud Identity Aware Proxy Structure.
varType: |-
diff --git a/modules/backend/outputs.tf b/modules/backend/outputs.tf
index c8ed10f1..bb09320e 100644
--- a/modules/backend/outputs.tf
+++ b/modules/backend/outputs.tf
@@ -16,11 +16,17 @@
output "backend_service_info" {
description = "Host, path and backend service mapping"
- value = [
+ value = concat(!local.is_backend_bucket ? [
for mapping in var.host_path_mappings : {
host = mapping.host
path = mapping.path
- backend_service = google_compute_backend_service.default.self_link
+ backend_service = google_compute_backend_service.default[0].self_link
}
- ]
+ ] : [], local.is_backend_bucket ? [for mapping in var.host_path_mappings : {
+ host = mapping.host
+ path = mapping.path
+ backend_service = google_compute_backend_bucket.default[0].self_link
+ }
+ ] : []
+ )
}
diff --git a/modules/backend/variables.tf b/modules/backend/variables.tf
index a79cfae3..9c9c0b3c 100644
--- a/modules/backend/variables.tf
+++ b/modules/backend/variables.tf
@@ -147,6 +147,12 @@ variable "serverless_neg_backends" {
}
}
+variable "backend_bucket_name" {
+ description = "The name of GCS bucket which serves the traffic."
+ type = string
+ default = ""
+}
+
variable "iap_config" {
description = "Settings for enabling Cloud Identity Aware Proxy Structure."
type = object({
diff --git a/modules/dynamic_backends/metadata.yaml b/modules/dynamic_backends/metadata.yaml
index 58de844d..9ba5066a 100644
--- a/modules/dynamic_backends/metadata.yaml
+++ b/modules/dynamic_backends/metadata.yaml
@@ -44,6 +44,8 @@ spec:
location: examples/cross-project-mig-backend
- name: dynamic-backend
location: examples/dynamic-backend
+ - name: external-lb-backend-bucket
+ location: examples/external-lb-backend-bucket
- name: gke-node-port
location: examples/https-gke/gke-node-port
- name: https-gke
diff --git a/modules/frontend/metadata.yaml b/modules/frontend/metadata.yaml
index 1fa724d1..4526e454 100644
--- a/modules/frontend/metadata.yaml
+++ b/modules/frontend/metadata.yaml
@@ -44,6 +44,8 @@ spec:
location: examples/cross-project-mig-backend
- name: dynamic-backend
location: examples/dynamic-backend
+ - name: external-lb-backend-bucket
+ location: examples/external-lb-backend-bucket
- name: gke-node-port
location: examples/https-gke/gke-node-port
- name: https-gke
diff --git a/modules/serverless_negs/metadata.yaml b/modules/serverless_negs/metadata.yaml
index 68d6007f..a37e19fa 100644
--- a/modules/serverless_negs/metadata.yaml
+++ b/modules/serverless_negs/metadata.yaml
@@ -44,6 +44,8 @@ spec:
location: examples/cross-project-mig-backend
- name: dynamic-backend
location: examples/dynamic-backend
+ - name: external-lb-backend-bucket
+ location: examples/external-lb-backend-bucket
- name: gke-node-port
location: examples/https-gke/gke-node-port
- name: https-gke
diff --git a/test/integration/external-lb-backend-bucket/external_lb_backend_bucket_test.go b/test/integration/external-lb-backend-bucket/external_lb_backend_bucket_test.go
new file mode 100644
index 00000000..0f0c56df
--- /dev/null
+++ b/test/integration/external-lb-backend-bucket/external_lb_backend_bucket_test.go
@@ -0,0 +1,38 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package external_lb_backend_bucket
+
+import (
+ "testing"
+
+ "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/tft"
+ "github.com/stretchr/testify/assert"
+
+ test "github.com/terraform-google-modules/terraform-google-lb-http/test/integration"
+)
+
+func TestExternalLbBackendBucket(t *testing.T) {
+ bpt := tft.NewTFBlueprintTest(t)
+
+ bpt.DefineVerify(func(assert *assert.Assertions) {
+ bpt.DefaultVerify(assert)
+
+ loadBalancerIp := bpt.GetStringOutput("load-balancer-ip")
+
+ test.AssertResponseStatus(t, assert, "http://"+loadBalancerIp+"/assets/gcp-logo.svg", 200)
+ })
+
+ bpt.Test()
+}