Add net-lb-proxy-int-cross-region module and tests (#4017)

* Add net-lb-proxy-int-cross-region module and tests

* Add context support example and tests

* Update copyright to 2026 and support instance group backends

* docs: correct Instance Groups support note in README
This commit is contained in:
Simone Ruffilli
2026-06-11 09:56:01 +02:00
committed by GitHub
parent e753344bd2
commit 8adb84c95c
17 changed files with 1699 additions and 0 deletions

View File

@@ -0,0 +1,269 @@
# Cross-region Internal Proxy Network Load Balancer Module
This module allows managing Cross-region Internal Proxy Network Load Balancers (L4 proxy ILBs). It's designed to expose the full configuration of the underlying resources, and to facilitate common usage patterns by providing sensible defaults, and optionally managing prerequisite resources like health checks, network endpoint groups (NEGs), etc.
> [!IMPORTANT]
> Cross-region internal proxy Network Load Balancers support Instance Groups (Managed or Unmanaged), zonal NEGs (`GCE_VM_IP_PORT`), and Hybrid NEGs as backends.
>
> Proxy-only subnets for cross-region load balancers must be created with the purpose set to `GLOBAL_MANAGED_PROXY` (in the VPC module, set `global = true` in the `subnets_proxy_only` configuration).
## Examples
<!-- BEGIN TOC -->
- [Examples](#examples)
- [Minimal Example](#minimal-example)
- [Health Checks](#health-checks)
- [Network Endpoint Groups (NEGs)](#network-endpoint-groups-negs)
- [Zonal NEG creation](#zonal-neg-creation)
- [Hybrid NEG creation](#hybrid-neg-creation)
- [Variables](#variables)
- [Outputs](#outputs)
<!-- END TOC -->
### Minimal Example
An internal TCP proxy cross-region load balancer with a backend service pointing to auto-created zonal NEGs. This example demonstrates the use of the [context pattern](../../adrs/20251013-context-locals.md) for symbolic variable interpolation:
```hcl
module "tcp-proxy-cross-region" {
source = "./fabric/modules/net-lb-proxy-int-cross-region"
name = "ilb-proxy-test"
project_id = "$project_ids:my-project"
port = 80
context = {
project_ids = {
my-project = var.project_id
}
networks = {
my-vpc = var.vpc.self_link
}
subnets = {
my-subnet-1 = var.subnet1.self_link
my-subnet-2 = var.subnet2.self_link
}
}
vpc_config = {
network = "$networks:my-vpc"
subnetworks = {
europe-west1 = "$subnets:my-subnet-1"
europe-west4 = "$subnets:my-subnet-2"
}
}
neg_configs = {
neg-a = {
gce = {
zone = "europe-west1-b"
network = "$networks:my-vpc"
subnetwork = "$subnets:my-subnet-1"
endpoints = {
vm-a = {
instance = "my-vm-a"
ip_address = "10.0.0.2"
port = 80
}
}
}
}
neg-b = {
gce = {
zone = "europe-west4-a"
network = "$networks:my-vpc"
subnetwork = "$subnets:my-subnet-2"
endpoints = {
vm-b = {
instance = "my-vm-b"
ip_address = "10.0.1.2"
port = 80
}
}
}
}
}
backend_service_config = {
backends = [
{
group = "neg-a"
max_connections = {
per_endpoint = 100
}
},
{
group = "neg-b"
max_connections = {
per_endpoint = 100
}
}
]
}
}
# tftest modules=1 resources=9
```
### Health Checks
You can leverage externally defined health checks for backend services, or have the module create them for you. By default a simple TCP health check is created, and used in backend services.
Defining a custom health check:
```hcl
module "tcp-proxy-cross-region" {
source = "./fabric/modules/net-lb-proxy-int-cross-region"
name = "ilb-proxy-test"
project_id = var.project_id
port = 80
vpc_config = {
network = var.vpc.self_link
subnetworks = {
europe-west1 = var.subnet1.self_link
}
}
backend_service_config = {
backends = [{
group = "projects/my-project/zones/europe-west1-b/networkEndpointGroups/my-neg"
max_connections = {
per_endpoint = 100
}
}]
}
health_check_config = {
tcp = {
port = 8080
}
}
}
# tftest modules=1 resources=4
```
### Network Endpoint Groups (NEGs)
#### Zonal NEG creation
You can have the module create zonal NEGs for you by defining the `neg_configs` variable:
```hcl
module "tcp-proxy-cross-region" {
source = "./fabric/modules/net-lb-proxy-int-cross-region"
name = "ilb-proxy-test"
project_id = var.project_id
port = 80
vpc_config = {
network = var.vpc.self_link
subnetworks = {
europe-west1 = var.subnet1.self_link
}
}
neg_configs = {
neg-a = {
gce = {
zone = "europe-west1-b"
network = var.vpc.self_link
subnetwork = var.subnet1.self_link
}
}
}
backend_service_config = {
backends = [{
group = "neg-a"
max_connections = {
per_endpoint = 100
}
}]
}
}
# tftest modules=1 resources=5
```
#### Hybrid NEG creation
You can also configure hybrid NEGs:
```hcl
module "tcp-proxy-cross-region" {
source = "./fabric/modules/net-lb-proxy-int-cross-region"
name = "ilb-proxy-test"
project_id = var.project_id
port = 80
vpc_config = {
network = var.vpc.self_link
subnetworks = {
europe-west1 = var.subnet1.self_link
}
}
neg_configs = {
hybrid-a = {
hybrid = {
network = var.vpc.self_link
zone = "europe-west1-b"
endpoints = {
endpoint-1 = {
ip_address = "10.10.10.1"
port = 80
}
}
}
}
}
backend_service_config = {
backends = [{
group = "hybrid-a"
max_connections = {
per_endpoint = 100
}
}]
}
}
# tftest modules=1 resources=6
```
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [name](variables.tf#L204) | Load balancer name. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L258) | Project id. | <code>string</code> | ✓ | |
| [vpc_config](variables.tf#L274) | VPC-level configuration. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | |
| [addresses](variables.tf#L17) | Optional IP addresses used for the forwarding rules, mapped by subnetwork key. | <code>map&#40;string&#41;</code> | | <code>null</code> |
| [backend_service_config](variables.tf#L23) | Backend service level configuration. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [context](variables.tf#L72) | Context-specific interpolations. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [description](variables.tf#L85) | Optional description used for resources. | <code>string</code> | | <code>&#34;Terraform managed.&#34;</code> |
| [group_configs](variables.tf#L91) | Optional unmanaged groups to create. Can be referenced in backends via key or outputs. | <code>map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [health_check](variables.tf#L105) | Name of existing health check to use, disables auto-created health check. | <code>string</code> | | <code>null</code> |
| [health_check_config](variables.tf#L111) | Optional auto-created health check configurations. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#8230;&#125;</code> |
| [labels](variables.tf#L198) | Labels set on resources. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [neg_configs](variables.tf#L209) | Optional network endpoint groups to create. Can be referenced in backends via key or outputs. | <code>map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [port](variables.tf#L252) | Forwarding rule port. Cross-region internal proxy load balancers support a single port. | <code>number</code> | | <code>80</code> |
| [target_proxy_config](variables.tf#L263) | Target proxy configuration. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [addresses](outputs.tf#L17) | Forwarding rule addresses. | |
| [backend_service](outputs.tf#L22) | Backend resource. | |
| [backend_service_id](outputs.tf#L27) | Backend id. | |
| [backend_service_self_link](outputs.tf#L32) | Backend self link. | |
| [forwarding_rules](outputs.tf#L37) | Forwarding rule resources. | |
| [group_self_links](outputs.tf#L42) | Optional unmanaged instance group self links. | |
| [groups](outputs.tf#L49) | Optional unmanaged instance group resources. | |
| [health_check](outputs.tf#L54) | Auto-created health-check resource. | |
| [health_check_id](outputs.tf#L59) | Auto-created health-check id. | |
| [health_check_self_link](outputs.tf#L64) | Auto-created health-check self link. | |
| [ids](outputs.tf#L69) | Fully qualified forwarding rule ids. | |
| [neg_ids](outputs.tf#L74) | Autogenerated network endpoint group ids. | |
| [psc_neg_ids](outputs.tf#L81) | Autogenerated PSC network endpoint group ids. | |
| [target_proxy](outputs.tf#L88) | Target proxy resource. | |
| [target_proxy_id](outputs.tf#L93) | Target proxy id. | |
<!-- END TFDOC -->

View File

@@ -0,0 +1,76 @@
/**
* 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.
*/
# tfdoc:file:description Backend service resources.
locals {
group_ids = merge(
{
for k, v in google_compute_instance_group.default : k => v.id
},
{
for k, v in google_compute_network_endpoint_group.default : k => v.id
},
{
for k, v in google_compute_region_network_endpoint_group.psc : k => v.id
}
)
}
resource "google_compute_backend_service" "default" {
provider = google-beta
project = local.project_id
name = coalesce(var.backend_service_config.name, var.name)
description = var.backend_service_config.description
affinity_cookie_ttl_sec = var.backend_service_config.affinity_cookie_ttl_sec
connection_draining_timeout_sec = var.backend_service_config.connection_draining_timeout_sec
health_checks = [local.health_check]
load_balancing_scheme = "INTERNAL_MANAGED"
port_name = var.backend_service_config.port_name
protocol = "TCP"
session_affinity = var.backend_service_config.session_affinity
timeout_sec = var.backend_service_config.timeout_sec
dynamic "backend" {
for_each = { for b in coalesce(var.backend_service_config.backends, []) : b.group => b }
content {
group = lookup(local.group_ids, backend.key, backend.key)
balancing_mode = backend.value.balancing_mode
capacity_scaler = backend.value.capacity_scaler
description = backend.value.description
max_connections = try(
backend.value.max_connections.per_group, null
)
max_connections_per_endpoint = try(
backend.value.max_connections.per_endpoint, null
)
max_connections_per_instance = try(
backend.value.max_connections.per_instance, null
)
max_utilization = backend.value.max_utilization
}
}
dynamic "log_config" {
for_each = var.backend_service_config.log_config == null ? [] : [""]
content {
enable = var.backend_service_config.log_config.enable
sample_rate = var.backend_service_config.log_config.sample_rate
optional_mode = var.backend_service_config.log_config.optional_mode
optional_fields = var.backend_service_config.log_config.optional_fields
}
}
}

View File

@@ -0,0 +1,38 @@
/**
* 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.
*/
# tfdoc:file:description Instance group resources.
resource "google_compute_instance_group" "default" {
for_each = var.group_configs
project = (
each.value.project_id == null
? local.project_id
: each.value.project_id
)
zone = try(local.ctx.locations[each.value.zone], each.value.zone)
name = coalesce(each.value.name, "${var.name}-${each.key}")
description = each.value.description
instances = each.value.instances
dynamic "named_port" {
for_each = each.value.named_ports
content {
name = named_port.key
port = named_port.value
}
}
}

View File

@@ -0,0 +1,121 @@
/**
* 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.
*/
# tfdoc:file:description Health check resource.
locals {
hc = (
var.health_check != null ? null : var.health_check_config
)
hc_grpc = try(local.hc.grpc, null) != null
hc_http = try(local.hc.http, null) != null
hc_http2 = try(local.hc.http2, null) != null
hc_https = try(local.hc.https, null) != null
hc_ssl = try(local.hc.ssl, null) != null
hc_tcp = try(local.hc.tcp, null) != null
}
resource "google_compute_health_check" "default" {
provider = google-beta
count = local.hc != null ? 1 : 0
project = local.project_id
name = coalesce(local.hc.name, var.name)
description = local.hc.description
check_interval_sec = local.hc.check_interval_sec
healthy_threshold = local.hc.healthy_threshold
timeout_sec = local.hc.timeout_sec
unhealthy_threshold = local.hc.unhealthy_threshold
dynamic "grpc_health_check" {
for_each = local.hc_grpc ? [""] : []
content {
port = local.hc.grpc.port
port_name = local.hc.grpc.port_name
port_specification = local.hc.grpc.port_specification
grpc_service_name = local.hc.grpc.service_name
}
}
dynamic "http_health_check" {
for_each = local.hc_http ? [""] : []
content {
host = local.hc.http.host
port = local.hc.http.port
port_name = local.hc.http.port_name
port_specification = local.hc.http.port_specification
proxy_header = local.hc.http.proxy_header
request_path = local.hc.http.request_path
response = local.hc.http.response
}
}
dynamic "http2_health_check" {
for_each = local.hc_http2 ? [""] : []
content {
host = local.hc.http2.host
port = local.hc.http2.port
port_name = local.hc.http2.port_name
port_specification = local.hc.http2.port_specification
proxy_header = local.hc.http2.proxy_header
request_path = local.hc.http2.request_path
response = local.hc.http2.response
}
}
dynamic "https_health_check" {
for_each = local.hc_https ? [""] : []
content {
host = local.hc.https.host
port = local.hc.https.port
port_name = local.hc.https.port_name
port_specification = local.hc.https.port_specification
proxy_header = local.hc.https.proxy_header
request_path = local.hc.https.request_path
response = local.hc.https.response
}
}
dynamic "ssl_health_check" {
for_each = local.hc_ssl ? [""] : []
content {
port = local.hc.ssl.port
port_name = local.hc.ssl.port_name
port_specification = local.hc.ssl.port_specification
proxy_header = local.hc.ssl.proxy_header
request = local.hc.ssl.request
response = local.hc.ssl.response
}
}
dynamic "tcp_health_check" {
for_each = local.hc_tcp ? [""] : []
content {
port = local.hc.tcp.port
port_name = local.hc.tcp.port_name
port_specification = local.hc.tcp.port_specification
proxy_header = local.hc.tcp.proxy_header
request = local.hc.tcp.request
response = local.hc.tcp.response
}
}
dynamic "log_config" {
for_each = try(local.hc.enable_logging, false) ? [""] : []
content {
enable = true
}
}
}

View File

@@ -0,0 +1,135 @@
/**
* 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.
*/
locals {
ctx = {
for k, v in var.context : k => {
for kk, vv in v : "${local.ctx_p}${k}:${kk}" => vv
}
}
ctx_p = "$"
network = lookup(local.ctx.networks, var.vpc_config.network, var.vpc_config.network)
project_id = lookup(local.ctx.project_ids, var.project_id, var.project_id)
}
locals {
_neg_endpoints = flatten([
for k, v in local.neg_zonal : [
for kk, vv in v.endpoints : merge(vv, {
key = "${k}-${kk}", neg = k, zone = v.zone
})
]
])
addresses = {
for k, v in coalesce(var.addresses, {}) : k => lookup(local.ctx.addresses, v, v)
}
health_check = (
var.health_check != null
? var.health_check
: google_compute_health_check.default[0].self_link
)
neg_endpoints = {
for v in local._neg_endpoints : (v.key) => v
}
neg_zonal = {
for k, v in var.neg_configs : k => {
endpoints = v.gce != null ? v.gce.endpoints : v.hybrid.endpoints
network = v.gce != null ? v.gce.network : v.hybrid.network
project_id = v.project_id
subnetwork = v.gce != null ? v.gce.subnetwork : null
type = v.gce != null ? "GCE_VM_IP_PORT" : "NON_GCP_PRIVATE_IP_PORT"
zone = v.gce != null ? v.gce.zone : v.hybrid.zone
} if v.gce != null || v.hybrid != null
}
neg_regional_psc = {
for k, v in var.neg_configs :
k => v if v.psc != null
}
subnetworks = {
for k, v in var.vpc_config.subnetworks : k => lookup(local.ctx.subnets, v, v)
}
}
resource "google_compute_global_forwarding_rule" "default" {
for_each = local.subnetworks
provider = google-beta
project = local.project_id
name = "${var.name}-${each.key}"
description = var.description
ip_address = try(local.addresses[each.key], null)
ip_protocol = "TCP"
load_balancing_scheme = "INTERNAL_MANAGED"
network = local.network
port_range = tostring(var.port)
subnetwork = each.value
labels = var.labels
target = google_compute_target_tcp_proxy.default.id
}
resource "google_compute_target_tcp_proxy" "default" {
project = local.project_id
name = coalesce(var.target_proxy_config.name, var.name)
description = var.target_proxy_config.description
backend_service = google_compute_backend_service.default.id
proxy_header = var.target_proxy_config.proxy_header
}
resource "google_compute_network_endpoint_group" "default" {
for_each = local.neg_zonal
project = (
each.value.project_id == null
? local.project_id
: each.value.project_id
)
name = "${var.name}-${each.key}"
network = (
each.value.network == null
? null
: lookup(local.ctx.networks, each.value.network, each.value.network)
)
subnetwork = (
each.value.subnetwork == null
? null
: lookup(local.ctx.subnets, each.value.subnetwork, each.value.subnetwork)
)
network_endpoint_type = each.value.type
zone = lookup(local.ctx.locations, each.value.zone, each.value.zone)
}
resource "google_compute_network_endpoint" "default" {
for_each = local.neg_endpoints
project = (
local.neg_zonal[each.value.neg].project_id == null
? local.project_id
: local.neg_zonal[each.value.neg].project_id
)
network_endpoint_group = google_compute_network_endpoint_group.default[each.value.neg].name
zone = lookup(local.ctx.locations, each.value.zone, each.value.zone)
instance = try(each.value.instance, null)
ip_address = each.value.ip_address
port = each.value.port
}
resource "google_compute_region_network_endpoint_group" "psc" {
for_each = local.neg_regional_psc
project = coalesce(each.value.project_id, local.project_id)
name = "${var.name}-${each.key}"
region = lookup(local.ctx.locations, each.value.psc.region, each.value.psc.region)
network = lookup(local.ctx.networks, each.value.psc.network, each.value.psc.network)
subnetwork = lookup(local.ctx.subnets, each.value.psc.subnetwork, each.value.psc.subnetwork)
network_endpoint_type = "PRIVATE_SERVICE_CONNECT"
psc_target_service = each.value.psc.target_service
}

View File

@@ -0,0 +1,96 @@
/**
* 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.
*/
output "addresses" {
description = "Forwarding rule addresses."
value = { for k, v in google_compute_global_forwarding_rule.default : k => v.ip_address }
}
output "backend_service" {
description = "Backend resource."
value = google_compute_backend_service.default
}
output "backend_service_id" {
description = "Backend id."
value = google_compute_backend_service.default.id
}
output "backend_service_self_link" {
description = "Backend self link."
value = google_compute_backend_service.default.self_link
}
output "forwarding_rules" {
description = "Forwarding rule resources."
value = google_compute_global_forwarding_rule.default
}
output "group_self_links" {
description = "Optional unmanaged instance group self links."
value = {
for k, v in google_compute_instance_group.default : k => v.self_link
}
}
output "groups" {
description = "Optional unmanaged instance group resources."
value = google_compute_instance_group.default
}
output "health_check" {
description = "Auto-created health-check resource."
value = try(google_compute_health_check.default[0], null)
}
output "health_check_id" {
description = "Auto-created health-check id."
value = try(google_compute_health_check.default[0].id, null)
}
output "health_check_self_link" {
description = "Auto-created health-check self link."
value = try(google_compute_health_check.default[0].self_link, null)
}
output "ids" {
description = "Fully qualified forwarding rule ids."
value = { for k, v in google_compute_global_forwarding_rule.default : k => v.id }
}
output "neg_ids" {
description = "Autogenerated network endpoint group ids."
value = {
for k, v in google_compute_network_endpoint_group.default : k => v.id
}
}
output "psc_neg_ids" {
description = "Autogenerated PSC network endpoint group ids."
value = {
for k, v in google_compute_region_network_endpoint_group.psc : k => v.id
}
}
output "target_proxy" {
description = "Target proxy resource."
value = google_compute_target_tcp_proxy.default
}
output "target_proxy_id" {
description = "Target proxy id."
value = google_compute_target_tcp_proxy.default.id
}

View File

@@ -0,0 +1,281 @@
/**
* 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.
*/
variable "addresses" {
description = "Optional IP addresses used for the forwarding rules, mapped by subnetwork key."
type = map(string)
default = null
}
variable "backend_service_config" {
description = "Backend service level configuration."
type = object({
name = optional(string)
description = optional(string, "Terraform managed.")
affinity_cookie_ttl_sec = optional(number)
connection_draining_timeout_sec = optional(number)
health_checks = optional(list(string), ["default"])
log_config = optional(object({
enable = optional(bool)
sample_rate = optional(number)
optional_mode = optional(string)
optional_fields = optional(list(string))
}))
port_name = optional(string)
project_id = optional(string)
session_affinity = optional(string, "NONE")
timeout_sec = optional(number)
backends = optional(list(object({
group = string
balancing_mode = optional(string, "CONNECTION")
capacity_scaler = optional(number, 1)
description = optional(string, "Terraform managed.")
max_connections = optional(object({
per_endpoint = optional(number)
per_group = optional(number)
per_instance = optional(number)
}))
max_utilization = optional(number)
})))
})
default = {}
nullable = false
validation {
condition = (var.backend_service_config == null || contains(["NONE", "CLIENT_IP"],
var.backend_service_config.session_affinity
))
error_message = "Invalid session affinity value."
}
validation {
condition = alltrue([
for b in var.backend_service_config.backends : contains(
["CONNECTION", "UTILIZATION"], coalesce(b.balancing_mode, "CONNECTION")
)
])
error_message = "When specified, balancing mode needs to be 'CONNECTION' or 'UTILIZATION'."
}
}
variable "context" {
description = "Context-specific interpolations."
type = object({
addresses = optional(map(string), {})
locations = optional(map(string), {})
networks = optional(map(string), {})
project_ids = optional(map(string), {})
subnets = optional(map(string), {})
})
default = {}
nullable = false
}
variable "description" {
description = "Optional description used for resources."
type = string
default = "Terraform managed."
}
variable "group_configs" {
description = "Optional unmanaged groups to create. Can be referenced in backends via key or outputs."
type = map(object({
name = optional(string)
description = optional(string, "Terraform managed.")
zone = string
instances = optional(list(string))
named_ports = optional(map(number), {})
project_id = optional(string)
}))
default = {}
nullable = false
}
variable "health_check" {
description = "Name of existing health check to use, disables auto-created health check."
type = string
default = null
}
variable "health_check_config" {
description = "Optional auto-created health check configurations."
type = object({
name = optional(string)
check_interval_sec = optional(number)
description = optional(string, "Terraform managed.")
enable_logging = optional(bool, false)
healthy_threshold = optional(number)
project_id = optional(string)
timeout_sec = optional(number)
unhealthy_threshold = optional(number)
grpc = optional(object({
port = optional(number)
port_name = optional(string)
port_specification = optional(string)
service_name = optional(string)
}))
http = optional(object({
host = optional(string)
port = optional(number)
port_name = optional(string)
port_specification = optional(string)
proxy_header = optional(string)
request_path = optional(string)
response = optional(string)
}))
http2 = optional(object({
host = optional(string)
port = optional(number)
port_name = optional(string)
port_specification = optional(string)
proxy_header = optional(string)
request_path = optional(string)
response = optional(string)
}))
https = optional(object({
host = optional(string)
port = optional(number)
port_name = optional(string)
port_specification = optional(string)
proxy_header = optional(string)
request_path = optional(string)
response = optional(string)
}))
tcp = optional(object({
port = optional(number)
port_name = optional(string)
port_specification = optional(string)
proxy_header = optional(string)
request = optional(string)
response = optional(string)
}))
ssl = optional(object({
port = optional(number)
port_name = optional(string)
port_specification = optional(string)
proxy_header = optional(string)
request = optional(string)
response = optional(string)
}))
})
default = {
tcp = {
port_specification = "USE_SERVING_PORT"
}
}
validation {
condition = (
(try(var.health_check_config.grpc, null) == null ? 0 : 1) +
(try(var.health_check_config.http, null) == null ? 0 : 1) +
(try(var.health_check_config.http2, null) == null ? 0 : 1) +
(try(var.health_check_config.https, null) == null ? 0 : 1) +
(try(var.health_check_config.tcp, null) == null ? 0 : 1) +
(try(var.health_check_config.ssl, null) == null ? 0 : 1) <= 1
)
error_message = "Only one health check type can be configured at a time."
}
validation {
condition = alltrue([
for k, v in var.health_check_config : contains([
"-", "USE_FIXED_PORT", "USE_NAMED_PORT", "USE_SERVING_PORT"
], coalesce(try(v.port_specification, null), "-"))
])
error_message = "Invalid 'port_specification' value. Supported values are 'USE_FIXED_PORT', 'USE_NAMED_PORT', 'USE_SERVING_PORT'."
}
}
variable "labels" {
description = "Labels set on resources."
type = map(string)
default = {}
}
variable "name" {
description = "Load balancer name."
type = string
}
variable "neg_configs" {
description = "Optional network endpoint groups to create. Can be referenced in backends via key or outputs."
type = map(object({
project_id = optional(string)
gce = optional(object({
zone = string
network = optional(string)
subnetwork = optional(string)
endpoints = optional(map(object({
instance = string
ip_address = string
port = number
})), {})
}))
hybrid = optional(object({
zone = string
network = optional(string)
endpoints = optional(map(object({
ip_address = string
port = number
})), {})
}))
psc = optional(object({
region = string
target_service = string
network = optional(string)
subnetwork = optional(string)
}))
}))
default = {}
nullable = false
validation {
condition = alltrue([
for k, v in var.neg_configs : (
(try(v.gce, null) == null ? 0 : 1) +
(try(v.hybrid, null) == null ? 0 : 1) +
(try(v.psc, null) == null ? 0 : 1) == 1
)
])
error_message = "Only one type of neg can be configured at a time."
}
}
variable "port" {
description = "Forwarding rule port. Cross-region internal proxy load balancers support a single port."
type = number
default = 80
}
variable "project_id" {
description = "Project id."
type = string
}
variable "target_proxy_config" {
description = "Target proxy configuration."
type = object({
description = optional(string, "Terraform managed.")
name = optional(string)
proxy_header = optional(string)
})
default = {}
nullable = false
}
variable "vpc_config" {
description = "VPC-level configuration."
type = object({
network = string
subnetworks = map(string)
})
nullable = false
}

View File

@@ -0,0 +1,35 @@
# 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
#
# https://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.
# Fabric release: v56.1.0
terraform {
required_version = ">= 1.12.2"
required_providers {
google = {
source = "hashicorp/google"
version = ">= 7.33.0, < 8.0.0" # tftest
}
google-beta = {
source = "hashicorp/google-beta"
version = ">= 7.33.0, < 8.0.0" # tftest
}
}
provider_meta "google" {
module_name = "google-pso-tool/cloud-foundation-fabric/modules/net-lb-proxy-int-cross-region:v56.1.0-tf"
}
provider_meta "google-beta" {
module_name = "google-pso-tool/cloud-foundation-fabric/modules/net-lb-proxy-int-cross-region:v56.1.0-tf"
}
}

View File

@@ -0,0 +1,35 @@
# 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
#
# https://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.
# Fabric release: v56.1.0
terraform {
required_version = ">= 1.11.0"
required_providers {
google = {
source = "hashicorp/google"
version = ">= 7.33.0, < 8.0.0" # tftest
}
google-beta = {
source = "hashicorp/google-beta"
version = ">= 7.33.0, < 8.0.0" # tftest
}
}
provider_meta "google" {
module_name = "google-pso-tool/cloud-foundation-fabric/modules/net-lb-proxy-int-cross-region:v56.1.0-tofu"
}
provider_meta "google-beta" {
module_name = "google-pso-tool/cloud-foundation-fabric/modules/net-lb-proxy-int-cross-region:v56.1.0-tofu"
}
}

View File

@@ -0,0 +1,14 @@
name = "proxy-cr-test"
project_id = "my-project"
backend_service_config = {
backends = [{
group = "projects/myprj/zones/europe-west1-a/instanceGroups/my-ig"
}]
}
vpc_config = {
network = "network"
subnetworks = {
europe-west1 = "subnet-ew1"
europe-west4 = "subnet-ew4"
}
}

View File

@@ -0,0 +1,56 @@
name = "proxy-cr-ctx-test"
project_id = "$project_ids:my-project"
context = {
project_ids = {
my-project = "concrete-project-id"
}
networks = {
my-vpc = "projects/concrete-project-id/global/networks/concrete-vpc"
}
subnets = {
my-subnet-1 = "projects/concrete-project-id/regions/europe-west1/subnetworks/concrete-subnet-1"
my-subnet-2 = "projects/concrete-project-id/regions/europe-west4/subnetworks/concrete-subnet-2"
}
addresses = {
my-addr-1 = "10.0.0.10"
}
}
vpc_config = {
network = "$networks:my-vpc"
subnetworks = {
europe-west1 = "$subnets:my-subnet-1"
europe-west4 = "$subnets:my-subnet-2"
}
}
addresses = {
europe-west1 = "$addresses:my-addr-1"
}
neg_configs = {
neg-a = {
gce = {
zone = "europe-west1-b"
network = "$networks:my-vpc"
subnetwork = "$subnets:my-subnet-1"
endpoints = {
vm-a = {
instance = "my-vm-a"
ip_address = "10.0.0.2"
port = 80
}
}
}
}
}
backend_service_config = {
backends = [{
group = "neg-a"
max_connections = {
per_endpoint = 100
}
}]
}

View File

@@ -0,0 +1,174 @@
# 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
#
# https://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_backend_service.default:
affinity_cookie_ttl_sec: null
backend:
- balancing_mode: CONNECTION
capacity_scaler: 1
custom_metrics: []
description: Terraform managed.
max_connections_per_endpoint: 100
preference: ''
traffic_duration: ''
circuit_breakers: []
compression_mode: null
connection_draining_timeout_sec: 300
consistent_hash: []
custom_metrics: []
custom_request_headers: null
custom_response_headers: null
deletion_policy: DELETE
description: Terraform managed.
dynamic_forwarding: []
edge_security_policy: null
enable_cdn: null
external_managed_migration_state: null
external_managed_migration_testing_percentage: null
ip_address_selection_policy: null
load_balancing_scheme: INTERNAL_MANAGED
locality_lb_policies: []
locality_lb_policy: null
max_stream_duration: []
name: proxy-cr-ctx-test
network_pass_through_lb_traffic_policy: []
outlier_detection: []
params: []
project: concrete-project-id
protocol: TCP
security_policy: null
security_settings: []
service_lb_policy: null
session_affinity: NONE
strong_session_affinity_cookie: []
timeouts: null
tls_settings: []
google_compute_global_forwarding_rule.default["europe-west1"]:
allow_psc_global_access: null
deletion_policy: DELETE
description: Terraform managed.
external_managed_backend_bucket_migration_state: null
external_managed_backend_bucket_migration_testing_percentage: null
ip_address: 10.0.0.10
ip_protocol: TCP
ip_version: null
labels: null
load_balancing_scheme: INTERNAL_MANAGED
metadata_filters: []
name: proxy-cr-ctx-test-europe-west1
network: projects/concrete-project-id/global/networks/concrete-vpc
no_automate_dns_zone: null
port_range: '80'
project: concrete-project-id
source_ip_ranges: null
subnetwork: projects/concrete-project-id/regions/europe-west1/subnetworks/concrete-subnet-1
timeouts: null
google_compute_global_forwarding_rule.default["europe-west4"]:
allow_psc_global_access: null
deletion_policy: DELETE
description: Terraform managed.
external_managed_backend_bucket_migration_state: null
external_managed_backend_bucket_migration_testing_percentage: null
ip_protocol: TCP
ip_version: null
labels: null
load_balancing_scheme: INTERNAL_MANAGED
metadata_filters: []
name: proxy-cr-ctx-test-europe-west4
network: projects/concrete-project-id/global/networks/concrete-vpc
no_automate_dns_zone: null
port_range: '80'
project: concrete-project-id
source_ip_ranges: null
subnetwork: projects/concrete-project-id/regions/europe-west4/subnetworks/concrete-subnet-2
timeouts: null
google_compute_health_check.default[0]:
check_interval_sec: 5
deletion_policy: DELETE
description: Terraform managed.
grpc_health_check: []
grpc_tls_health_check: []
healthy_threshold: 2
http2_health_check: []
http_health_check: []
https_health_check: []
name: proxy-cr-ctx-test
project: concrete-project-id
source_regions: null
ssl_health_check: []
tcp_health_check:
- port: null
port_name: null
port_specification: USE_SERVING_PORT
proxy_header: NONE
request: null
response: null
timeout_sec: 5
timeouts: null
unhealthy_threshold: 2
google_compute_network_endpoint.default["neg-a-vm-a"]:
deletion_policy: DELETE
instance: my-vm-a
ip_address: 10.0.0.2
network_endpoint_group: proxy-cr-ctx-test-neg-a
port: 80
project: concrete-project-id
timeouts: null
zone: europe-west1-b
google_compute_network_endpoint_group.default["neg-a"]:
default_port: null
deletion_policy: DELETE
description: null
name: proxy-cr-ctx-test-neg-a
network: projects/concrete-project-id/global/networks/concrete-vpc
network_endpoint_type: GCE_VM_IP_PORT
project: concrete-project-id
subnetwork: projects/concrete-project-id/regions/europe-west1/subnetworks/concrete-subnet-1
timeouts: null
zone: europe-west1-b
google_compute_target_tcp_proxy.default:
deletion_policy: DELETE
description: Terraform managed.
name: proxy-cr-ctx-test
project: concrete-project-id
proxy_header: NONE
timeouts: null
counts:
google_compute_backend_service: 1
google_compute_global_forwarding_rule: 2
google_compute_health_check: 1
google_compute_network_endpoint: 1
google_compute_network_endpoint_group: 1
google_compute_target_tcp_proxy: 1
modules: 0
resources: 7
outputs:
addresses: __missing__
backend_service: __missing__
backend_service_id: __missing__
backend_service_self_link: __missing__
forwarding_rules: __missing__
group_self_links: {}
groups: {}
health_check: __missing__
health_check_id: __missing__
health_check_self_link: __missing__
ids: __missing__
neg_ids: __missing__
psc_neg_ids: {}
target_proxy: __missing__
target_proxy_id: __missing__

View File

@@ -0,0 +1,10 @@
health_check_config = {
tcp = {
port = 5123
port_name = "tcp_port_name"
port_specification = "USE_FIXED_PORT"
proxy_header = "PROXY_V1"
request = "tcp_request"
response = "tcp_response"
}
}

View File

@@ -0,0 +1,151 @@
# 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
#
# https://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_backend_service.default:
affinity_cookie_ttl_sec: null
backend:
- balancing_mode: CONNECTION
capacity_scaler: 1
custom_metrics: []
description: Terraform managed.
group: projects/myprj/zones/europe-west1-a/instanceGroups/my-ig
preference: ''
traffic_duration: ''
circuit_breakers: []
compression_mode: null
connection_draining_timeout_sec: 300
consistent_hash: []
custom_metrics: []
custom_request_headers: null
custom_response_headers: null
deletion_policy: DELETE
description: Terraform managed.
dynamic_forwarding: []
edge_security_policy: null
enable_cdn: null
external_managed_migration_state: null
external_managed_migration_testing_percentage: null
ip_address_selection_policy: null
load_balancing_scheme: INTERNAL_MANAGED
locality_lb_policies: []
locality_lb_policy: null
max_stream_duration: []
name: proxy-cr-test
network_pass_through_lb_traffic_policy: []
outlier_detection: []
params: []
project: my-project
protocol: TCP
security_policy: null
security_settings: []
service_lb_policy: null
session_affinity: NONE
strong_session_affinity_cookie: []
timeouts: null
tls_settings: []
google_compute_global_forwarding_rule.default["europe-west1"]:
allow_psc_global_access: null
deletion_policy: DELETE
description: Terraform managed.
external_managed_backend_bucket_migration_state: null
external_managed_backend_bucket_migration_testing_percentage: null
ip_protocol: TCP
ip_version: null
labels: null
load_balancing_scheme: INTERNAL_MANAGED
metadata_filters: []
name: proxy-cr-test-europe-west1
network: network
no_automate_dns_zone: null
port_range: '80'
project: my-project
source_ip_ranges: null
subnetwork: subnet-ew1
timeouts: null
google_compute_global_forwarding_rule.default["europe-west4"]:
allow_psc_global_access: null
deletion_policy: DELETE
description: Terraform managed.
external_managed_backend_bucket_migration_state: null
external_managed_backend_bucket_migration_testing_percentage: null
ip_protocol: TCP
ip_version: null
labels: null
load_balancing_scheme: INTERNAL_MANAGED
metadata_filters: []
name: proxy-cr-test-europe-west4
network: network
no_automate_dns_zone: null
port_range: '80'
project: my-project
source_ip_ranges: null
subnetwork: subnet-ew4
timeouts: null
google_compute_health_check.default[0]:
check_interval_sec: 5
deletion_policy: DELETE
description: Terraform managed.
grpc_health_check: []
grpc_tls_health_check: []
healthy_threshold: 2
http2_health_check: []
http_health_check: []
https_health_check: []
name: proxy-cr-test
project: my-project
source_regions: null
ssl_health_check: []
tcp_health_check:
- port: 5123
port_name: tcp_port_name
port_specification: USE_FIXED_PORT
proxy_header: PROXY_V1
request: tcp_request
response: tcp_response
timeout_sec: 5
timeouts: null
unhealthy_threshold: 2
google_compute_target_tcp_proxy.default:
deletion_policy: DELETE
description: Terraform managed.
name: proxy-cr-test
project: my-project
proxy_header: NONE
timeouts: null
counts:
google_compute_backend_service: 1
google_compute_global_forwarding_rule: 2
google_compute_health_check: 1
google_compute_target_tcp_proxy: 1
modules: 0
resources: 5
outputs:
addresses: __missing__
backend_service: __missing__
backend_service_id: __missing__
backend_service_self_link: __missing__
forwarding_rules: __missing__
group_self_links: {}
groups: {}
health_check: __missing__
health_check_id: __missing__
health_check_self_link: __missing__
ids: __missing__
neg_ids: {}
psc_neg_ids: {}
target_proxy: __missing__
target_proxy_id: __missing__

View File

@@ -0,0 +1,26 @@
name = "proxy-cr-ig-test"
project_id = "my-project"
vpc_config = {
network = "network"
subnetworks = {
europe-west1 = "subnet-ew1"
europe-west4 = "subnet-ew4"
}
}
group_configs = {
ig-a = {
zone = "europe-west1-b"
instances = [
"projects/my-project/zones/europe-west1-b/instances/vm-a"
]
}
}
backend_service_config = {
backends = [{
group = "ig-a"
balancing_mode = "UTILIZATION"
}]
}

View File

@@ -0,0 +1,161 @@
# 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
#
# https://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_backend_service.default:
affinity_cookie_ttl_sec: null
backend:
- balancing_mode: UTILIZATION
capacity_scaler: 1
custom_metrics: []
description: Terraform managed.
preference: ''
traffic_duration: ''
circuit_breakers: []
compression_mode: null
connection_draining_timeout_sec: 300
consistent_hash: []
custom_metrics: []
custom_request_headers: null
custom_response_headers: null
deletion_policy: DELETE
description: Terraform managed.
dynamic_forwarding: []
edge_security_policy: null
enable_cdn: null
external_managed_migration_state: null
external_managed_migration_testing_percentage: null
ip_address_selection_policy: null
load_balancing_scheme: INTERNAL_MANAGED
locality_lb_policies: []
locality_lb_policy: null
max_stream_duration: []
name: proxy-cr-ig-test
network_pass_through_lb_traffic_policy: []
outlier_detection: []
params: []
project: my-project
protocol: TCP
security_policy: null
security_settings: []
service_lb_policy: null
session_affinity: NONE
strong_session_affinity_cookie: []
timeouts: null
tls_settings: []
google_compute_global_forwarding_rule.default["europe-west1"]:
allow_psc_global_access: null
deletion_policy: DELETE
description: Terraform managed.
external_managed_backend_bucket_migration_state: null
external_managed_backend_bucket_migration_testing_percentage: null
ip_protocol: TCP
ip_version: null
labels: null
load_balancing_scheme: INTERNAL_MANAGED
metadata_filters: []
name: proxy-cr-ig-test-europe-west1
network: network
no_automate_dns_zone: null
port_range: '80'
project: my-project
source_ip_ranges: null
subnetwork: subnet-ew1
timeouts: null
google_compute_global_forwarding_rule.default["europe-west4"]:
allow_psc_global_access: null
deletion_policy: DELETE
description: Terraform managed.
external_managed_backend_bucket_migration_state: null
external_managed_backend_bucket_migration_testing_percentage: null
ip_protocol: TCP
ip_version: null
labels: null
load_balancing_scheme: INTERNAL_MANAGED
metadata_filters: []
name: proxy-cr-ig-test-europe-west4
network: network
no_automate_dns_zone: null
port_range: '80'
project: my-project
source_ip_ranges: null
subnetwork: subnet-ew4
timeouts: null
google_compute_health_check.default[0]:
check_interval_sec: 5
deletion_policy: DELETE
description: Terraform managed.
grpc_health_check: []
grpc_tls_health_check: []
healthy_threshold: 2
http2_health_check: []
http_health_check: []
https_health_check: []
name: proxy-cr-ig-test
project: my-project
source_regions: null
ssl_health_check: []
tcp_health_check:
- port: null
port_name: null
port_specification: USE_SERVING_PORT
proxy_header: NONE
request: null
response: null
timeout_sec: 5
timeouts: null
unhealthy_threshold: 2
google_compute_instance_group.default["ig-a"]:
deletion_policy: DELETE
description: Terraform managed.
instances:
- projects/my-project/zones/europe-west1-b/instances/vm-a
name: proxy-cr-ig-test-ig-a
named_port: []
project: my-project
timeouts: null
zone: europe-west1-b
google_compute_target_tcp_proxy.default:
deletion_policy: DELETE
description: Terraform managed.
name: proxy-cr-ig-test
project: my-project
proxy_header: NONE
timeouts: null
counts:
google_compute_backend_service: 1
google_compute_global_forwarding_rule: 2
google_compute_health_check: 1
google_compute_instance_group: 1
google_compute_target_tcp_proxy: 1
modules: 0
resources: 6
outputs:
addresses: __missing__
backend_service: __missing__
backend_service_id: __missing__
backend_service_self_link: __missing__
forwarding_rules: __missing__
group_self_links: __missing__
groups: __missing__
health_check: __missing__
health_check_id: __missing__
health_check_self_link: __missing__
ids: __missing__
neg_ids: {}
psc_neg_ids: {}
target_proxy: __missing__
target_proxy_id: __missing__

View File

@@ -0,0 +1,21 @@
# 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.
module: modules/net-lb-proxy-int-cross-region
common_tfvars:
- common.tfvars
tests:
health-checks-tcp:
context:
instance-groups: