Quick take — A reusable hashicorp/google ~> 5.0 module for google_compute_interconnect_attachment: builds the VLAN attachment (DEDICATED or PARTNER), a regional Cloud Router with your ASN, and the BGP interface + peer that turn a physical Interconnect into routed hybrid connectivity. New here? Jump to the Quickstart below to deploy it in minutes; read on for how it works and when to reach for it.
Quickstart (copy-paste)
Minimal, runnable configuration — drop this in a .tf file and fill in the "..." placeholders (each required input is commented):
provider "google" {
project = "my-project"
region = "us-central1"
}
module "interconnect" {
source = "git::https://dev.azure.com/teknohut/kloudvin/_git/terraform-modules//terraform-module-gcp-interconnect?ref=v1.0.0"
name = "..." # Base name for the attachment, router, and BGP peer.
project_id = "..." # GCP project that owns the Cloud Router + VLAN attachment.
region = "..." # Region of the Cloud Router (must match the circuit's edge).
network = "..." # Self-link or name of the VPC the router attaches to.
# Attachment type: "PARTNER" (provider-brokered) or "DEDICATED" (your circuit).
attachment_type = "..."
# For DEDICATED only: the self-link of the pre-provisioned physical
# google_compute_interconnect plus the 802.1Q VLAN tag you were assigned.
# interconnect = "..." # Required when attachment_type = "DEDICATED".
# vlan_tag8021q = 0 # Required when attachment_type = "DEDICATED".
# For PARTNER only: which edge availability domain to land in.
# edge_availability_domain = "AVAILABILITY_DOMAIN_1"
}
Then terraform init && terraform apply. Every other input has a sensible default — see Inputs below to override behaviour.
What this module is
Google Cloud Interconnect gives you a private, high-bandwidth path between your on-prem (or another cloud) network and a Google VPC, bypassing the public internet. There are two flavours: Dedicated Interconnect, where you order a 10G/100G physical circuit into a Google colocation facility and own the google_compute_interconnect object; and Partner Interconnect, where a supported service provider already has the physical link and brokers you a slice of bandwidth (50M–50G) without a colo cross-connect.
The single most important thing to understand is the layering. The physical link — the google_compute_interconnect resource for Dedicated, or the provider’s circuit for Partner — is almost always pre-provisioned out of band: someone orders the port, runs the cross-connect, and waits days or weeks for it to light up. Terraform rarely creates that object from scratch. What you do build in code, repeatedly and per-environment, is the logical overlay: a VLAN attachment (google_compute_interconnect_attachment) that carves a tagged Layer-2 segment out of the physical link, a regional Cloud Router (google_compute_router) that owns your BGP ASN, a router interface bound to the attachment, and a BGP peer (google_compute_router_peer) that exchanges routes with your on-prem edge.
This module wraps exactly that overlay. You hand it a pre-existing physical Interconnect self-link (Dedicated) or just pick an edge availability domain (Partner), and it stitches together the attachment, Cloud Router, interface, and BGP session with the right arguments wired for each type — so a hybrid connection becomes a single reviewed, versioned Terraform change instead of a console clickthrough across four resource pages.
When to use it
- You have an already-ordered physical Interconnect (Dedicated circuit lit in a colo, or a Partner connection provisioned by your carrier) and you need to bring up the logical VLAN attachment + routing repeatably across
dev/staging/prodor across regions. - You run a hub-and-spoke landing zone and want every hybrid edge built identically — same Cloud Router ASN convention, same advertised-route priorities, same MTU — rather than hand-rolled per team.
- You need redundant attachments: a pair of VLAN attachments (one per edge availability domain for Partner, or one per circuit for Dedicated) sharing a Cloud Router, and you want each one expressed as a clean module call.
- You are migrating from HA VPN to Interconnect for higher bandwidth/lower latency and want the BGP plumbing (peer ASN, route priority, interface naming) codified the same way.
Reach for the HA VPN module instead when you do not have (or do not want) a physical circuit and IPsec-over-internet bandwidth is acceptable. This module deliberately owns the attachment-plus-routing overlay and assumes the physical google_compute_interconnect is provisioned elsewhere.
Module structure
terraform-module-gcp-interconnect/
├── versions.tf
├── main.tf
├── variables.tf
└── outputs.tf
versions.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}
main.tf
locals {
# Derive consistent child-resource names from a single base name so the
# router, attachment, interface, and peer are easy to correlate in the console.
router_name = coalesce(var.router_name, "${var.name}-router")
attachment_name = coalesce(var.attachment_name, "${var.name}-vlan")
interface_name = "${var.name}-if"
peer_name = "${var.name}-peer"
is_dedicated = var.attachment_type == "DEDICATED"
is_partner = var.attachment_type == "PARTNER"
}
# Cloud Router owns the Google-side BGP ASN and lives in the same region as the
# VLAN attachment. Reuse an existing router by passing router_name to skip the
# creation here only if you manage the router elsewhere; this module creates it.
resource "google_compute_router" "this" {
project = var.project_id
name = local.router_name
region = var.region
network = var.network
description = var.description
# encrypted_interconnect_router is required when the attachment uses IPSEC
# encryption (HA VPN over Cloud Interconnect). Harmless to leave false otherwise.
encrypted_interconnect_router = var.encryption == "IPSEC"
bgp {
asn = var.google_asn
advertise_mode = var.advertise_mode
advertised_groups = var.advertise_mode == "CUSTOM" ? var.advertised_groups : []
dynamic "advertised_ip_ranges" {
for_each = var.advertise_mode == "CUSTOM" ? var.advertised_ip_ranges : []
content {
range = advertised_ip_ranges.value.range
description = lookup(advertised_ip_ranges.value, "description", null)
}
}
}
}
# The VLAN attachment carves a tagged segment out of the physical Interconnect.
# DEDICATED -> you supply the interconnect self-link and the 802.1Q VLAN tag.
# PARTNER -> Google/your carrier manages the interconnect + VLAN tag upstream;
# you only choose the edge availability domain.
resource "google_compute_interconnect_attachment" "this" {
project = var.project_id
name = local.attachment_name
region = var.region
router = google_compute_router.this.id
type = var.attachment_type
description = var.description
# DEDICATED-only arguments. interconnect must be set for DEDICATED and must
# NOT be set for PARTNER; vlan_tag8021q is managed upstream for PARTNER.
interconnect = local.is_dedicated ? var.interconnect : null
vlan_tag8021q = local.is_dedicated ? var.vlan_tag8021q : null
# PARTNER-only argument. Pin a pair of attachments to different domains for HA.
edge_availability_domain = local.is_partner ? var.edge_availability_domain : null
# Bandwidth is settable for DEDICATED; for PARTNER it is set by the operator
# (output only), so we only pass it through on DEDICATED attachments.
bandwidth = local.is_dedicated ? var.bandwidth : null
mtu = var.mtu
admin_enabled = var.admin_enabled
# Encryption: NONE (default) or IPSEC for HA VPN over Cloud Interconnect.
encryption = var.encryption
ipsec_internal_addresses = var.encryption == "IPSEC" ? var.ipsec_internal_addresses : null
stack_type = var.stack_type
}
# Bind a Cloud Router interface to the VLAN attachment. The interface IP is
# allocated by Google from the attachment's link-local /29 unless you pin one.
resource "google_compute_router_interface" "this" {
project = var.project_id
name = local.interface_name
region = var.region
router = google_compute_router.this.name
interconnect_attachment = google_compute_interconnect_attachment.this.self_link
# Optional: pin the Google-side interface address. For PARTNER attachments the
# addresses are typically auto-allocated, so leave this null.
ip_range = var.interface_ip_range
}
# BGP session with your on-prem / peer edge. Lower advertised_route_priority
# wins for inbound traffic engineering across a redundant pair of attachments.
resource "google_compute_router_peer" "this" {
project = var.project_id
name = local.peer_name
region = var.region
router = google_compute_router.this.name
interface = google_compute_router_interface.this.name
peer_asn = var.peer_asn
advertised_route_priority = var.advertised_route_priority
# peer_ip_address is required when you pin ip_address on the interface; for
# auto-allocated PARTNER attachments both are usually left null.
ip_address = var.peer_interface_ip_address
peer_ip_address = var.peer_ip_address
enable = var.peer_enable
dynamic "bfd" {
for_each = var.enable_bfd ? [1] : []
content {
session_initialization_mode = var.bfd_session_initialization_mode
min_receive_interval = var.bfd_min_receive_interval
min_transmit_interval = var.bfd_min_transmit_interval
multiplier = var.bfd_multiplier
}
}
}
variables.tf
variable "name" {
type = string
description = "Base name used to derive the router, attachment, interface, and BGP peer names."
validation {
condition = can(regex("^[a-z]([-a-z0-9]{0,40}[a-z0-9])?$", var.name))
error_message = "name must be RFC1035: lowercase, start with a letter, end alphanumeric, <= 42 chars (leaves room for suffixes)."
}
}
variable "project_id" {
type = string
description = "ID of the GCP project that owns the Cloud Router and VLAN attachment."
}
variable "region" {
type = string
description = "Region of the Cloud Router and attachment; must match the Interconnect edge location."
}
variable "network" {
type = string
description = "Self-link or name of the VPC network the Cloud Router attaches to."
}
variable "description" {
type = string
description = "Free-text description applied to the router and attachment."
default = "Managed by Terraform (kloudvin terraform-module-gcp-interconnect)."
}
variable "router_name" {
type = string
description = "Explicit Cloud Router name. If null, derived as \"<name>-router\"."
default = null
}
variable "attachment_name" {
type = string
description = "Explicit VLAN attachment name. If null, derived as \"<name>-vlan\"."
default = null
}
variable "attachment_type" {
type = string
description = "Attachment type: DEDICATED (you own the physical circuit) or PARTNER (broker-provisioned)."
validation {
condition = contains(["DEDICATED", "PARTNER"], var.attachment_type)
error_message = "attachment_type must be DEDICATED or PARTNER."
}
}
variable "interconnect" {
type = string
description = "Self-link of the pre-provisioned physical google_compute_interconnect. Required for DEDICATED, must be null for PARTNER."
default = null
}
variable "vlan_tag8021q" {
type = number
description = "IEEE 802.1Q VLAN tag (2-4094) for DEDICATED attachments. Managed upstream for PARTNER (leave null)."
default = null
validation {
condition = var.vlan_tag8021q == null || (var.vlan_tag8021q >= 2 && var.vlan_tag8021q <= 4094)
error_message = "vlan_tag8021q must be null or between 2 and 4094."
}
}
variable "edge_availability_domain" {
type = string
description = "Edge availability domain for PARTNER attachments (AVAILABILITY_DOMAIN_1/2/ANY). Ignored for DEDICATED."
default = "AVAILABILITY_DOMAIN_1"
validation {
condition = contains(
["AVAILABILITY_DOMAIN_ANY", "AVAILABILITY_DOMAIN_1", "AVAILABILITY_DOMAIN_2"],
var.edge_availability_domain
)
error_message = "edge_availability_domain must be AVAILABILITY_DOMAIN_ANY, _1, or _2."
}
}
variable "bandwidth" {
type = string
description = "Provisioned bandwidth for DEDICATED attachments (e.g. BPS_10G). Output-only for PARTNER."
default = "BPS_10G"
validation {
condition = 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.bandwidth)
error_message = "bandwidth must be a valid BPS_* enum value."
}
}
variable "mtu" {
type = number
description = "MTU in bytes for the attachment. Valid values are 1440, 1460, 1500, and 8896."
default = 1500
validation {
condition = contains([1440, 1460, 1500, 8896], var.mtu)
error_message = "mtu must be one of 1440, 1460, 1500, or 8896."
}
}
variable "admin_enabled" {
type = bool
description = "Whether the VLAN attachment is enabled. For PARTNER, true pre-activates the attachment."
default = true
}
variable "encryption" {
type = string
description = "Encryption option: NONE (default) or IPSEC for HA VPN over Cloud Interconnect."
default = "NONE"
validation {
condition = contains(["NONE", "IPSEC"], var.encryption)
error_message = "encryption must be NONE or IPSEC."
}
}
variable "ipsec_internal_addresses" {
type = list(string)
description = "Self-links of reserved internal addresses used when encryption = IPSEC."
default = []
}
variable "stack_type" {
type = string
description = "Stack type for the attachment: IPV4_ONLY or IPV4_IPV6."
default = "IPV4_ONLY"
validation {
condition = contains(["IPV4_ONLY", "IPV4_IPV6"], var.stack_type)
error_message = "stack_type must be IPV4_ONLY or IPV4_IPV6."
}
}
variable "google_asn" {
type = number
description = "BGP ASN for the Google-side Cloud Router (private range, e.g. 16550 for PARTNER, or your own 64512-65534)."
default = 16550
}
variable "peer_asn" {
type = number
description = "BGP ASN of the on-prem / peer router."
}
variable "advertised_route_priority" {
type = number
description = "Priority of routes advertised to the peer. Lower wins; use to steer traffic across redundant attachments."
default = 100
}
variable "advertise_mode" {
type = string
description = "Cloud Router advertisement mode: DEFAULT (all subnets) or CUSTOM (explicit groups/ranges)."
default = "DEFAULT"
validation {
condition = contains(["DEFAULT", "CUSTOM"], var.advertise_mode)
error_message = "advertise_mode must be DEFAULT or CUSTOM."
}
}
variable "advertised_groups" {
type = list(string)
description = "Prefix groups to advertise in CUSTOM mode (e.g. [\"ALL_SUBNETS\"])."
default = ["ALL_SUBNETS"]
}
variable "advertised_ip_ranges" {
type = list(object({
range = string
description = optional(string)
}))
description = "Explicit IP ranges to advertise in CUSTOM mode, in addition to advertised_groups."
default = []
}
variable "interface_ip_range" {
type = string
description = "Optional link-local /29 to pin the Google-side router interface IP. Null = auto-allocate."
default = null
}
variable "peer_interface_ip_address" {
type = string
description = "Google-side interface IP (ip_address on the peer). Required if peer_ip_address is set."
default = null
}
variable "peer_ip_address" {
type = string
description = "On-prem BGP interface IP. Required when peer_interface_ip_address is set; null for auto-allocated PARTNER."
default = null
}
variable "peer_enable" {
type = bool
description = "Whether the BGP peer session is enabled."
default = true
}
variable "enable_bfd" {
type = bool
description = "Enable Bidirectional Forwarding Detection for faster failure detection on the BGP session."
default = true
}
variable "bfd_session_initialization_mode" {
type = string
description = "BFD session mode: ACTIVE, PASSIVE, or DISABLED."
default = "ACTIVE"
validation {
condition = contains(["ACTIVE", "PASSIVE", "DISABLED"], var.bfd_session_initialization_mode)
error_message = "bfd_session_initialization_mode must be ACTIVE, PASSIVE, or DISABLED."
}
}
variable "bfd_min_receive_interval" {
type = number
description = "Minimum BFD receive interval in milliseconds."
default = 1000
}
variable "bfd_min_transmit_interval" {
type = number
description = "Minimum BFD transmit interval in milliseconds."
default = 1000
}
variable "bfd_multiplier" {
type = number
description = "Number of consecutive missed BFD packets before the session is declared down."
default = 5
}
outputs.tf
output "router_id" {
description = "Fully-qualified ID of the Cloud Router."
value = google_compute_router.this.id
}
output "router_name" {
description = "Name of the Cloud Router."
value = google_compute_router.this.name
}
output "attachment_id" {
description = "ID of the VLAN (interconnect) attachment."
value = google_compute_interconnect_attachment.this.id
}
output "attachment_self_link" {
description = "Self-link of the VLAN attachment, for use by router interfaces and monitoring."
value = google_compute_interconnect_attachment.this.self_link
}
output "pairing_key" {
description = "PARTNER-only opaque pairing key to hand to your service provider to provision the circuit. Null for DEDICATED."
value = google_compute_interconnect_attachment.this.pairing_key
}
output "cloud_router_ip_address" {
description = "Google-side IPv4 address + prefix configured on the Cloud Router interface for this attachment."
value = google_compute_interconnect_attachment.this.cloud_router_ip_address
}
output "customer_router_ip_address" {
description = "Customer-side IPv4 address + prefix to configure on your on-prem router subinterface."
value = google_compute_interconnect_attachment.this.customer_router_ip_address
}
output "interface_name" {
description = "Name of the Cloud Router interface bound to the attachment."
value = google_compute_router_interface.this.name
}
output "peer_name" {
description = "Name of the BGP peer."
value = google_compute_router_peer.this.name
}
output "state" {
description = "Current operational state of the VLAN attachment."
value = google_compute_interconnect_attachment.this.state
}
How to use it
# DEDICATED: you own the physical circuit. Reference its self-link and the
# VLAN tag Google assigned, then bring up a 10G attachment with BGP.
module "interconnect_dedicated" {
source = "git::https://dev.azure.com/teknohut/kloudvin/_git/terraform-modules//terraform-module-gcp-interconnect?ref=v1.0.0"
name = "prod-dc1"
project_id = "kv-prod-host-9f3a"
region = "us-central1"
network = module.vpc_network.network_self_link
attachment_type = "DEDICATED"
interconnect = "projects/kv-prod-host-9f3a/global/interconnects/colo-equinix-ch1"
vlan_tag8021q = 1010
bandwidth = "BPS_10G"
mtu = 1500
google_asn = 65001
peer_asn = 64512
advertised_route_priority = 100
# Pin the BGP /30 from your IPAM-reserved link-local range.
interface_ip_range = "169.254.10.1/29"
peer_interface_ip_address = "169.254.10.1"
peer_ip_address = "169.254.10.2"
advertise_mode = "CUSTOM"
advertised_ip_ranges = [
{ range = "10.0.0.0/16", description = "prod-supernet" },
]
}
# PARTNER: the carrier owns the circuit. You pick an edge domain, apply, then
# hand the pairing_key output to your provider to complete provisioning.
module "interconnect_partner" {
source = "git::https://dev.azure.com/teknohut/kloudvin/_git/terraform-modules//terraform-module-gcp-interconnect?ref=v1.0.0"
name = "prod-partner-a"
project_id = "kv-prod-host-9f3a"
region = "europe-west2"
network = module.vpc_network.network_self_link
attachment_type = "PARTNER"
edge_availability_domain = "AVAILABILITY_DOMAIN_1"
google_asn = 16550 # required ASN for PARTNER attachments
peer_asn = 64513
}
# Downstream: surface the pairing key for the operations runbook / ticket.
resource "google_secret_manager_secret_version" "pairing_key" {
secret = google_secret_manager_secret.partner_pairing.id
secret_data = module.interconnect_partner.pairing_key
}
Build a pair of attachments — one per edge availability domain (PARTNER) or one per circuit (DEDICATED) — sharing route priorities so a single edge failure does not sever hybrid connectivity.
With Terragrunt
Terragrunt keeps this module DRY across environments — define the backend and provider once in a root config, then a thin terragrunt.hcl per environment supplies only the inputs that differ.
1. Root config — live/terragrunt.hcl (inherited by every module):
remote_state {
backend = "gcs"
generate = { path = "backend.tf", if_exists = "overwrite" }
config = {
# ...gcs state bucket/container + key per path...
}
}
2. Module config — live/prod/interconnect/terragrunt.hcl:
include "root" {
path = find_in_parent_folders()
}
terraform {
source = "git::https://dev.azure.com/teknohut/kloudvin/_git/terraform-modules//terraform-module-gcp-interconnect?ref=v1.0.0"
}
inputs = {
name = "..."
project_id = "..."
region = "..."
network = "..."
attachment_type = "..."
peer_asn = 0
}
3. Deploy one environment, or roll out all modules together:
cd live/prod/interconnect && terragrunt apply # this module
terragrunt run-all apply # every module under live/prod
Why Terragrunt here: the backend and provider live in one place instead of being copy-pasted into every module; inputs is overridden per environment (dev / stage / prod) without forking the module; and run-all orchestrates dependencies across modules. Reach for it once you have more than one environment or more than a handful of modules — for a single stack, the plain Quickstart above is enough.
Inputs
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
name |
string |
— | Yes | Base name for the router, attachment, interface, and peer. |
project_id |
string |
— | Yes | Project that owns the Cloud Router and attachment. |
region |
string |
— | Yes | Region of the router/attachment; matches the Interconnect edge. |
network |
string |
— | Yes | Self-link or name of the VPC the router attaches to. |
attachment_type |
string |
— | Yes | DEDICATED or PARTNER. |
peer_asn |
number |
— | Yes | BGP ASN of the on-prem / peer router. |
description |
string |
"Managed by Terraform…" |
No | Description on the router and attachment. |
router_name |
string |
null |
No | Explicit router name; derived as <name>-router if null. |
attachment_name |
string |
null |
No | Explicit attachment name; derived as <name>-vlan if null. |
interconnect |
string |
null |
No | Physical Interconnect self-link (required for DEDICATED). |
vlan_tag8021q |
number |
null |
No | 802.1Q VLAN tag (2–4094) for DEDICATED. |
edge_availability_domain |
string |
"AVAILABILITY_DOMAIN_1" |
No | Edge domain for PARTNER attachments. |
bandwidth |
string |
"BPS_10G" |
No | Provisioned bandwidth for DEDICATED. |
mtu |
number |
1500 |
No | Attachment MTU (1440/1460/1500/8896). |
admin_enabled |
bool |
true |
No | Whether the attachment is enabled / pre-activated. |
encryption |
string |
"NONE" |
No | NONE or IPSEC (HA VPN over Interconnect). |
ipsec_internal_addresses |
list(string) |
[] |
No | Reserved internal address self-links for IPSEC. |
stack_type |
string |
"IPV4_ONLY" |
No | IPV4_ONLY or IPV4_IPV6. |
google_asn |
number |
16550 |
No | Cloud Router ASN (16550 required for PARTNER). |
advertised_route_priority |
number |
100 |
No | Inbound route priority; lower wins. |
advertise_mode |
string |
"DEFAULT" |
No | DEFAULT or CUSTOM advertisement mode. |
advertised_groups |
list(string) |
["ALL_SUBNETS"] |
No | Prefix groups for CUSTOM mode. |
advertised_ip_ranges |
list(object) |
[] |
No | Explicit ranges for CUSTOM mode. |
interface_ip_range |
string |
null |
No | Pinned link-local /29 for the router interface. |
peer_interface_ip_address |
string |
null |
No | Google-side interface IP. |
peer_ip_address |
string |
null |
No | On-prem BGP interface IP. |
peer_enable |
bool |
true |
No | Enable the BGP peer session. |
enable_bfd |
bool |
true |
No | Enable BFD on the BGP session. |
bfd_session_initialization_mode |
string |
"ACTIVE" |
No | BFD mode: ACTIVE/PASSIVE/DISABLED. |
bfd_min_receive_interval |
number |
1000 |
No | BFD min receive interval (ms). |
bfd_min_transmit_interval |
number |
1000 |
No | BFD min transmit interval (ms). |
bfd_multiplier |
number |
5 |
No | Missed-packet multiplier before session down. |
Outputs
| Name | Description |
|---|---|
router_id |
Fully-qualified Cloud Router ID. |
router_name |
Cloud Router name. |
attachment_id |
VLAN attachment ID. |
attachment_self_link |
Self-link of the VLAN attachment. |
pairing_key |
PARTNER pairing key to hand to your provider (null for DEDICATED). |
cloud_router_ip_address |
Google-side BGP IPv4 + prefix. |
customer_router_ip_address |
Customer-side BGP IPv4 + prefix. |
interface_name |
Router interface name. |
peer_name |
BGP peer name. |
state |
Operational state of the attachment. |
Enterprise scenario
A logistics platform runs a Shared VPC host project in kv-prod-host-9f3a and needs resilient hybrid connectivity from two data centres into us-central1 and europe-west2. The network team had already ordered two Dedicated 10G circuits months earlier; the physical google_compute_interconnect objects were lit by the colo provider and live outside Terraform. With this module, each region’s connectivity becomes two module calls — one per circuit, each with a distinct vlan_tag8021q and a shared Cloud Router ASN of 65001 — and the advertised_route_priority is staggered (100 primary, 200 backup) so traffic prefers the primary circuit but fails over automatically. A separate Partner attachment in europe-west2 provides burst capacity; its pairing_key output is pushed to Secret Manager and the carrier completes provisioning from there. The entire hybrid edge — four attachments, two routers, eight BGP sessions with BFD — is one reviewed PR, and the customer_router_ip_address outputs feed straight into the on-prem router automation.
Best practices
- Treat the physical Interconnect as pre-existing. Order and light the
google_compute_interconnect(or Partner circuit) out of band; this module owns only the VLAN attachment, Cloud Router, and BGP. Pass the physical self-link in for DEDICATED and never let Terraform try to create the port. - Always deploy attachments in redundant pairs. For PARTNER, place one attachment in
AVAILABILITY_DOMAIN_1and another inAVAILABILITY_DOMAIN_2; for DEDICATED, use two circuits in different edge zones. Share a Cloud Router and staggeradvertised_route_priorityfor clean primary/backup failover. - Enable BFD on every BGP session. Sub-second failure detection (
enable_bfd = true) is the difference between a few dropped packets and a minutes-long outage when a circuit flaps; keep the defaultACTIVEmode with a multiplier of 5. - Use the right ASN for the type. PARTNER attachments require the Google-side ASN
16550; for DEDICATED, use a private ASN from your allocation (64512–65534) and keep it consistent per environment so route policies are predictable. - Pin MTU end to end. Match
mtuto what your on-prem edge and the circuit support (1500 is the safe default; 8896 only when every hop supports jumbo frames). A mismatch silently blackholes large packets. - Surface and protect the pairing key. For PARTNER, treat
pairing_keyas a short-lived secret — push it to Secret Manager or a ticketing workflow for the provider, and never commit it to a repo or echo it into CI logs.