diff --git a/build/int.cloudbuild.yaml b/build/int.cloudbuild.yaml index 37f90561..d3b0f7b8 100644 --- a/build/int.cloudbuild.yaml +++ b/build/int.cloudbuild.yaml @@ -127,6 +127,22 @@ steps: - verify lb-http-separate-frontend-and-backend name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' args: ['/bin/bash', '-c', 'cft test run TestSeparateFrontendAndBackend --stage teardown --verbose'] + # Internal cross regional http load balancer with cloud-run +- id: apply internal-lb-http + waitFor: + - create + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run TestInternalLbCloudRun --stage apply --verbose'] +- id: verify internal-lb-http + waitFor: + - apply internal-lb-http + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'sleep 360 && cft test run TestInternalLbCloudRun --stage verify --verbose'] +- id: teardown internal-lb-http + waitFor: + - verify internal-lb-http + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run TestInternalLbCloudRun --stage teardown --verbose'] tags: - 'ci' - 'integration' diff --git a/examples/internal-lb-cloud-run/main.tf b/examples/internal-lb-cloud-run/main.tf new file mode 100644 index 00000000..4a84ac81 --- /dev/null +++ b/examples/internal-lb-cloud-run/main.tf @@ -0,0 +1,175 @@ +/** + * 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. + */ + +provider "google" { + project = var.project_id +} + +provider "google-beta" { + project = var.project_id +} + +module "internal-lb-network" { + source = "terraform-google-modules/network/google//modules/vpc" + version = "~> 10.0.0" + project_id = var.project_id + network_name = "int-lb-network" + auto_create_subnetworks = false +} + +module "internal-lb-subnet" { + source = "terraform-google-modules/network/google//modules/subnets" + version = "~> 10.0.0" + + subnets = [ + { + subnet_name = "int-lb-subnet-a" + subnet_ip = "10.1.2.0/24" + subnet_region = "us-east1" + }, + { + subnet_name = "int-lb-proxy-only-subnet-a" + subnet_ip = "10.129.0.0/23" + subnet_region = "us-east1" + purpose = "GLOBAL_MANAGED_PROXY" + role = "ACTIVE" + }, + { + subnet_name = "int-lb-subnet-b" + subnet_ip = "10.1.3.0/24" + subnet_region = "us-south1" + }, + { + subnet_name = "int-lb-proxy-only-subnet-b", + subnet_ip = "10.130.0.0/23" + subnet_region = "us-south1" + purpose = "GLOBAL_MANAGED_PROXY" + role = "ACTIVE" + } + ] + + network_name = module.internal-lb-network.network_name + project_id = var.project_id + depends_on = [module.internal-lb-network] +} + +module "backend-service-region-a" { + source = "GoogleCloudPlatform/cloud-run/google//modules/v2" + version = "~> 0.16.3" + project_id = var.project_id + location = "us-central1" + service_name = "bs-a" + containers = [{ "container_name" = "", "container_image" = "gcr.io/cloudrun/hello" }] + members = ["allUsers"] + ingress = "INGRESS_TRAFFIC_INTERNAL_ONLY" + cloud_run_deletion_protection = false + enable_prometheus_sidecar = false +} + +module "backend-service-region-b" { + source = "GoogleCloudPlatform/cloud-run/google//modules/v2" + version = "~> 0.16.3" + project_id = var.project_id + location = "us-west1" + service_name = "bs-b" + containers = [{ "container_name" = "", "container_image" = "gcr.io/cloudrun/hello" }] + members = ["allUsers"] + ingress = "INGRESS_TRAFFIC_INTERNAL_ONLY" + cloud_run_deletion_protection = false + enable_prometheus_sidecar = false +} + +module "internal-lb-http-backend" { + source = "terraform-google-modules/lb-http/google//modules/backend" + version = "~> 12.0" + + project_id = var.project_id + name = "int-lb-http-backend" + enable_cdn = false + load_balancing_scheme = "INTERNAL_MANAGED" + locality_lb_policy = "RANDOM" + serverless_neg_backends = [ + { region : "us-central1", type : "cloud-run", service_name : module.backend-service-region-a.service_name }, + { region : "us-west1", type : "cloud-run", service_name : module.backend-service-region-b.service_name } + ] +} + +module "internal-lb-http-frontend" { + source = "terraform-google-modules/lb-http/google//modules/frontend" + version = "~> 12.0" + + project_id = var.project_id + name = "int-lb-http-frontend" + url_map_input = module.internal-lb-http-backend.backend_service_info + network = module.internal-lb-network.network_name + load_balancing_scheme = "INTERNAL_MANAGED" + internal_forwarding_rules_config = [ + { + "region" : "us-east1", + "subnetwork" : module.internal-lb-subnet.subnets["us-east1/int-lb-subnet-a"].id + }, + { + "region" : "us-south1", + "subnetwork" : module.internal-lb-subnet.subnets["us-south1/int-lb-subnet-b"].id + } + ] +} + +resource "google_vpc_access_connector" "internal_lb_vpc_connector" { + provider = google-beta + project = var.project_id + name = "int-lb-vpc-connector" + region = "us-east1" + ip_cidr_range = "10.8.0.0/28" + network = module.internal-lb-network.network_name + max_throughput = 500 + min_throughput = 300 +} + +module "frontend-service-a" { + source = "GoogleCloudPlatform/cloud-run/google//modules/v2" + version = "~> 0.16.3" + project_id = var.project_id + location = "us-east1" + service_name = "fs-a" + containers = [{ "env_vars" : { "TARGET_IP" : module.internal-lb-http-frontend.ip_address_internal_managed_http[0] }, "ports" = { "container_port" = 80, "name" = "http1" }, "container_name" = "", "container_image" = "gcr.io/design-center-container-repo/redirect-traffic:latest-2002" }] + members = ["allUsers"] + vpc_access = { + connector = google_vpc_access_connector.internal_lb_vpc_connector.id + egress = "ALL_TRAFFIC" + } + ingress = "INGRESS_TRAFFIC_ALL" + cloud_run_deletion_protection = false + enable_prometheus_sidecar = false + depends_on = [google_vpc_access_connector.internal_lb_vpc_connector] +} + +module "frontend-service-b" { + source = "GoogleCloudPlatform/cloud-run/google//modules/v2" + version = "~> 0.16.3" + project_id = var.project_id + location = "us-east1" + service_name = "fs-b" + containers = [{ "env_vars" : { "TARGET_IP" : module.internal-lb-http-frontend.ip_address_internal_managed_http[1] }, "ports" = { "container_port" = 80, "name" = "http1" }, "container_name" = "", "container_image" = "gcr.io/design-center-container-repo/redirect-traffic:latest-2002" }] + members = ["allUsers"] + vpc_access = { + connector = google_vpc_access_connector.internal_lb_vpc_connector.id + egress = "ALL_TRAFFIC" + } + ingress = "INGRESS_TRAFFIC_ALL" + cloud_run_deletion_protection = false + enable_prometheus_sidecar = false +} diff --git a/examples/internal-lb-cloud-run/outputs.tf b/examples/internal-lb-cloud-run/outputs.tf new file mode 100644 index 00000000..22772df2 --- /dev/null +++ b/examples/internal-lb-cloud-run/outputs.tf @@ -0,0 +1,20 @@ +/** + * 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 "external_cloudrun_uris" { + description = "List of URIs for the frontend Cloud Run services" + value = [module.frontend-service-a.service_uri, module.frontend-service-b.service_uri] +} diff --git a/examples/internal-lb-cloud-run/readme.md b/examples/internal-lb-cloud-run/readme.md new file mode 100644 index 00000000..444c6e16 --- /dev/null +++ b/examples/internal-lb-cloud-run/readme.md @@ -0,0 +1,20 @@ +# HTTP Internal Regional Load Balancer Example + +This example creates a simple application with below components. + +* *Frontend Service*: Two cloud-run services to send request to internal cross-regional load balancers. The cloud-run service uses VPC access connector to send the request to the internal load balancer. +* *Internal Load Balancer*: An internal cross-regional load balancer to distribute traffic to internal cloud run services. +* *Backend Service*: Two cloud-run services to run the actual application code. These can be accessed within internal traffic. The internal Application Load Balancer is considered internal traffic. + + +The `google_compute_backend_service` and its dependencies are created as part of `backend` module. +The forwarding rules and its dependecies are created as part of `frontend` module. + + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| project\_id | n/a | `string` | n/a | yes | + + diff --git a/examples/internal-lb-cloud-run/variables.tf b/examples/internal-lb-cloud-run/variables.tf new file mode 100644 index 00000000..419e3a19 --- /dev/null +++ b/examples/internal-lb-cloud-run/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 99297dcd..72db8795 100644 --- a/metadata.yaml +++ b/metadata.yaml @@ -58,6 +58,8 @@ spec: location: examples/https-gke - name: https-redirect location: examples/https-redirect + - name: internal-lb-cloud-run + location: examples/internal-lb-cloud-run - name: lb-http-separate-frontend-and-backend location: examples/lb-http-separate-frontend-and-backend - name: mig-nat-http-lb @@ -338,6 +340,8 @@ spec: - roles/run.admin - roles/iam.serviceAccountUser - roles/certificatemanager.owner + - roles/vpcaccess.admin + - roles/iam.serviceAccountAdmin services: - cloudresourcemanager.googleapis.com - storage-api.googleapis.com @@ -346,6 +350,7 @@ spec: - run.googleapis.com - iam.googleapis.com - certificatemanager.googleapis.com + - vpcaccess.googleapis.com providerVersions: - source: hashicorp/google version: ">= 6.0, < 7" diff --git a/modules/backend/README.md b/modules/backend/README.md index 0bc78add..5c5616c7 100644 --- a/modules/backend/README.md +++ b/modules/backend/README.md @@ -21,7 +21,7 @@ This module creates `google_compute_backend_service` resource and its dependenci | health\_check | Input for creating HttpHealthCheck or HttpsHealthCheck resource for health checking this BackendService. A health check must be specified unless the backend service uses an internet or serverless NEG as a backend. |
object({
host = optional(string, null)
request_path = optional(string, null)
request = optional(string, null)
response = optional(string, null)
port = optional(number, null)
port_name = optional(string, null)
proxy_header = optional(string, null)
port_specification = optional(string, null)
protocol = optional(string, null)
check_interval_sec = optional(number, 5)
timeout_sec = optional(number, 5)
healthy_threshold = optional(number, 2)
unhealthy_threshold = optional(number, 2)
logging = optional(bool, false)
})
| `null` | no | | host\_path\_mappings | The list of host/path for which traffic could be sent to the backend service |
list(object({
host = string
path = string
}))
|
[
{
"host": "*",
"path": "/*"
}
]
| no | | iap\_config | Settings for enabling Cloud Identity Aware Proxy Structure. |
object({
enable = bool
oauth2_client_id = optional(string)
oauth2_client_secret = optional(string)
})
|
{
"enable": false
}
| no | -| load\_balancing\_scheme | Load balancing scheme type (EXTERNAL for classic external load balancer, EXTERNAL\_MANAGED for Envoy-based load balancer, and INTERNAL\_SELF\_MANAGED for traffic director). | `string` | `"EXTERNAL_MANAGED"` | no | +| load\_balancing\_scheme | Load balancing scheme type (EXTERNAL for classic external load balancer, EXTERNAL\_MANAGED for Envoy-based load balancer, INTERNAL\_MANAGED for internal load balancer and INTERNAL\_SELF\_MANAGED for traffic director) | `string` | `"EXTERNAL_MANAGED"` | no | | locality\_lb\_policy | The load balancing algorithm used within the scope of the locality. | `string` | `null` | no | | log\_config | This field denotes the logging options for the load balancer traffic served by this backend service. If logging is enabled, logs will be exported to Stackdriver. |
object({
enable = bool
sample_rate = number
})
|
{
"enable": true,
"sample_rate": 1
}
| no | | name | Name for the backend service. | `string` | n/a | yes | diff --git a/modules/backend/main.tf b/modules/backend/main.tf index 0212a7ee..09864b33 100644 --- a/modules/backend/main.tf +++ b/modules/backend/main.tf @@ -28,7 +28,7 @@ resource "google_compute_backend_service" "default" { description = var.description connection_draining_timeout_sec = var.connection_draining_timeout_sec enable_cdn = var.enable_cdn - compression_mode = var.compression_mode + compression_mode = var.load_balancing_scheme == "INTERNAL_SELF_MANAGED" || var.load_balancing_scheme == "INTERNAL_MANAGED" ? null : var.compression_mode custom_request_headers = var.custom_request_headers custom_response_headers = var.custom_response_headers session_affinity = var.session_affinity diff --git a/modules/backend/metadata.display.yaml b/modules/backend/metadata.display.yaml index 6a406b2c..77480ec8 100644 --- a/modules/backend/metadata.display.yaml +++ b/modules/backend/metadata.display.yaml @@ -77,6 +77,11 @@ spec: load_balancing_scheme: name: load_balancing_scheme title: Load Balancing Scheme + enumValueLabels: + - label: EXTERNAL_MANAGED + value: EXTERNAL_MANAGED + - label: INTERNAL_MANAGED + value: INTERNAL_MANAGED locality_lb_policy: name: locality_lb_policy title: Locality Lb Policy diff --git a/modules/backend/metadata.yaml b/modules/backend/metadata.yaml index 814a3bf0..77dd99a9 100644 --- a/modules/backend/metadata.yaml +++ b/modules/backend/metadata.yaml @@ -50,6 +50,8 @@ spec: location: examples/https-gke - name: https-redirect location: examples/https-redirect + - name: internal-lb-cloud-run + location: examples/internal-lb-cloud-run - name: lb-http-separate-frontend-and-backend location: examples/lb-http-separate-frontend-and-backend - name: mig-nat-http-lb @@ -77,7 +79,7 @@ spec: varType: string required: true - name: load_balancing_scheme - description: Load balancing scheme type (EXTERNAL for classic external load balancer, EXTERNAL_MANAGED for Envoy-based load balancer, and INTERNAL_SELF_MANAGED for traffic director). + description: Load balancing scheme type (EXTERNAL for classic external load balancer, EXTERNAL_MANAGED for Envoy-based load balancer, INTERNAL_MANAGED for internal load balancer and INTERNAL_SELF_MANAGED for traffic director) varType: string defaultValue: EXTERNAL_MANAGED - name: protocol @@ -305,6 +307,8 @@ spec: - roles/run.admin - roles/iam.serviceAccountUser - roles/certificatemanager.owner + - roles/vpcaccess.admin + - roles/iam.serviceAccountAdmin services: - cloudresourcemanager.googleapis.com - storage-api.googleapis.com @@ -313,6 +317,7 @@ spec: - run.googleapis.com - iam.googleapis.com - certificatemanager.googleapis.com + - vpcaccess.googleapis.com providerVersions: - source: hashicorp/google version: ">= 6.0, < 7" diff --git a/modules/backend/variables.tf b/modules/backend/variables.tf index 28166182..882aff6d 100644 --- a/modules/backend/variables.tf +++ b/modules/backend/variables.tf @@ -26,7 +26,7 @@ variable "project_id" { } variable "load_balancing_scheme" { - description = "Load balancing scheme type (EXTERNAL for classic external load balancer, EXTERNAL_MANAGED for Envoy-based load balancer, and INTERNAL_SELF_MANAGED for traffic director)." + description = "Load balancing scheme type (EXTERNAL for classic external load balancer, EXTERNAL_MANAGED for Envoy-based load balancer, INTERNAL_MANAGED for internal load balancer and INTERNAL_SELF_MANAGED for traffic director)" type = string default = "EXTERNAL_MANAGED" } diff --git a/modules/dynamic_backends/metadata.yaml b/modules/dynamic_backends/metadata.yaml index cad75afd..af4894f7 100644 --- a/modules/dynamic_backends/metadata.yaml +++ b/modules/dynamic_backends/metadata.yaml @@ -50,6 +50,8 @@ spec: location: examples/https-gke - name: https-redirect location: examples/https-redirect + - name: internal-lb-cloud-run + location: examples/internal-lb-cloud-run - name: lb-http-separate-frontend-and-backend location: examples/lb-http-separate-frontend-and-backend - name: mig-nat-http-lb @@ -330,6 +332,8 @@ spec: - roles/run.admin - roles/iam.serviceAccountUser - roles/certificatemanager.owner + - roles/vpcaccess.admin + - roles/iam.serviceAccountAdmin services: - cloudresourcemanager.googleapis.com - storage-api.googleapis.com @@ -338,6 +342,7 @@ spec: - run.googleapis.com - iam.googleapis.com - certificatemanager.googleapis.com + - vpcaccess.googleapis.com providerVersions: - source: hashicorp/google version: ">= 6.0, < 7" diff --git a/modules/frontend/README.md b/modules/frontend/README.md index 0e43a1dd..e2552a99 100644 --- a/modules/frontend/README.md +++ b/modules/frontend/README.md @@ -19,12 +19,13 @@ This module creates `HTTP(S) forwarding rule` and its dependencies. This modules | http\_port | The port for the HTTP load balancer | `number` | `80` | no | | https\_port | The port for the HTTPS load balancer | `number` | `443` | no | | https\_redirect | Set to `true` to enable https redirect on the lb. | `bool` | `false` | no | +| internal\_forwarding\_rules\_config | List of internal managed forwarding rules config. One of 'address' or 'subnetwork' is required for each. It is only applicable for internal load balancer |
list(object({
region = string
address = optional(string)
subnetwork = optional(string)
}))
| `[]` | no | | ipv6\_address | An existing IPv6 address to use (the actual IP address value) | `string` | `null` | no | | labels | The labels to attach to resources created by this module | `map(string)` | `{}` | no | -| load\_balancing\_scheme | Load balancing scheme type (EXTERNAL for classic external load balancer, EXTERNAL\_MANAGED for Envoy-based load balancer, and INTERNAL\_SELF\_MANAGED for traffic director) | `string` | `"EXTERNAL_MANAGED"` | no | +| load\_balancing\_scheme | Load balancing scheme type (EXTERNAL for classic external load balancer, EXTERNAL\_MANAGED for Envoy-based load balancer, INTERNAL\_MANAGED for internal load balancer and INTERNAL\_SELF\_MANAGED for traffic director) | `string` | `"EXTERNAL_MANAGED"` | no | | managed\_ssl\_certificate\_domains | Create Google-managed SSL certificates for specified domains. Requires `ssl` to be set to `true` | `list(string)` | `[]` | no | | name | Name for the forwarding rule and prefix for supporting resources | `string` | n/a | yes | -| network | Network for INTERNAL\_SELF\_MANAGED load balancing scheme | `string` | `"default"` | no | +| network | Network for internal load balancer | `string` | `"default"` | no | | private\_key | Content of the private SSL key. Requires `ssl` to be set to `true` and `create_ssl_certificate` set to `true` | `string` | `null` | no | | project\_id | The project to deploy to, if not set the default provider project is used. | `string` | n/a | yes | | quic | Specifies the QUIC override policy for this resource. Set true to enable HTTP/3 and Google QUIC support, false to disable both. Defaults to null which enables support for HTTP/3 only. | `bool` | `null` | no | @@ -44,6 +45,8 @@ This module creates `HTTP(S) forwarding rule` and its dependencies. This modules | external\_ipv6\_address | The external IPv6 assigned to the global fowarding rule. | | http\_proxy | The HTTP proxy used by this module. | | https\_proxy | The HTTPS proxy used by this module. | +| ip\_address\_internal\_managed\_http | The internal/external IP addresses assigned to the HTTP forwarding rules. | +| ip\_address\_internal\_managed\_https | The internal/external IP addresses assigned to the HTTPS forwarding rules. | | ipv6\_enabled | Whether IPv6 configuration is enabled on this load-balancer | | ssl\_certificate\_created | The SSL certificate create from key/pem | | url\_map | The default URL map used by this module. | diff --git a/modules/frontend/main.tf b/modules/frontend/main.tf index 366ce6af..de2983b7 100644 --- a/modules/frontend/main.tf +++ b/modules/frontend/main.tf @@ -23,8 +23,9 @@ locals { create_http_forward = var.http_forward || var.https_redirect - is_internal = var.load_balancing_scheme == "INTERNAL_SELF_MANAGED" - internal_network = local.is_internal ? var.network : null + is_internal_self_managed = var.load_balancing_scheme == "INTERNAL_SELF_MANAGED" + is_internal_managed = var.load_balancing_scheme == "INTERNAL_MANAGED" + internal_network = local.is_internal_self_managed || local.is_internal_managed ? var.network : null # Create a map with hosts as keys and empty lists as initial values hosts = toset([for service in var.url_map_input : service.host]) @@ -46,7 +47,7 @@ locals { resource "google_compute_global_forwarding_rule" "http" { provider = google-beta project = var.project_id - count = local.create_http_forward ? 1 : 0 + count = local.create_http_forward && !local.is_internal_managed ? 1 : 0 name = var.name target = google_compute_target_http_proxy.default[0].self_link ip_address = local.address @@ -56,10 +57,27 @@ resource "google_compute_global_forwarding_rule" "http" { network = local.internal_network } +resource "google_compute_global_forwarding_rule" "internal_managed_http" { + for_each = local.create_http_forward && local.is_internal_managed ? { + for config in var.internal_forwarding_rules_config : config.region => config + } : {} + + provider = google-beta + project = var.project_id + name = "${var.name}-internal-managed-http-${each.key}" + target = google_compute_target_http_proxy.default[0].self_link + port_range = var.http_port + labels = var.labels + load_balancing_scheme = var.load_balancing_scheme + network = local.internal_network + subnetwork = each.value.subnetwork + ip_address = each.value.address +} + resource "google_compute_global_forwarding_rule" "https" { provider = google-beta project = var.project_id - count = var.ssl ? 1 : 0 + count = var.ssl && !local.is_internal_managed ? 1 : 0 name = "${var.name}-https" target = google_compute_target_https_proxy.default[0].self_link ip_address = local.address @@ -69,9 +87,26 @@ resource "google_compute_global_forwarding_rule" "https" { network = local.internal_network } +resource "google_compute_global_forwarding_rule" "internal_managed_https" { + for_each = var.ssl && local.is_internal_managed ? { + for config in var.internal_forwarding_rules_config : config.region => config + } : {} + + provider = google-beta + project = var.project_id + name = "${var.name}-internal-managed-https-${each.key}" + target = google_compute_target_https_proxy.default[0].self_link + port_range = var.https_port + labels = var.labels + load_balancing_scheme = var.load_balancing_scheme + network = local.internal_network + subnetwork = each.value.subnetwork + ip_address = each.value.address +} + resource "google_compute_global_address" "default" { provider = google-beta - count = local.is_internal ? 0 : var.create_address ? 1 : 0 + count = local.is_internal_self_managed || local.is_internal_managed ? 0 : var.create_address ? 1 : 0 project = var.project_id name = "${var.name}-address" ip_version = "IPV4" @@ -83,7 +118,7 @@ resource "google_compute_global_address" "default" { resource "google_compute_global_forwarding_rule" "http_ipv6" { provider = google-beta project = var.project_id - count = (var.enable_ipv6 && local.create_http_forward) ? 1 : 0 + count = (var.enable_ipv6 && local.create_http_forward && !local.is_internal_managed) ? 1 : 0 name = "${var.name}-ipv6-http" target = google_compute_target_http_proxy.default[0].self_link ip_address = local.ipv6_address @@ -93,10 +128,26 @@ resource "google_compute_global_forwarding_rule" "http_ipv6" { network = local.internal_network } +resource "google_compute_global_forwarding_rule" "internal_managed_http_ipv6" { + for_each = var.enable_ipv6 && local.create_http_forward && local.is_internal_managed ? { + for config in var.internal_forwarding_rules_config : config.region => config + } : {} + + provider = google-beta + project = var.project_id + name = "${var.name}-internal-managed-http-ipv6-${each.key}" + target = google_compute_target_http_proxy.default[0].self_link + port_range = "80" + labels = var.labels + load_balancing_scheme = var.load_balancing_scheme + subnetwork = each.value.subnetwork + ip_address = each.value.address +} + resource "google_compute_global_forwarding_rule" "https_ipv6" { provider = google-beta project = var.project_id - count = var.enable_ipv6 && var.ssl ? 1 : 0 + count = var.enable_ipv6 && var.ssl && !local.is_internal_managed ? 1 : 0 name = "${var.name}-ipv6-https" target = google_compute_target_https_proxy.default[0].self_link ip_address = local.ipv6_address @@ -106,9 +157,25 @@ resource "google_compute_global_forwarding_rule" "https_ipv6" { network = local.internal_network } +resource "google_compute_global_forwarding_rule" "internal_managed_https_ipv6" { + for_each = var.enable_ipv6 && var.ssl && local.is_internal_managed ? { + for config in var.internal_forwarding_rules_config : config.region => config + } : {} + + provider = google-beta + project = var.project_id + name = "${var.name}-internal-managed-https-ipv6-${each.key}" + target = google_compute_target_https_proxy.default[0].self_link + port_range = "443" + labels = var.labels + load_balancing_scheme = var.load_balancing_scheme + subnetwork = each.value.subnetwork + ip_address = each.value.address +} + resource "google_compute_global_address" "default_ipv6" { provider = google-beta - count = local.is_internal ? 0 : (var.enable_ipv6 && var.create_ipv6_address) ? 1 : 0 + count = local.is_internal_self_managed || local.is_internal_managed ? 0 : (var.enable_ipv6 && var.create_ipv6_address) ? 1 : 0 project = var.project_id name = "${var.name}-ipv6-address" ip_version = "IPV6" diff --git a/modules/frontend/metadata.display.yaml b/modules/frontend/metadata.display.yaml index 94428c24..6622c464 100644 --- a/modules/frontend/metadata.display.yaml +++ b/modules/frontend/metadata.display.yaml @@ -76,6 +76,11 @@ spec: load_balancing_scheme: name: load_balancing_scheme title: Load Balancing Scheme + enumValueLabels: + - label: EXTERNAL_MANAGED + value: EXTERNAL_MANAGED + - label: INTERNAL_MANAGED + value: INTERNAL_MANAGED managed_ssl_certificate_domains: name: managed_ssl_certificate_domains title: Managed Ssl Certificate Domains diff --git a/modules/frontend/metadata.yaml b/modules/frontend/metadata.yaml index 39b549b2..4cd66e47 100644 --- a/modules/frontend/metadata.yaml +++ b/modules/frontend/metadata.yaml @@ -50,6 +50,8 @@ spec: location: examples/https-gke - name: https-redirect location: examples/https-redirect + - name: internal-lb-cloud-run + location: examples/internal-lb-cloud-run - name: lb-http-separate-frontend-and-backend location: examples/lb-http-separate-frontend-and-backend - name: mig-nat-http-lb @@ -164,11 +166,11 @@ spec: varType: map(string) defaultValue: {} - name: load_balancing_scheme - description: Load balancing scheme type (EXTERNAL for classic external load balancer, EXTERNAL_MANAGED for Envoy-based load balancer, and INTERNAL_SELF_MANAGED for traffic director) + description: Load balancing scheme type (EXTERNAL for classic external load balancer, EXTERNAL_MANAGED for Envoy-based load balancer, INTERNAL_MANAGED for internal load balancer and INTERNAL_SELF_MANAGED for traffic director) varType: string defaultValue: EXTERNAL_MANAGED - name: network - description: Network for INTERNAL_SELF_MANAGED load balancing scheme + description: Network for internal load balancer varType: string defaultValue: default - name: server_tls_policy @@ -185,6 +187,15 @@ spec: - name: http_keep_alive_timeout_sec description: Specifies how long to keep a connection open, after completing a response, while there is no matching traffic (in seconds). varType: number + - name: internal_forwarding_rules_config + description: List of internal managed forwarding rules config. One of 'address' or 'subnetwork' is required for each. It is only applicable for internal load balancer + varType: |- + list(object({ + region = string + address = optional(string) + subnetwork = optional(string) + })) + defaultValue: [] outputs: - name: external_ip description: The external IPv4 assigned to the global fowarding rule. @@ -198,6 +209,10 @@ spec: - name: https_proxy description: The HTTPS proxy used by this module. type: string + - name: ip_address_internal_managed_http + description: The internal/external IP addresses assigned to the HTTP forwarding rules. + - name: ip_address_internal_managed_https + description: The internal/external IP addresses assigned to the HTTPS forwarding rules. - name: ipv6_enabled description: Whether IPv6 configuration is enabled on this load-balancer type: bool @@ -219,6 +234,8 @@ spec: - roles/run.admin - roles/iam.serviceAccountUser - roles/certificatemanager.owner + - roles/vpcaccess.admin + - roles/iam.serviceAccountAdmin services: - cloudresourcemanager.googleapis.com - storage-api.googleapis.com @@ -227,6 +244,7 @@ spec: - run.googleapis.com - iam.googleapis.com - certificatemanager.googleapis.com + - vpcaccess.googleapis.com providerVersions: - source: hashicorp/google version: ">= 6.0, < 7" diff --git a/modules/frontend/outputs.tf b/modules/frontend/outputs.tf index df3dd025..7f25310f 100644 --- a/modules/frontend/outputs.tf +++ b/modules/frontend/outputs.tf @@ -14,6 +14,16 @@ * limitations under the License. */ +output "ip_address_internal_managed_http" { + description = "The internal/external IP addresses assigned to the HTTP forwarding rules." + value = [for rule in google_compute_global_forwarding_rule.internal_managed_http : rule.ip_address] +} + +output "ip_address_internal_managed_https" { + description = "The internal/external IP addresses assigned to the HTTPS forwarding rules." + value = [for rule in google_compute_global_forwarding_rule.internal_managed_https : rule.ip_address] +} + output "external_ip" { description = "The external IPv4 assigned to the global fowarding rule." value = local.address diff --git a/modules/frontend/variables.tf b/modules/frontend/variables.tf index be93c406..7f11bc84 100644 --- a/modules/frontend/variables.tf +++ b/modules/frontend/variables.tf @@ -156,13 +156,13 @@ variable "labels" { } variable "load_balancing_scheme" { - description = "Load balancing scheme type (EXTERNAL for classic external load balancer, EXTERNAL_MANAGED for Envoy-based load balancer, and INTERNAL_SELF_MANAGED for traffic director)" + description = "Load balancing scheme type (EXTERNAL for classic external load balancer, EXTERNAL_MANAGED for Envoy-based load balancer, INTERNAL_MANAGED for internal load balancer and INTERNAL_SELF_MANAGED for traffic director)" type = string default = "EXTERNAL_MANAGED" } variable "network" { - description = "Network for INTERNAL_SELF_MANAGED load balancing scheme" + description = "Network for internal load balancer" type = string default = "default" } @@ -198,3 +198,13 @@ variable "http_keep_alive_timeout_sec" { type = number default = null } + +variable "internal_forwarding_rules_config" { + description = "List of internal managed forwarding rules config. One of 'address' or 'subnetwork' is required for each. It is only applicable for internal load balancer" + type = list(object({ + region = string + address = optional(string) + subnetwork = optional(string) + })) + default = [] +} diff --git a/modules/serverless_negs/metadata.yaml b/modules/serverless_negs/metadata.yaml index 1ba53db8..f709bc65 100644 --- a/modules/serverless_negs/metadata.yaml +++ b/modules/serverless_negs/metadata.yaml @@ -50,6 +50,8 @@ spec: location: examples/https-gke - name: https-redirect location: examples/https-redirect + - name: internal-lb-cloud-run + location: examples/internal-lb-cloud-run - name: lb-http-separate-frontend-and-backend location: examples/lb-http-separate-frontend-and-backend - name: mig-nat-http-lb @@ -294,6 +296,8 @@ spec: - roles/run.admin - roles/iam.serviceAccountUser - roles/certificatemanager.owner + - roles/vpcaccess.admin + - roles/iam.serviceAccountAdmin services: - cloudresourcemanager.googleapis.com - storage-api.googleapis.com @@ -302,6 +306,7 @@ spec: - run.googleapis.com - iam.googleapis.com - certificatemanager.googleapis.com + - vpcaccess.googleapis.com providerVersions: - source: hashicorp/google version: ">= 6.0, < 7" diff --git a/test/integration/internal-lb-cloud-run/internal_lb_cloud_run_test.go b/test/integration/internal-lb-cloud-run/internal_lb_cloud_run_test.go new file mode 100644 index 00000000..6472a313 --- /dev/null +++ b/test/integration/internal-lb-cloud-run/internal_lb_cloud_run_test.go @@ -0,0 +1,47 @@ +// 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 internal_lb_cloud_run + +import ( + "testing" + + "net/http" + + "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/tft" + "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/utils" + "github.com/stretchr/testify/assert" +) + +func TestInternalLbCloudRun(t *testing.T) { + bpt := tft.NewTFBlueprintTest(t) + + bpt.DefineVerify(func(assert *assert.Assertions) { + bpt.DefaultVerify(assert) + + cloudRunURIs := bpt.GetStringOutputList("external_cloudrun_uris") + + assertHttp := utils.NewAssertHTTP() + + for _, uri := range cloudRunURIs { + httpRequest, err := http.NewRequest("GET", uri, nil) + if err != nil { + t.Fatalf("Failed to create HTTP request for %s: %v", uri, err) + } + assertHttp.AssertResponse(t, httpRequest, http.StatusOK) + } + }) + + bpt.Test() +} diff --git a/test/setup/iam.tf b/test/setup/iam.tf index 6cba4dca..033e829d 100644 --- a/test/setup/iam.tf +++ b/test/setup/iam.tf @@ -20,7 +20,9 @@ locals { "roles/compute.admin", "roles/run.admin", "roles/iam.serviceAccountUser", - "roles/certificatemanager.owner" + "roles/certificatemanager.owner", + "roles/vpcaccess.admin", + "roles/iam.serviceAccountAdmin" ] int_required_folder_roles = [ "roles/compute.xpnAdmin" diff --git a/test/setup/main.tf b/test/setup/main.tf index c158fa58..3d51ae0c 100644 --- a/test/setup/main.tf +++ b/test/setup/main.tf @@ -36,6 +36,7 @@ module "project-ci-lb-http" { "run.googleapis.com", "iam.googleapis.com", "certificatemanager.googleapis.com", + "vpcaccess.googleapis.com", ] } @@ -61,5 +62,6 @@ module "project-ci-lb-http-1" { "run.googleapis.com", "iam.googleapis.com", "certificatemanager.googleapis.com", + "vpcaccess.googleapis.com", ] }