diff --git a/modules/net-lb-int/README.md b/modules/net-lb-int/README.md index 24308dab04..49e393783b 100644 --- a/modules/net-lb-int/README.md +++ b/modules/net-lb-int/README.md @@ -10,6 +10,7 @@ This module allows managing a GCE Internal Load Balancer and integrates the forw - [Multiple forwarding rules](#multiple-forwarding-rules) - [Dual stack (IPv4 and IPv6)](#dual-stack-ipv4-and-ipv6) - [PSC service attachments](#psc-service-attachments) + - [Zonal affinity traffic policy](#zonal-affinity-traffic-policy) - [Regional health check](#regional-health-check) - [End to end example](#end-to-end-example) - [Context](#context) @@ -282,6 +283,45 @@ module "ilb" { # tftest modules=1 resources=7 ``` +### Zonal affinity traffic policy + +The `backend_service_config.network_pass_through_lb_traffic_policy` block allows tuning the backend service behavior for network passthrough load balancers, including zonal affinity spillover settings. + +```hcl +module "ilb" { + source = "./fabric/modules/net-lb-int" + project_id = var.project_id + region = "europe-west1" + name = "ilb-test" + service_label = "ilb-test" + vpc_config = { + network = var.vpc.self_link + subnetwork = var.subnet.self_link + } + backend_service_config = { + network_pass_through_lb_traffic_policy = { + zonal_affinity = { + spillover = "ZONAL_AFFINITY_SPILL_CROSS_ZONE" + spillover_ratio = 0.5 + } + } + } + group_configs = { + my-group = { + zone = "europe-west1-b" + instances = [ + "instance-1-self-link", + "instance-2-self-link" + ] + } + } + backends = [{ + group = module.ilb.groups.my-group.self_link + }] +} +# tftest modules=1 resources=4 +``` + ### Regional health check The `is_regional` flag in the `health_check_config` block allows creating a regional health check instead of a global one. @@ -459,21 +499,21 @@ One other issue is a `Provider produced inconsistent final plan` error which is | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [name](variables.tf#L208) | Name used for all resources. | string | ✓ | | -| [project_id](variables.tf#L213) | Project id where resources will be created. | string | ✓ | | -| [region](variables.tf#L218) | GCP region. | string | ✓ | | -| [vpc_config](variables.tf#L244) | VPC-level configuration. | object({…}) | ✓ | | +| [name](variables.tf#L235) | Name used for all resources. | string | ✓ | | +| [project_id](variables.tf#L240) | Project id where resources will be created. | string | ✓ | | +| [region](variables.tf#L245) | GCP region. | string | ✓ | | +| [vpc_config](variables.tf#L271) | VPC-level configuration. | object({…}) | ✓ | | | [backend_service_config](variables.tf#L17) | Backend service level configuration. | object({…}) | | {} | -| [backends](variables.tf#L58) | Load balancer backends. | list(object({…})) | | [] | -| [context](variables.tf#L69) | Context-specific interpolations. | object({…}) | | {} | -| [description](variables.tf#L82) | Optional description used for resources. | string | | "Terraform managed." | -| [forwarding_rules_config](variables.tf#L88) | The optional forwarding rules configuration. | map(object({…})) | | {…} | -| [group_configs](variables.tf#L104) | Optional unmanaged groups to create. Can be referenced in backends via outputs. | map(object({…})) | | {} | -| [health_check](variables.tf#L117) | Name of existing health check to use, disables auto-created health check. Also set `health_check_config = null` when cross-referencing an health check from another load balancer module to avoid a Terraform error. | string | | null | -| [health_check_config](variables.tf#L123) | Optional auto-created health check configuration, use the output self-link to set it in the auto healing policy. Refer to examples for usage. | object({…}) | | {…} | -| [labels](variables.tf#L202) | Labels set on resources. | map(string) | | {} | -| [service_attachments](variables.tf#L223) | PSC service attachments, keyed by forwarding rule. | map(object({…})) | | null | -| [service_label](variables.tf#L238) | Optional prefix of the fully qualified forwarding rule name. | string | | null | +| [backends](variables.tf#L85) | Load balancer backends. | list(object({…})) | | [] | +| [context](variables.tf#L96) | Context-specific interpolations. | object({…}) | | {} | +| [description](variables.tf#L109) | Optional description used for resources. | string | | "Terraform managed." | +| [forwarding_rules_config](variables.tf#L115) | The optional forwarding rules configuration. | map(object({…})) | | {…} | +| [group_configs](variables.tf#L131) | Optional unmanaged groups to create. Can be referenced in backends via outputs. | map(object({…})) | | {} | +| [health_check](variables.tf#L144) | Name of existing health check to use, disables auto-created health check. Also set `health_check_config = null` when cross-referencing an health check from another load balancer module to avoid a Terraform error. | string | | null | +| [health_check_config](variables.tf#L150) | Optional auto-created health check configuration, use the output self-link to set it in the auto healing policy. Refer to examples for usage. | object({…}) | | {…} | +| [labels](variables.tf#L229) | Labels set on resources. | map(string) | | {} | +| [service_attachments](variables.tf#L250) | PSC service attachments, keyed by forwarding rule. | map(object({…})) | | null | +| [service_label](variables.tf#L265) | Optional prefix of the fully qualified forwarding rule name. | string | | null | ## Outputs diff --git a/modules/net-lb-int/main.tf b/modules/net-lb-int/main.tf index 273b5cc957..368cccd87b 100644 --- a/modules/net-lb-int/main.tf +++ b/modules/net-lb-int/main.tf @@ -21,6 +21,7 @@ locals { ) bs_conntrack = var.backend_service_config.connection_tracking bs_failover = var.backend_service_config.failover_config + bs_nptlb = var.backend_service_config.network_pass_through_lb_traffic_policy forwarding_rule_names = { for k, v in var.forwarding_rules_config : k => k == "" ? var.name : "${var.name}-${k}" @@ -150,6 +151,18 @@ resource "google_compute_region_backend_service" "default" { } } + dynamic "network_pass_through_lb_traffic_policy" { + for_each = local.bs_nptlb != null ? [""] : [] + content { + dynamic "zonal_affinity" { + for_each = local.bs_nptlb.zonal_affinity != null ? [""] : [] + content { + spillover = local.bs_nptlb.zonal_affinity.spillover + spillover_ratio = local.bs_nptlb.zonal_affinity.spillover_ratio + } + } + } + } } resource "google_compute_service_attachment" "default" { diff --git a/modules/net-lb-int/variables.tf b/modules/net-lb-int/variables.tf index 2f1ce22f71..2583bf8a67 100644 --- a/modules/net-lb-int/variables.tf +++ b/modules/net-lb-int/variables.tf @@ -35,6 +35,12 @@ variable "backend_service_config" { optional_mode = optional(string) optional_fields = optional(list(string)) })) + network_pass_through_lb_traffic_policy = optional(object({ + zonal_affinity = object({ + spillover = optional(string, "ZONAL_AFFINITY_DISABLED") + spillover_ratio = optional(number) + }) + })) name = optional(string) description = optional(string, "Terraform managed.") protocol = optional(string, "UNSPECIFIED") @@ -53,6 +59,27 @@ variable "backend_service_config" { ) error_message = "Invalid session affinity value." } + validation { + condition = ( + try(var.backend_service_config.network_pass_through_lb_traffic_policy, null) == null + || contains( + ["ZONAL_AFFINITY_DISABLED", "ZONAL_AFFINITY_SPILL_CROSS_ZONE", "ZONAL_AFFINITY_STAY_WITHIN_ZONE"], + coalesce(var.backend_service_config.network_pass_through_lb_traffic_policy.zonal_affinity.spillover, "ZONAL_AFFINITY_DISABLED") + ) + ) + error_message = "network_pass_through_lb_traffic_policy.zonal_affinity.spillover must be one of ZONAL_AFFINITY_DISABLED, ZONAL_AFFINITY_SPILL_CROSS_ZONE, or ZONAL_AFFINITY_STAY_WITHIN_ZONE." + } + validation { + condition = ( + try(var.backend_service_config.network_pass_through_lb_traffic_policy, null) == null + || try(var.backend_service_config.network_pass_through_lb_traffic_policy.zonal_affinity.spillover_ratio, null) == null + || ( + var.backend_service_config.network_pass_through_lb_traffic_policy.zonal_affinity.spillover_ratio >= 0 && + var.backend_service_config.network_pass_through_lb_traffic_policy.zonal_affinity.spillover_ratio <= 1 + ) + ) + error_message = "network_pass_through_lb_traffic_policy.zonal_affinity.spillover_ratio must be a number between 0 and 1." + } } variable "backends" { diff --git a/tests/modules/net_lb_int/tftest.yaml b/tests/modules/net_lb_int/tftest.yaml index a691278c5c..2cbaa775aa 100644 --- a/tests/modules/net_lb_int/tftest.yaml +++ b/tests/modules/net_lb_int/tftest.yaml @@ -24,3 +24,4 @@ tests: health-checks-https: health-checks-ssl: health-checks-tcp: + zonal-affinity: diff --git a/tests/modules/net_lb_int/zonal-affinity.tfvars b/tests/modules/net_lb_int/zonal-affinity.tfvars new file mode 100644 index 0000000000..788935710a --- /dev/null +++ b/tests/modules/net_lb_int/zonal-affinity.tfvars @@ -0,0 +1,32 @@ +# Copyright 2026 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. + +project_id = "my-project" +region = "europe-west1" +name = "ilb-test" +vpc_config = { + network = "default" + subnetwork = "default" +} +backends = [{ + group = "foo" +}] +backend_service_config = { + network_pass_through_lb_traffic_policy = { + zonal_affinity = { + spillover = "ZONAL_AFFINITY_SPILL_CROSS_ZONE" + spillover_ratio = 0.5 + } + } +} diff --git a/tests/modules/net_lb_int/zonal-affinity.yaml b/tests/modules/net_lb_int/zonal-affinity.yaml new file mode 100644 index 0000000000..9fb607c297 --- /dev/null +++ b/tests/modules/net_lb_int/zonal-affinity.yaml @@ -0,0 +1,23 @@ +# Copyright 2026 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. + +values: + google_compute_region_backend_service.default: + network_pass_through_lb_traffic_policy: + - zonal_affinity: + - spillover: ZONAL_AFFINITY_SPILL_CROSS_ZONE + spillover_ratio: 0.5 + +counts: + google_compute_region_backend_service: 1