diff --git a/modules/net-vpn-ha/README.md b/modules/net-vpn-ha/README.md
index b34bac9aa..b9ffdad5d 100644
--- a/modules/net-vpn-ha/README.md
+++ b/modules/net-vpn-ha/README.md
@@ -223,15 +223,16 @@ You can optionally avoid to specify MD5 keys and the module will automatically g
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [name](variables.tf#L17) | VPN Gateway name (if an existing VPN Gateway is not used), and prefix used for dependent resources. | string | ✓ | |
-| [network](variables.tf#L22) | VPC used for the gateway and routes. | string | ✓ | |
-| [project_id](variables.tf#L48) | Project where resources will be created. | string | ✓ | |
-| [region](variables.tf#L53) | Region used for resources. | string | ✓ | |
-| [router_config](variables.tf#L58) | Cloud Router configuration for the VPN. If you want to reuse an existing router, set create to false and use name to specify the desired router. | object({…}) | ✓ | |
-| [peer_gateways](variables.tf#L27) | Configuration of the (external or GCP) peer gateway. | map(object({…})) | | {} |
-| [tunnels](variables.tf#L74) | VPN tunnel configurations. | map(object({…})) | | {} |
-| [vpn_gateway](variables.tf#L111) | HA VPN Gateway Self Link for using an existing HA VPN Gateway. Ignored if `vpn_gateway_create` is set to `true`. | string | | null |
-| [vpn_gateway_create](variables.tf#L117) | Create HA VPN Gateway. Set to null to avoid creation. | object({…}) | | {} |
+| [name](variables.tf#L31) | VPN Gateway name (if an existing VPN Gateway is not used), and prefix used for dependent resources. | string | ✓ | |
+| [network](variables.tf#L36) | VPC used for the gateway and routes. | string | ✓ | |
+| [project_id](variables.tf#L62) | Project where resources will be created. | string | ✓ | |
+| [region](variables.tf#L67) | Region used for resources. | string | ✓ | |
+| [router_config](variables.tf#L72) | Cloud Router configuration for the VPN. If you want to reuse an existing router, set create to false and use name to specify the desired router. | object({…}) | ✓ | |
+| [context](variables.tf#L17) | Context-specific interpolations. | object({…}) | | {} |
+| [peer_gateways](variables.tf#L41) | Configuration of the (external or GCP) peer gateway. | map(object({…})) | | {} |
+| [tunnels](variables.tf#L88) | VPN tunnel configurations. | map(object({…})) | | {} |
+| [vpn_gateway](variables.tf#L125) | HA VPN Gateway Self Link for using an existing HA VPN Gateway. Ignored if `vpn_gateway_create` is set to `true`. | string | | null |
+| [vpn_gateway_create](variables.tf#L131) | Create HA VPN Gateway. Set to null to avoid creation. | object({…}) | | {} |
## Outputs
diff --git a/modules/net-vpn-ha/main.tf b/modules/net-vpn-ha/main.tf
index c53e18e52..cd5ade606 100644
--- a/modules/net-vpn-ha/main.tf
+++ b/modules/net-vpn-ha/main.tf
@@ -16,25 +16,39 @@
*/
locals {
+ ctx = {
+ for k, v in var.context : k => {
+ for kk, vv in v : "${local.ctx_p}${k}:${kk}" => vv
+ }
+ }
+ ctx_p = "$"
md5_keys = {
for k, v in random_id.md5_keys
: k => v.b64_url
}
+ network = lookup(local.ctx.networks, var.network, var.network)
peer_gateways_external = {
for k, v in var.peer_gateways : k => v.external if v.external != null
}
peer_gateways_gcp = {
- for k, v in var.peer_gateways : k => v.gcp if v.gcp != null
+ for k, v in var.peer_gateways :
+ k => lookup(local.ctx.vpn_gateways, v.gcp, v.gcp) if v.gcp != null
}
+ project_id = lookup(
+ local.ctx.project_ids, var.project_id, var.project_id
+ )
+ region = lookup(
+ local.ctx.locations, var.region, var.region
+ )
router = (
var.router_config.create
? try(google_compute_router.router[0].name, null)
- : var.router_config.name
+ : lookup(local.ctx.routers, var.router_config.name, var.router_config.name)
)
vpn_gateway = (
var.vpn_gateway_create != null
? try(google_compute_ha_vpn_gateway.ha_gateway[0].self_link, null)
- : var.vpn_gateway
+ : lookup(local.ctx.vpn_gateways, var.vpn_gateway, var.vpn_gateway)
)
secret = random_id.secret.b64_url
}
@@ -43,23 +57,23 @@ resource "google_compute_ha_vpn_gateway" "ha_gateway" {
count = var.vpn_gateway_create != null ? 1 : 0
name = var.name
description = var.vpn_gateway_create.description
- project = var.project_id
- region = var.region
- network = var.network
+ project = local.project_id
+ region = local.region
+ network = local.network
stack_type = var.vpn_gateway_create.ipv6 ? "IPV4_IPV6" : "IPV4_ONLY"
}
resource "google_compute_external_vpn_gateway" "external_gateway" {
for_each = local.peer_gateways_external
name = each.value.name != null ? each.value.name : "${var.name}-${each.key}"
- project = var.project_id
+ project = local.project_id
redundancy_type = each.value.redundancy_type
description = each.value.description
dynamic "interface" {
for_each = each.value.interfaces
content {
id = interface.key
- ip_address = interface.value
+ ip_address = lookup(local.ctx.addresses, interface.value, interface.value)
}
}
}
@@ -67,9 +81,9 @@ resource "google_compute_external_vpn_gateway" "external_gateway" {
resource "google_compute_router" "router" {
count = var.router_config.create ? 1 : 0
name = coalesce(var.router_config.name, "vpn-${var.name}")
- project = var.project_id
- region = var.region
- network = var.network
+ project = local.project_id
+ region = local.region
+ network = local.network
bgp {
advertise_mode = (
var.router_config.custom_advertise != null
@@ -96,8 +110,8 @@ resource "google_compute_router" "router" {
resource "google_compute_router_peer" "bgp_peer" {
for_each = var.tunnels
- region = var.region
- project = var.project_id
+ region = local.region
+ project = local.project_id
name = each.value.bgp_peer.name != null ? each.value.bgp_peer.name : "${var.name}-${each.key}"
router = coalesce(each.value.router, local.router)
peer_ip_address = each.value.bgp_peer.address
@@ -132,10 +146,14 @@ resource "google_compute_router_peer" "bgp_peer" {
resource "google_compute_router_interface" "router_interface" {
for_each = var.tunnels
- project = var.project_id
- region = var.region
- name = each.value.peer_router_interface_name != null ? each.value.peer_router_interface_name : "${var.name}-${each.key}"
- router = local.router
+ project = local.project_id
+ region = local.region
+ name = (
+ each.value.peer_router_interface_name != null
+ ? each.value.peer_router_interface_name :
+ "${var.name}-${each.key}"
+ )
+ router = local.router
# FIXME: can bgp_session_range be null?
ip_range = each.value.bgp_session_range == "" ? null : each.value.bgp_session_range
vpn_tunnel = google_compute_vpn_tunnel.tunnels[each.key].name
@@ -143,8 +161,8 @@ resource "google_compute_router_interface" "router_interface" {
resource "google_compute_vpn_tunnel" "tunnels" {
for_each = var.tunnels
- project = var.project_id
- region = var.region
+ project = local.project_id
+ region = local.region
name = each.value.name != null ? each.value.name : "${var.name}-${each.key}"
router = local.router
peer_external_gateway = try(
diff --git a/modules/net-vpn-ha/variables.tf b/modules/net-vpn-ha/variables.tf
index d20b48daa..18875342a 100644
--- a/modules/net-vpn-ha/variables.tf
+++ b/modules/net-vpn-ha/variables.tf
@@ -14,6 +14,20 @@
* limitations under the License.
*/
+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), {})
+ routers = optional(map(string), {})
+ vpn_gateways = optional(map(string), {})
+ })
+ default = {}
+ nullable = false
+}
+
variable "name" {
description = "VPN Gateway name (if an existing VPN Gateway is not used), and prefix used for dependent resources."
type = string
diff --git a/tests/modules/net_vpn_ha/context.tfvars b/tests/modules/net_vpn_ha/context.tfvars
new file mode 100644
index 000000000..43bcf2b14
--- /dev/null
+++ b/tests/modules/net_vpn_ha/context.tfvars
@@ -0,0 +1,57 @@
+context = {
+ addresses = {
+ test = "8.8.8.8"
+ }
+ locations = {
+ ew8 = "europe-west8"
+ }
+ networks = {
+ test = "projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0"
+ }
+ project_ids = {
+ test = "foo-test-0"
+ }
+ routers = {
+ test = "vpn-to-onprem-ew8"
+ }
+ vpn_gateways = {
+ local = "projects/foo-prod-net-landing-0/regions/europe-west8/vpnGateways/vpn-to-onprem-ew8"
+ remote = "projects/foo-prod-net-landing-1/regions/europe-west8/vpnGateways/vpn-to-onprem-ew8"
+ }
+}
+project_id = "$project_ids:test"
+network = "$networks:test"
+region = "$locations:ew8"
+name = "test"
+router_config = {
+ asn = 64513
+ create = false
+ name = "$routers:test"
+}
+peer_gateways = {
+ default = {
+ gcp = "$vpn_gateways:remote"
+ }
+}
+tunnels = {
+ remote-0 = {
+ bgp_peer = {
+ address = "169.254.1.2"
+ asn = 64514
+ }
+ bgp_session_range = "169.254.1.1/30"
+ shared_secret = "foo"
+ vpn_gateway_interface = 0
+ }
+ remote-1 = {
+ bgp_peer = {
+ address = "169.254.2.2"
+ asn = 64514
+ }
+ bgp_session_range = "169.254.2.1/30"
+ shared_secret = "foo"
+ vpn_gateway_interface = 1
+ }
+}
+vpn_gateway = "$vpn_gateways:local"
+vpn_gateway_create = null
diff --git a/tests/modules/net_vpn_ha/context.yaml b/tests/modules/net_vpn_ha/context.yaml
new file mode 100644
index 000000000..ea377f415
--- /dev/null
+++ b/tests/modules/net_vpn_ha/context.yaml
@@ -0,0 +1,143 @@
+# 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.
+
+values:
+ google_compute_router_interface.router_interface["remote-0"]:
+ interconnect_attachment: null
+ ip_range: 169.254.1.1/30
+ name: test-remote-0
+ private_ip_address: null
+ project: foo-test-0
+ region: europe-west8
+ router: vpn-to-onprem-ew8
+ subnetwork: null
+ timeouts: null
+ vpn_tunnel: test-remote-0
+ google_compute_router_interface.router_interface["remote-1"]:
+ interconnect_attachment: null
+ ip_range: 169.254.2.1/30
+ name: test-remote-1
+ private_ip_address: null
+ project: foo-test-0
+ region: europe-west8
+ router: vpn-to-onprem-ew8
+ subnetwork: null
+ timeouts: null
+ vpn_tunnel: test-remote-1
+ google_compute_router_peer.bgp_peer["remote-0"]:
+ advertise_mode: DEFAULT
+ advertised_groups: []
+ advertised_ip_ranges: []
+ advertised_route_priority: 1000
+ custom_learned_ip_ranges: []
+ custom_learned_route_priority: null
+ enable: true
+ enable_ipv6: false
+ export_policies: null
+ import_policies: null
+ interface: test-remote-0
+ md5_authentication_key: []
+ name: test-remote-0
+ peer_asn: 64514
+ peer_ip_address: 169.254.1.2
+ project: foo-test-0
+ region: europe-west8
+ router: vpn-to-onprem-ew8
+ router_appliance_instance: null
+ timeouts: null
+ zero_advertised_route_priority: null
+ zero_custom_learned_route_priority: false
+ google_compute_router_peer.bgp_peer["remote-1"]:
+ advertise_mode: DEFAULT
+ advertised_groups: []
+ advertised_ip_ranges: []
+ advertised_route_priority: 1000
+ custom_learned_ip_ranges: []
+ custom_learned_route_priority: null
+ enable: true
+ enable_ipv6: false
+ export_policies: null
+ import_policies: null
+ interface: test-remote-1
+ md5_authentication_key: []
+ name: test-remote-1
+ peer_asn: 64514
+ peer_ip_address: 169.254.2.2
+ project: foo-test-0
+ region: europe-west8
+ router: vpn-to-onprem-ew8
+ router_appliance_instance: null
+ timeouts: null
+ zero_advertised_route_priority: null
+ zero_custom_learned_route_priority: false
+ google_compute_vpn_tunnel.tunnels["remote-0"]:
+ description: null
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ ike_version: 2
+ labels: null
+ name: test-remote-0
+ peer_external_gateway: null
+ peer_external_gateway_interface: null
+ peer_gcp_gateway: projects/foo-prod-net-landing-1/regions/europe-west8/vpnGateways/vpn-to-onprem-ew8
+ project: foo-test-0
+ region: europe-west8
+ router: vpn-to-onprem-ew8
+ shared_secret: foo
+ target_vpn_gateway: null
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ vpn_gateway: projects/foo-prod-net-landing-0/regions/europe-west8/vpnGateways/vpn-to-onprem-ew8
+ vpn_gateway_interface: 0
+ google_compute_vpn_tunnel.tunnels["remote-1"]:
+ description: null
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ ike_version: 2
+ labels: null
+ name: test-remote-1
+ peer_external_gateway: null
+ peer_external_gateway_interface: null
+ peer_gcp_gateway: projects/foo-prod-net-landing-1/regions/europe-west8/vpnGateways/vpn-to-onprem-ew8
+ project: foo-test-0
+ region: europe-west8
+ router: vpn-to-onprem-ew8
+ shared_secret: foo
+ target_vpn_gateway: null
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ vpn_gateway: projects/foo-prod-net-landing-0/regions/europe-west8/vpnGateways/vpn-to-onprem-ew8
+ vpn_gateway_interface: 1
+ random_id.md5_keys["remote-0"]:
+ byte_length: 12
+ keepers: null
+ prefix: null
+ random_id.md5_keys["remote-1"]:
+ byte_length: 12
+ keepers: null
+ prefix: null
+ random_id.secret:
+ byte_length: 8
+ keepers: null
+ prefix: null
+
+counts:
+ google_compute_router_interface: 2
+ google_compute_router_peer: 2
+ google_compute_vpn_tunnel: 2
+ modules: 0
+ random_id: 3
+ resources: 9
diff --git a/tests/modules/net_vpn_ha/tftest.yaml b/tests/modules/net_vpn_ha/tftest.yaml
new file mode 100644
index 000000000..1849499c2
--- /dev/null
+++ b/tests/modules/net_vpn_ha/tftest.yaml
@@ -0,0 +1,18 @@
+# Copyright 2023 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-vpn-ha
+
+tests:
+ context: