Creates a single Google Cloud Router (
google_compute_router) with an optional Cloud NAT gateway (google_compute_router_nat). Targetshashicorp/google ~> 7.0, Terraform>= 1.12.0.
- 🔀 Creates one
google_compute_router— the GCP resource behind the "Cloud Router" product, providing dynamic BGP routing for VPN tunnels and Interconnect/Partner attachments. - 🌐 Optionally creates one
google_compute_router_nat— the "Cloud NAT" gateway attached to this router, giving instances without external IPs outbound internet access. - 🧱 Standalone, not composite:
google_compute_router_natis a single, optional 1:1 companion to the router in the common case (a router typically has zero or one Cloud NAT gateway configured against it), not afor_each-managed collection of many child records. Per this module suite's definition of standalone ("a single keystone resource with no natural child collection"), this module stays standalone — the NAT resource is rendered from a single optional object variable, never a caller-keyed map of many NAT gateways. - 🔒 Never defaults
source_subnetwork_ip_ranges_to_nattoALL_SUBNETWORKS_ALL_IP_RANGES— the caller must explicitly choose how much of the network NAT covers. - 🧭 Takes an explicit
network(id/self_link fromterraform-google-vpc-network) andregion— both legitimate module variables here per this module suite's authentication-model exception, since they are resource-level arguments intrinsic togoogle_compute_router, not provider overrides.
💡 Why it matters: a Cloud Router is the control-plane resource behind both dynamic BGP routing (VPN/Interconnect) and Cloud NAT. Getting its region/network pairing right — and never silently NATing an entire network — is a foundational networking control, not an implementation detail.
If these Terraform modules have been helpful to you or your organization, I'd appreciate your support in any of the following ways:
- ⭐ Star this repository to help others discover this Terraform module.
- 🤝 Connect with me on LinkedIn: linkedin.com/in/microsoftexpert
- ☕ Buy me a coffee: buymeacoffee.com/microsoftexpert
Whether it's a star, a professional connection, or a coffee, every gesture helps keep these modules actively maintained and continually improving. Thank you for being part of the community!
graph LR
VPC["terraform-google-vpc-network"]:::keystoneSibling
PS["terraform-google-project-services"]:::external
THIS["terraform-google-cloud-router"]:::thisModule
FUT["Future: VPN Gateway /\nInterconnect Attachment module"]:::future
PS -. "enables compute.googleapis.com (informal prerequisite)".-> THIS
VPC -- "network self_link / id consumed as var.network" --> THIS
THIS -. "router self_link / name consumed by (future, not yet in catalog)".-> FUT
classDef thisModule fill:#4285F4,color:#ffffff,stroke:#174EA6,stroke-width:1px;
classDef keystoneSibling fill:#174EA6,color:#ffffff,stroke:#174EA6,stroke-width:1px;
classDef external fill:#E8EAED,color:#202124,stroke:#9AA0A6,stroke-width:1px,stroke-dasharray: 3 3;
classDef future fill:#E8EAED,color:#202124,stroke:#9AA0A6,stroke-width:1px,stroke-dasharray: 3 3;
terraform-google-project-services (external, dashed) must have already enabled compute.googleapis.com
before this module applies. terraform-google-vpc-network is this module's only real cross-module input —
its self_link (or id) output becomes var.network. A VPN Gateway / Interconnect Attachment
module would be this router's most direct downstream consumer (of name/self_link, for BGP peer
configuration), but no such module exists yet in the catalog — shown dashed as a documented
future consumer, not a shipped dependency.
graph LR
subgraph Inputs
A["var.router_name"]
B["var.network"]
C["var.region"]
D["var.bgp (optional)"]
E["var.nat (optional)"]
F["var.timeouts"]
end
R["google_compute_router.this"]:::thisModule
N["google_compute_router_nat.this\n(for_each 0..1, keyed on var.nat != null)"]:::optionalResource
A --> R
B --> R
C --> R
D --> R
F --> R
R -- "router = google_compute_router.this.name" --> N
E -- "nat config" --> N
R --> O1["output: id"]
R --> O2["output: self_link"]
R --> O3["output: name"]
N -. "null when var.nat == null".-> O4["output: nat_name"]
classDef thisModule fill:#4285F4,color:#ffffff,stroke:#174EA6,stroke-width:1px;
classDef optionalResource fill:#8957E5,color:#ffffff,stroke:#174EA6,stroke-width:1px,stroke-dasharray: 3 3;
Resource inventory:
| Resource | Cardinality | Notes |
|---|---|---|
google_compute_router.this |
Exactly 1 | Keystone; BGP config is optional (var.bgp, default null) |
google_compute_router_nat.this |
0 or 1 (for_each over { this = var.nat } when non-null) |
Never count, even for this naturally-singular resource |
| Requirement | Value |
|---|---|
| Terraform | >= 1.12.0 |
hashicorp/google provider |
~> 7.0 |
| Provider block | None — the caller configures google (project, region/zone, auth) |
Schema notes that bite (verified against hashicorp/google v7.39.0 via terraform-registry,
providerDocID 12683499 / 12683502):
- A router can only BGP-peer within its own region/network pairing — a VPN tunnel or Interconnect attachment in a different region, or attached to a different network, cannot use this router.
router_name,network, andregionare force-new — changing any of them destroys and recreates the router (and transitively the NAT gateway configured against it).bgpis Optional in the live schema, not Required — a router created solely to host a Cloud NAT gateway does not need a BGP session at all. This module models that:var.bgpdefaults tonull.- The NAT
subnetworknested block'snameargument is confusingly named — the provider wants the subnetwork's self-link there, not a display name. This module's field is namedsubnetwork_self_linkto avoid that confusion. nat.source_subnetwork_ip_ranges_to_nathas no default in this module — the caller must explicitly chooseALL_SUBNETWORKS_ALL_IP_RANGES,ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES, orLIST_OF_SUBNETWORKSeven though the underlying provider argument is Required regardless.- Neither
google_compute_routernorgoogle_compute_router_natexposes alabelsargument in the live schema — confirmed for both, symmetrically. See Architecture Notes. timeouts(create/update/delete, default 20 minutes each) is supported by both resources independently — the router's ownvar.timeoutsand the NAT's nestedvar.nat.timeouts.
roles/compute.networkAdminon the target project — create, update, and delete Cloud Router and Cloud NAT resources. No broader project-level role is required by this module alone.
compute.googleapis.comenabled on the target project (viaterraform-google-project-services, applied before this module).- The target VPC network (from
terraform-google-vpc-network) must already exist — this module only attaches to it viavar.network, it does not create it.
terraform-google-cloud-router/
├── providers.tf # required_providers (hashicorp/google ~> 7.0) + required_version — no provider {} block
├── variables.tf # router_name, network, region, bgp, nat, timeouts
├── main.tf # google_compute_router.this + optional google_compute_router_nat.this
├── outputs.tf # id, self_link, name, nat_name
├── README.md # this file
├── SCOPE.md # lightweight cross-module contract
└── examples/ # runnable example matching the Quick Start below
module "cloud_router" {
source = "git::https://github.com/microsoftexpert/terraform-google-cloud-router.git?ref=v1.0.0"
router_name = "us-east1-router"
network = module.vpc_network.self_link
region = "us-east1"
}The caller's root module configures the google provider (project, region/zone, and authentication
via ADC, Workload Identity Federation, or a service account key supplied out-of-band) — this module
accepts none of those as variables.
Consumes
| Input | Type | Source module |
|---|---|---|
network |
string (self_link or id) |
terraform-google-vpc-network |
Emits
| Output | Description | Consumed by |
|---|---|---|
id |
Router resource id, projects/{{project}}/regions/{{region}}/routers/{{name}} |
Any module needing the Terraform-internal reference |
self_link |
Router self-link (URL form) | Future VPN/Interconnect modules' router field |
name |
Router name | Future VPN/Interconnect modules' router field (name form) |
nat_name |
Name of the NAT gateway, or null if var.nat was not supplied |
Diagnostic/reference use; conditional |
1 · Minimal router — no BGP, no NAT
module "cloud_router" {
source = "git::https://github.com/microsoftexpert/terraform-google-cloud-router.git?ref=v1.0.0"
router_name = "us-east1-router"
network = module.vpc_network.self_link
region = "us-east1"
}💡 The empty-beyond-required call is the safe, complete path: no BGP session and no NAT gateway are created. This is a valid, working Cloud Router — useful as a placeholder that a later
applycan extend withbgp/natwithout recreating the resource.
2 · Router with a default-mode BGP session
module "cloud_router" {
source = "git::https://github.com/microsoftexpert/terraform-google-cloud-router.git?ref=v1.0.0"
router_name = "us-east1-router"
network = module.vpc_network.self_link
region = "us-east1"
bgp = {
asn = 64514
}
}ℹ️
advertise_modedefaults toDEFAULT— GCP automatically advertises the router's own subnet ranges. UseCUSTOMmode (example 8) to advertise specific groups/ranges instead.
3 · Router + NAT — ALL_SUBNETWORKS_ALL_IP_RANGES, explicit choice
module "cloud_router" {
source = "git::https://github.com/microsoftexpert/terraform-google-cloud-router.git?ref=v1.0.0"
router_name = "us-east1-router"
network = module.vpc_network.self_link
region = "us-east1"
nat = {
name = "us-east1-nat"
source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"
}
}
⚠️ ALL_SUBNETWORKS_ALL_IP_RANGESNATs every IP range in every subnetwork in this router's region — this module requires the caller to type this value explicitly (there is no default), so this broad a scope is always a conscious choice, never an accident.
4 · Router + NAT — ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES, explicit choice
module "cloud_router" {
source = "git::https://github.com/microsoftexpert/terraform-google-cloud-router.git?ref=v1.0.0"
router_name = "us-east1-router"
network = module.vpc_network.self_link
region = "us-east1"
nat = {
name = "us-east1-nat"
source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES"
}
}ℹ️ Narrower than example 3 — only each subnetwork's primary IP range is NATed, excluding secondary (alias) ranges such as GKE pod/service ranges.
5 · Router + NAT — LIST_OF_SUBNETWORKS, single subnetwork
module "cloud_router" {
source = "git::https://github.com/microsoftexpert/terraform-google-cloud-router.git?ref=v1.0.0"
router_name = "us-east1-router"
network = module.vpc_network.self_link
region = "us-east1"
nat = {
name = "us-east1-nat"
source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS"
subnetworks = {
"app-subnet" = {
subnetwork_self_link = module.vpc_network.subnetwork_self_links["app-subnet-use1"]
source_ip_ranges_to_nat = ["ALL_IP_RANGES"]
}
}
}
}🔒 The narrowest, most auditable NAT scope — only the named subnetwork(s) get outbound NAT. This module's
validation {}block rejects an emptysubnetworksmap wheneverLIST_OF_SUBNETWORKSis chosen.
6 · Router + NAT — LIST_OF_SUBNETWORKS, multiple subnetworks with secondary ranges
module "cloud_router" {
source = "git::https://github.com/microsoftexpert/terraform-google-cloud-router.git?ref=v1.0.0"
router_name = "us-east1-router"
network = module.vpc_network.self_link
region = "us-east1"
nat = {
name = "us-east1-nat"
source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS"
subnetworks = {
"gke-nodes" = {
subnetwork_self_link = module.vpc_network.subnetwork_self_links["gke-nodes-use1"]
source_ip_ranges_to_nat = ["ALL_IP_RANGES"]
}
"app-subnet" = {
subnetwork_self_link = module.vpc_network.subnetwork_self_links["app-subnet-use1"]
source_ip_ranges_to_nat = ["LIST_OF_SECONDARY_IP_RANGES"]
secondary_ip_range_names = ["pods", "services"]
}
}
}
}ℹ️
secondary_ip_range_namesis only meaningful when the corresponding entry'ssource_ip_ranges_to_natincludesLIST_OF_SECONDARY_IP_RANGES— otherwise it is ignored by the provider.
7 · Router + NAT — MANUAL_ONLY with static NAT IPs
resource "google_compute_address" "nat_ip" {
name = "us-east1-nat-ip"
region = "us-east1"
lifecycle {
create_before_destroy = true
}
}
module "cloud_router" {
source = "git::https://github.com/microsoftexpert/terraform-google-cloud-router.git?ref=v1.0.0"
router_name = "us-east1-router"
network = module.vpc_network.self_link
region = "us-east1"
nat = {
name = "us-east1-nat"
source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"
nat_ip_allocate_option = "MANUAL_ONLY"
nat_ips = [google_compute_address.nat_ip.self_link]
}
}
⚠️ Per the live provider docs, recreating agoogle_compute_addressused bygoogle_compute_router_natraises aresourceInUseByAnotherResourceerror — thelifecycle.create_before_destroy = trueblock shown above is required on the address resource to avoid it.
8 · Custom BGP advertisement mode
module "cloud_router" {
source = "git::https://github.com/microsoftexpert/terraform-google-cloud-router.git?ref=v1.0.0"
router_name = "us-east1-router"
network = module.vpc_network.self_link
region = "us-east1"
bgp = {
asn = 64514
advertise_mode = "CUSTOM"
advertised_groups = ["ALL_SUBNETS"]
advertised_ip_ranges = [
{ range = "192.168.100.0/24", description = "on-prem DR site" },
{ range = "10.50.0.0/16" },
]
}
}ℹ️
advertised_groupsandadvertised_ip_rangesare only applied whenadvertise_mode = "CUSTOM"; this module'svalidation {}block rejects anyadvertised_groupsvalue other than"ALL_SUBNETS"(the only value the live schema accepts).
9 · BGP keepalive interval and explicit identifier range
module "cloud_router" {
source = "git::https://github.com/microsoftexpert/terraform-google-cloud-router.git?ref=v1.0.0"
router_name = "us-east1-router"
network = module.vpc_network.self_link
region = "us-east1"
bgp = {
asn = 64514
keepalive_interval = 60
identifier_range = "169.254.0.0/29"
}
}
⚠️ keepalive_intervalmust be between 20 and 60 seconds (enforced viavalidation {});identifier_rangemust not overlap any IPv4 BGP session range already in use on this router.
10 · Router at the 32-bit private ASN boundary
module "cloud_router" {
source = "git::https://github.com/microsoftexpert/terraform-google-cloud-router.git?ref=v1.0.0"
router_name = "us-east1-router-32bit-asn"
network = module.vpc_network.self_link
region = "us-east1"
bgp = {
asn = 4200000000 # minimum of the 32-bit private ASN range
}
}
⚠️ asnmust fall within either the 16-bit private range (64512-65534) or the 32-bit private range (4200000000-4294967294) per RFC6996 — a value outside both ranges fails this module'svalidation {}block at plan time, before any API call.
11 · NAT logging widened to ALL
module "cloud_router" {
source = "git::https://github.com/microsoftexpert/terraform-google-cloud-router.git?ref=v1.0.0"
router_name = "us-east1-router"
network = module.vpc_network.self_link
region = "us-east1"
nat = {
name = "us-east1-nat"
source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"
log_config = {
enable = true
filter = "ALL"
}
}
}🔒 The default is
{ enable = true, filter = "ERRORS_ONLY" }— logging is on by default per this module suite's audit-logging secure-default rule, at the least noisy level. Widen toTRANSLATIONS_ONLYorALLexplicitly if full connection visibility is required (higher log volume and cost).
12 · Higher port allocation per VM
module "cloud_router" {
source = "git::https://github.com/microsoftexpert/terraform-google-cloud-router.git?ref=v1.0.0"
router_name = "us-east1-router"
network = module.vpc_network.self_link
region = "us-east1"
nat = {
name = "us-east1-nat"
source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"
min_ports_per_vm = 256
}
}ℹ️ Raise
min_ports_per_vmabove the provider's default of 64 for workloads that open many concurrent outbound connections per instance (e.g. high-fanout microservices); this reduces the number of instances a single NAT IP can support.
13 · Custom router and NAT timeouts
module "cloud_router" {
source = "git::https://github.com/microsoftexpert/terraform-google-cloud-router.git?ref=v1.0.0"
router_name = "us-east1-router"
network = module.vpc_network.self_link
region = "us-east1"
timeouts = {
create = "30m"
update = "30m"
delete = "30m"
}
nat = {
name = "us-east1-nat"
source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"
timeouts = {
create = "30m"
delete = "30m"
}
}
}ℹ️ The router's
var.timeoutsand the NAT's nestedvar.nat.timeoutsare entirely independent — each resource has its own schema-supported create/update/delete timeout, defaulting to 20 minutes each if left unset.
14 · 🏗️ End-to-end composition
module "project_services" {
source = "git::https://github.com/microsoftexpert/terraform-google-project-services.git?ref=v1.0.0"
# Enables compute.googleapis.com (and any other APIs the composition needs)
# — applied before every other module in this composition.
}
module "vpc_network" {
source = "git::https://github.com/microsoftexpert/terraform-google-vpc-network.git?ref=v1.0.0"
network_name = "prod-use1-network"
subnetworks = {
"app-subnet-use1" = {
ip_cidr_range = "10.0.1.0/24"
region = "us-east1"
}
}
depends_on = [module.project_services]
}
module "cloud_router" {
source = "git::https://github.com/microsoftexpert/terraform-google-cloud-router.git?ref=v1.0.0"
router_name = "prod-use1-router"
network = module.vpc_network.self_link
region = "us-east1"
bgp = {
asn = 64514
}
nat = {
name = "prod-use1-nat"
source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS"
subnetworks = {
"app-subnet" = {
subnetwork_self_link = module.vpc_network.subnetwork_self_links["app-subnet-use1"]
source_ip_ranges_to_nat = ["ALL_IP_RANGES"]
}
}
}
}💡 This wires
terraform-google-project-services→terraform-google-vpc-network→terraform-google-cloud-routerin dependency order: APIs enabled first, then the network and its subnetwork, then a router in the same region attached to that network viamodule.vpc_network.self_link, with a Cloud NAT scoped narrowly (LIST_OF_SUBNETWORKS) to only the one subnetwork that needs outbound internet access.
⚠️ region = "us-east1"on the router must match the region of every subnetwork referenced innat.subnetworks— a router cannot NAT a subnetwork in a different region. See Troubleshooting.
| Variable | Type | Required | Default | Notes |
|---|---|---|---|---|
router_name |
string |
Yes | — | Force-new; RFC1035, 1-63 chars |
network |
string |
Yes | — | self_link or id from terraform-google-vpc-network; force-new |
region |
string |
Yes | — | Force-new; router/NAT can only peer within this region |
bgp |
object({...}) |
No | null |
Omit for a NAT-only router (Cloud NAT does not require BGP) |
nat |
object({...}) |
No | null |
source_subnetwork_ip_ranges_to_nat has no default — explicit choice required |
timeouts |
object({ create, update, delete = optional(string) }) |
No | null |
Router's own timeouts; NAT has an independent nat.timeouts |
Full variable schemas
variable "router_name" {
type = string
# RFC1035, 1-63 chars, enforced via validation {}
}
variable "network" {
type = string
}
variable "region" {
type = string
}
variable "bgp" {
type = object({
asn = number
advertise_mode = optional(string, "DEFAULT")
advertised_groups = optional(list(string), [])
advertised_ip_ranges = optional(list(object({
range = string
description = optional(string)
})), [])
keepalive_interval = optional(number, 20)
identifier_range = optional(string)
})
default = null
}
variable "nat" {
type = object({
name = string
source_subnetwork_ip_ranges_to_nat = string
nat_ip_allocate_option = optional(string, "AUTO_ONLY")
nat_ips = optional(list(string), [])
min_ports_per_vm = optional(number, 64)
subnetworks = optional(map(object({
subnetwork_self_link = string
source_ip_ranges_to_nat = list(string)
secondary_ip_range_names = optional(list(string), [])
})), {})
log_config = optional(object({
enable = optional(bool, true)
filter = optional(string, "ERRORS_ONLY")
}), { enable = true, filter = "ERRORS_ONLY" })
timeouts = optional(object({
create = optional(string)
update = optional(string)
delete = optional(string)
}))
})
default = null
}
variable "timeouts" {
type = object({
create = optional(string)
update = optional(string)
delete = optional(string)
})
default = null
}No labels variable exists on this module — see Architecture Notes for why.
| Output | Description |
|---|---|
id |
projects/{{project}}/regions/{{region}}/routers/{{name}} |
self_link |
Router self-link (URL form) |
name |
Router name |
nat_name |
NAT gateway name, or null when var.nat was not supplied (conditional) |
None of these outputs are secret-bearing; no sensitive = true is applied to any of them.
- Region/network BGP-peering constraint. A Cloud Router can only BGP-peer with VPN tunnels or
Interconnect attachments in its own region and attached network. This is not enforced by
terraform validate— it fails only atapply(or at BGP session establishment time), per the house "plan-only posture" limitation. bgpis optional by design. The live schema marksbgpOptional, and this module preserves that: a router created solely to anchor a Cloud NAT gateway does not need a BGP session. Do not assume every router in this library carries BGP configuration.- NAT
subnetwork.nameis actually a self-link. The provider's nestedsubnetworkblock uses the argument namename, but the value it expects is the subnetwork's self-link, not a display name. This module's own field is namedsubnetwork_self_linkspecifically to avoid propagating that confusing name into caller-facing configuration. - No silent "NAT everything."
nat.source_subnetwork_ip_ranges_to_nathas no default in this module — every caller must type one of the three valid values explicitly. This is a deliberate deviation from simply passing through the provider's own (Required-but-otherwise-unguided) argument. - No
labelsvariable — confirmed schema-driven omission, symmetric across both resources. Verified against the live schema (hashicorp/googlev7.39.0) that neithergoogle_compute_routernorgoogle_compute_router_natexposes alabelsargument. Per this module suite's own post-module checklist, this module documents the omission rather than fabricating a decorative variable with no effect on either resource. timeoutsis modeled independently per resource. The router's ownvar.timeoutsand the NAT's nestedvar.nat.timeoutsboth support create/update/delete (20-minute provider default each) — they are not shared or coupled.nat_nameis conditional. It resolves tonullwhenevervar.natis not supplied; any consumer of this output must check fornullbefore using it.
| Concern | Secure default | Opt-out (explicit) |
|---|---|---|
NAT subnetwork scope (source_subnetwork_ip_ranges_to_nat) |
No default — caller must explicitly choose ALL_SUBNETWORKS_ALL_IP_RANGES, ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES, or LIST_OF_SUBNETWORKS |
N/A — this is itself the opt-out mechanism; there is no narrower "safe" value to fall back to silently |
NAT logging (nat.log_config) |
{ enable = true, filter = "ERRORS_ONLY" } — enabled by default at the least noisy level |
Caller sets filter = "TRANSLATIONS_ONLY" or "ALL" for full visibility, or enable = false to disable |
NAT IP allocation (nat.nat_ip_allocate_option) |
AUTO_ONLY — GCP-managed IP allocation, no static IP resource to manage |
Caller sets MANUAL_ONLY and supplies nat_ips (self-links of pre-created google_compute_address resources) |
BGP session (bgp) |
null — no BGP session unless explicitly configured |
Caller supplies bgp = { asn =... } |
| Deletion guard | Not applicable — google_compute_router/google_compute_router_nat expose no deletion_protection argument in the live schema |
N/A |
cd terraform-google-cloud-router
terraform init -backend=false
terraform validate
terraform fmt -checkPin the module source to ?ref=v1.0.0 — never a branch. This library is plan-only from an
authoring session; a human applies from CI with valid Workload Identity Federation or ADC
credentials.
terraform validate confirms internal type and reference consistency (e.g. router_name's and
nat.name's validation {} blocks, the bgp/nat object schemas, correct resource/output
wiring). terraform fmt -check confirms canonical formatting. Neither can catch GCP API-level
rejections — most importantly, the region/network BGP-peering constraint, quota limits, or org-policy
constraints. Only a real terraform plan/apply against a live project, with valid credentials,
exercises those paths — that step belongs to the consuming CI pipeline, not this authoring session.
$ terraform output
id = "projects/casey-prod-networking/regions/us-east1/routers/prod-use1-router"
name = "prod-use1-router"
nat_name = "prod-use1-nat"
self_link = "https://www.googleapis.com/compute/v1/projects/casey-prod-networking/regions/us-east1/routers/prod-use1-router"
| Symptom | Cause | Fix |
|---|---|---|
apply fails with a region/network mismatch error on a BGP peer or NAT subnetwork |
The router can only BGP-peer or NAT within its own region/network pairing | Ensure var.region matches the region of every subnetwork referenced in nat.subnetworks, and that var.network is the same network those subnetworks belong to |
plan fails with a nat.source_subnetwork_ip_ranges_to_nat validation error |
Value omitted or not one of the three valid enums | Set it explicitly to ALL_SUBNETWORKS_ALL_IP_RANGES, ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES, or LIST_OF_SUBNETWORKS |
plan fails with "nat.subnetworks must contain at least one entry" |
source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS" but subnetworks was left empty |
Supply at least one entry in nat.subnetworks |
plan fails with a bgp.asn validation error |
ASN outside the RFC6996 private ranges (64512-65534 or 4200000000-4294967294) | Choose an ASN within one of the two private ranges |
apply fails with resourceInUseByAnotherResource on a google_compute_address used by NAT |
Recreating a static NAT IP address that Cloud NAT is actively using | Add lifecycle { create_before_destroy = true } to the google_compute_address resource (see example 7) |
nat_name output is null when a NAT gateway was expected |
var.nat was not supplied on this apply |
Confirm nat = {... } is set; nat_name is null by design whenever var.nat == null |
No labels variable in variables.tf — looks incomplete compared to other modules |
Deliberate: neither google_compute_router nor google_compute_router_nat exposes a labels argument in the live schema |
Confirmed against the live provider schema; see Architecture Notes |
google_compute_routerprovider resource referencegoogle_compute_router_natprovider resource referenceterraform-google-vpc-network(upstream — suppliesvar.network)terraform-google-project-services(must enablecompute.googleapis.combefore this module applies)- This module's
SCOPE.md