Add support for attachment-level BGP sessions and candidate_*_router_ip_address (#3827)

* Support VLAN-attachment-level bgp advertisements
* Support for candidate_cloud_router_ip_address and candidate_customer_router_ip_address
This commit is contained in:
Simone Ruffilli
2026-04-01 14:21:18 +02:00
committed by GitHub
parent 7b43c3e8cf
commit a6b98bac28
18 changed files with 995 additions and 83 deletions

View File

@@ -80,6 +80,14 @@ module "example-va" {
router_config = {
create = false
name = google_compute_router.interconnect-router.name
}
bgp_peer = {
custom_learned_ip_ranges = {
route_priority = 100
ip_ranges = {
"10.0.0.0/24" = "test advertisement"
}
}
bfd = {
min_receive_interval = 1000
min_transmit_interval = 1000
@@ -92,14 +100,15 @@ module "example-va" {
}
}
dedicated_interconnect_config = {
bandwidth = "BPS_10G"
bgp_range = "169.254.0.0/29"
interconnect = "https://www.googleapis.com/compute/v1/projects/my-project/global/interconnects/interconnect-a"
vlan_tag = 12345
bandwidth = "BPS_10G"
bgp_range = "169.254.0.0/29"
candidate_cloud_router_ip_address = "169.254.0.1/29"
candidate_customer_router_ip_address = "169.254.0.2/29"
interconnect = "https://www.googleapis.com/compute/v1/projects/my-project/global/interconnects/interconnect-a"
vlan_tag = 12345
}
}
# tftest modules=1 resources=5
# tftest modules=1 resources=5 inventory=bgp-peer.yaml
```
If you don't specify the MD5 key, the module will generate a random 12 characters key for you.
@@ -646,20 +655,21 @@ module "example-va-b" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [description](variables.tf#L52) | VLAN attachment description. | <code>string</code> | ✓ | |
| [name](variables.tf#L69) | The common resources name, used after resource type prefix and suffix. | <code>string</code> | ✓ | |
| [network](variables.tf#L74) | The VPC name to which resources are associated to. | <code>string</code> | ✓ | |
| [peer_asn](variables.tf#L91) | The on-premises underlay router ASN. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L96) | The project id where resources are created. | <code>string</code> | ✓ | |
| [region](variables.tf#L101) | The region where resources are created. | <code>string</code> | ✓ | |
| [router_config](variables.tf#L106) | 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. | <code title="object&#40;&#123;&#10; create &#61; optional&#40;bool, true&#41;&#10; asn &#61; optional&#40;number, 65001&#41;&#10; bfd &#61; optional&#40;object&#40;&#123;&#10; min_receive_interval &#61; optional&#40;number&#41;&#10; min_transmit_interval &#61; optional&#40;number&#41;&#10; multiplier &#61; optional&#40;number&#41;&#10; session_initialization_mode &#61; optional&#40;string, &#34;ACTIVE&#34;&#41;&#10; &#125;&#41;&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; md5_authentication_key &#61; optional&#40;object&#40;&#123;&#10; name &#61; string&#10; key &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; keepalive &#61; optional&#40;number&#41;&#10; name &#61; optional&#40;string, &#34;router&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | |
| [description](variables.tf#L79) | VLAN attachment description. | <code>string</code> | ✓ | |
| [name](variables.tf#L96) | The common resources name, used after resource type prefix and suffix. | <code>string</code> | ✓ | |
| [network](variables.tf#L101) | The VPC name to which resources are associated to. | <code>string</code> | ✓ | |
| [peer_asn](variables.tf#L118) | The on-premises underlay router ASN. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L123) | The project id where resources are created. | <code>string</code> | ✓ | |
| [region](variables.tf#L128) | The region where resources are created. | <code>string</code> | ✓ | |
| [router_config](variables.tf#L133) | 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. | <code title="object&#40;&#123;&#10; create &#61; optional&#40;bool, true&#41;&#10; asn &#61; optional&#40;number, 65001&#41;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; md5_authentication_key &#61; optional&#40;object&#40;&#123;&#10; name &#61; string&#10; key &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; keepalive &#61; optional&#40;number&#41;&#10; name &#61; optional&#40;string, &#34;router&#34;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | |
| [admin_enabled](variables.tf#L17) | Whether the VLAN attachment is enabled. | <code>bool</code> | | <code>true</code> |
| [context](variables.tf#L23) | Context-specific interpolations. | <code title="object&#40;&#123;&#10; locations &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; networks &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; project_ids &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; routers &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [dedicated_interconnect_config](variables.tf#L35) | Dedicated interconnect configuration. | <code title="object&#40;&#123;&#10; bandwidth &#61; optional&#40;string, &#34;BPS_10G&#34;&#41;&#10; bgp_range &#61; optional&#40;string&#41;&#10; bgp_priority &#61; optional&#40;number&#41;&#10; interconnect &#61; string&#10; vlan_tag &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [ipsec_gateway_ip_ranges](variables.tf#L57) | IPSec Gateway IP Ranges. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [mtu](variables.tf#L63) | The MTU associated to the VLAN attachment (1440 / 1500). | <code>number</code> | | <code>1500</code> |
| [partner_interconnect_config](variables.tf#L79) | Partner interconnect configuration. | <code title="object&#40;&#123;&#10; edge_availability_domain &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [vpn_gateways_ip_range](variables.tf#L131) | The IP range (cidr notation) to be used for the GCP VPN gateways. If null IPSec over Interconnect is not enabled. | <code>string</code> | | <code>null</code> |
| [bgp_peer](variables.tf#L23) | BGP peer configuration for the VLAN attachment. | <code title="object&#40;&#123;&#10; custom_advertise &#61; optional&#40;object&#40;&#123;&#10; all_subnets &#61; bool&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; custom_learned_ip_ranges &#61; optional&#40;object&#40;&#123;&#10; route_priority &#61; optional&#40;number, 1000&#41;&#10; ip_ranges &#61; map&#40;string&#41;&#10; &#125;&#41;&#41;&#10; bfd &#61; optional&#40;object&#40;&#123;&#10; min_receive_interval &#61; optional&#40;number&#41;&#10; min_transmit_interval &#61; optional&#40;number&#41;&#10; multiplier &#61; optional&#40;number&#41;&#10; session_initialization_mode &#61; optional&#40;string, &#34;ACTIVE&#34;&#41;&#10; &#125;&#41;&#41;&#10; md5_authentication_key &#61; optional&#40;object&#40;&#123;&#10; name &#61; string&#10; key &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [context](variables.tf#L48) | Context-specific interpolations. | <code title="object&#40;&#123;&#10; locations &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; networks &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; project_ids &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; routers &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [dedicated_interconnect_config](variables.tf#L60) | Dedicated interconnect configuration. | <code title="object&#40;&#123;&#10; bandwidth &#61; optional&#40;string, &#34;BPS_10G&#34;&#41;&#10; bgp_range &#61; optional&#40;string&#41;&#10; bgp_priority &#61; optional&#40;number&#41;&#10; candidate_cloud_router_ip_address &#61; optional&#40;string&#41;&#10; candidate_customer_router_ip_address &#61; optional&#40;string&#41;&#10; interconnect &#61; string&#10; vlan_tag &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [ipsec_gateway_ip_ranges](variables.tf#L84) | IPSec Gateway IP Ranges. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [mtu](variables.tf#L90) | The MTU associated to the VLAN attachment (1440 / 1500). | <code>number</code> | | <code>1500</code> |
| [partner_interconnect_config](variables.tf#L106) | Partner interconnect configuration. | <code title="object&#40;&#123;&#10; edge_availability_domain &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [vpn_gateways_ip_range](variables.tf#L152) | The IP range (cidr notation) to be used for the GCP VPN gateways. If null IPSec over Interconnect is not enabled. | <code>string</code> | | <code>null</code> |
## Outputs

View File

@@ -47,21 +47,23 @@ resource "google_compute_address" "default" {
}
resource "google_compute_interconnect_attachment" "default" {
project = local.project_id
region = local.region
router = local.router
name = var.name
description = var.description
interconnect = try(var.dedicated_interconnect_config.interconnect, null)
bandwidth = try(var.dedicated_interconnect_config.bandwidth, null)
mtu = local.ipsec_enabled ? null : var.mtu
candidate_subnets = try(var.dedicated_interconnect_config.bgp_range, null) != null ? [var.dedicated_interconnect_config.bgp_range] : null
vlan_tag8021q = try(var.dedicated_interconnect_config.vlan_tag, null)
admin_enabled = var.admin_enabled
encryption = local.ipsec_enabled ? "IPSEC" : null
type = var.dedicated_interconnect_config == null ? "PARTNER" : "DEDICATED"
edge_availability_domain = try(var.partner_interconnect_config.edge_availability_domain, null)
ipsec_internal_addresses = local.ipsec_enabled ? [google_compute_address.default[0].self_link] : null
project = local.project_id
region = local.region
router = local.router
name = var.name
description = var.description
interconnect = try(var.dedicated_interconnect_config.interconnect, null)
bandwidth = try(var.dedicated_interconnect_config.bandwidth, null)
mtu = local.ipsec_enabled ? null : var.mtu
candidate_subnets = try(var.dedicated_interconnect_config.bgp_range, null) != null ? [var.dedicated_interconnect_config.bgp_range] : null
vlan_tag8021q = try(var.dedicated_interconnect_config.vlan_tag, null)
candidate_cloud_router_ip_address = try(var.dedicated_interconnect_config.candidate_cloud_router_ip_address, null)
candidate_customer_router_ip_address = try(var.dedicated_interconnect_config.candidate_customer_router_ip_address, null)
admin_enabled = var.admin_enabled
encryption = local.ipsec_enabled ? "IPSEC" : null
type = var.dedicated_interconnect_config == null ? "PARTNER" : "DEDICATED"
edge_availability_domain = try(var.partner_interconnect_config.edge_availability_domain, null)
ipsec_internal_addresses = local.ipsec_enabled ? [google_compute_address.default[0].self_link] : null
}
resource "google_compute_router" "encrypted" {
@@ -134,18 +136,42 @@ resource "google_compute_router_peer" "default" {
peer_asn = var.peer_asn
interface = google_compute_router_interface.default[0].name
advertised_route_priority = var.dedicated_interconnect_config.bgp_priority
advertise_mode = "CUSTOM"
advertise_mode = (
var.bgp_peer != null
? (try(var.bgp_peer.custom_advertise, null) != null ? "CUSTOM" : "DEFAULT")
: "CUSTOM"
)
advertised_groups = (
try(var.bgp_peer.custom_advertise.all_subnets, false)
? ["ALL_SUBNETS"]
: null
)
dynamic "advertised_ip_ranges" {
for_each = var.ipsec_gateway_ip_ranges
for_each = var.bgp_peer != null ? try(var.bgp_peer.custom_advertise.ip_ranges, {}) : var.ipsec_gateway_ip_ranges
iterator = range
content {
description = advertised_ip_ranges.key
range = advertised_ip_ranges.value
range = range.value
description = range.key
}
}
dynamic "custom_learned_ip_ranges" {
for_each = try(var.bgp_peer.custom_learned_ip_ranges.ip_ranges, {})
iterator = range
content {
range = range.key
}
}
custom_learned_route_priority = try(
var.bgp_peer.custom_learned_ip_ranges.route_priority,
null
)
dynamic "bfd" {
for_each = var.router_config.bfd != null ? toset([var.router_config.bfd]) : []
for_each = try(var.bgp_peer.bfd, null) != null ? toset([var.bgp_peer.bfd]) : []
content {
session_initialization_mode = bfd.value.session_initialization_mode
min_receive_interval = bfd.value.min_receive_interval
@@ -155,7 +181,11 @@ resource "google_compute_router_peer" "default" {
}
dynamic "md5_authentication_key" {
for_each = var.router_config.md5_authentication_key != null ? [var.router_config.md5_authentication_key] : []
for_each = (
try(var.bgp_peer.md5_authentication_key, null) != null
? [var.bgp_peer.md5_authentication_key]
: var.router_config.md5_authentication_key != null ? [var.router_config.md5_authentication_key] : []
)
content {
name = md5_authentication_key.value.name
key = coalesce(md5_authentication_key.value.key, local.secret)

View File

@@ -20,6 +20,31 @@ variable "admin_enabled" {
default = true
}
variable "bgp_peer" {
description = "BGP peer configuration for the VLAN attachment."
type = object({
custom_advertise = optional(object({
all_subnets = bool
ip_ranges = map(string)
}))
custom_learned_ip_ranges = optional(object({
route_priority = optional(number, 1000)
ip_ranges = map(string)
}))
bfd = optional(object({
min_receive_interval = optional(number)
min_transmit_interval = optional(number)
multiplier = optional(number)
session_initialization_mode = optional(string, "ACTIVE")
}))
md5_authentication_key = optional(object({
name = string
key = optional(string)
}))
})
default = null
}
variable "context" {
description = "Context-specific interpolations."
type = object({
@@ -36,11 +61,13 @@ variable "dedicated_interconnect_config" {
description = "Dedicated interconnect configuration."
type = object({
# Possible values @ https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_interconnect_attachment#bandwidth
bandwidth = optional(string, "BPS_10G")
bgp_range = optional(string)
bgp_priority = optional(number)
interconnect = string
vlan_tag = string
bandwidth = optional(string, "BPS_10G")
bgp_range = optional(string)
bgp_priority = optional(number)
candidate_cloud_router_ip_address = optional(string)
candidate_customer_router_ip_address = optional(string)
interconnect = string
vlan_tag = string
})
validation {
condition = var.dedicated_interconnect_config == null ? true : contains(["BPS_50M", "BPS_100M", "BPS_200M", "BPS_300M", "BPS_400M", "BPS_500M", "BPS_1G", "BPS_2G", "BPS_5G", "BPS_10G", "BPS_20G", "BPS_50G", "BPS_100G", "BPS_400G"], var.dedicated_interconnect_config.bandwidth)
@@ -108,12 +135,6 @@ variable "router_config" {
type = object({
create = optional(bool, true)
asn = optional(number, 65001)
bfd = optional(object({
min_receive_interval = optional(number)
min_transmit_interval = optional(number)
multiplier = optional(number)
session_initialization_mode = optional(string, "ACTIVE")
}))
custom_advertise = optional(object({
all_subnets = bool
ip_ranges = map(string)