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: