Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

☁️ Google Cloud Compute Route Terraform Module

Creates a single static custom route (google_compute_route) in an existing VPC network — routed to a VPN tunnel, a specific instance, an internal load balancer, a literal next-hop IP, or the default internet gateway. Targets hashicorp/google ~> 7.0, Terraform >= 1.12.0.

Terraform Google Provider Module Version Module Type Resources Posture


🧩 Overview

  • 🛣️ Creates one google_compute_route — the GCP resource behind a "static route" entry in a VPC network's routing table. Terraform's resource name (google_compute_route) does not map 1:1 to a distinct GCP console product the way, say, google_container_cluster maps to "GKE" — a route is simply an entry inside the VPC network product's own routing table, per this module suite's guidance on naming mismatches between Terraform resources and GCP console concepts.
  • 🎯 A route tells traffic matching a destination CIDR where to go next: to a VPN tunnel, a specific instance, an internal load balancer, a literal next-hop IP, or the internet gateway. Exactly one next-hop target is required per route — never zero, never more than one.
  • 🧱 Standalone, not composite: no natural for_each-managed child collection — a single keystone google_compute_route.this.
  • 🔀 Five mutually-exclusive next-hop variables (next_hop_gateway, next_hop_instance, next_hop_ip, next_hop_vpn_tunnel, next_hop_ilb), enforced via a validation {} block that catches zero-set or more-than-one-set at plan time — before an invalid combination ever reaches the GCP API.
  • ⚠️ priority defaults to GCP's own default (1000) with a prominent tie-break-risk callout — there is no universally "safer" priority value; the right one always depends on the other routes already present in the target network.
  • 🚫 Confirmed schema gap: this resource has no labels argument at alltags (instance tags controlling which VMs use this route) is a distinct, unrelated mechanism.

💡 Why it matters: a route with the wrong (or absent) next-hop configuration either fails outright at apply or, worse, silently loses the tie-break against another route in the same network and never carries any traffic at all. Enforcing the five-way exclusivity at plan time, and surfacing the priority tie-break gotcha prominently, is what keeps a hybrid-connectivity or custom-routing composition from a routing failure that terraform validate alone would never catch.


❤️ Support this project

If these Terraform modules have been helpful to you or your organization, I'd appreciate your support in any of the following ways:

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!


🗺️ Where this fits

graph LR
 PS["terraform-google-project-services"]:::external
 VPC["terraform-google-vpc-network"]:::keystoneSibling
 VPN["terraform-google-cloud-vpn"]:::sibling
 CI["terraform-google-compute-instance"]:::sibling
 THIS["terraform-google-compute-route"]:::thisModule

 PS -. "enables compute.googleapis.com (informal prerequisite)".-> THIS
 VPC -- "network self_link (recommended) or name consumed as var.network" --> THIS
 VPN -- "tunnel_self_links map entry consumed as var.next_hop_vpn_tunnel (optional)" --> THIS
 CI -- "self_link/name/internal_ip consumed as var.next_hop_instance / var.next_hop_ip (optional)" --> THIS

 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 sibling fill:#E8EAED,color:#202124,stroke:#9AA0A6,stroke-width:1px;
Loading

Validated via the Mermaid Chart MCP (validate_and_render_mermaid_diagram) before embedding.

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 keystone/target sibling — its network self_link (recommended) or name output becomes var.network, the one required cross-module input every call needs. terraform-google-cloud-vpn and terraform-google-compute-instance are real, optional upstream siblings feeding this module's next-hop variables — this module is the "future static-route module" terraform-google-cloud-vpn's own SCOPE.md already anticipated for its tunnel_self_links output.


🧬 What this builds

graph LR
 subgraph Inputs
 A["var.name / var.network / var.dest_range"]
 B["var.description / var.priority / var.tags"]
 C["exactly one of: var.next_hop_gateway / var.next_hop_instance (+ var.next_hop_instance_zone) / var.next_hop_ip / var.next_hop_vpn_tunnel / var.next_hop_ilb"]
 D["var.params.resource_manager_tags (optional)"]
 E["var.project / var.deletion_policy / var.timeouts"]
 end

 R["google_compute_route.this"]:::thisModule

 A --> R
 B --> R
 C --> R
 D --> R
 E --> R

 R --> O1["output: id"]
 R --> O2["output: self_link"]
 R --> O3["output: name"]
 R --> O4["output: route_status"]

 classDef thisModule fill:#4285F4,color:#ffffff,stroke:#174EA6,stroke-width:1px;
Loading

Validated via the Mermaid Chart MCP (validate_and_render_mermaid_diagram) before embedding.

Resource inventory:

Resource Cardinality Notes
google_compute_route.this Exactly 1 Keystone; params is the only optional nested block, rendered via dynamic only when populated

✅ Provider / Versions

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, provider_doc_id 12683498, cross-checked against the installed provider's own terraform providers schema -json output):

  • Five-way next-hop exclusivity is "exactly one," not "at most one." Confirmed verbatim: "A Route resource must have exactly one specification of either nextHopGateway, nextHopInstance, nextHopIp, nextHopVpnTunnel, or nextHopIlb." A route with none set is just as invalid as one with two or more set.
  • priority's default (1000) is a silent tie-break risk. Confirmed verbatim: "the one with the lowest-numbered priority value wins" — a caller who accepts the default is only safe from conflicts if no other route in the network also uses 1000 (or a lower value) for an overlapping destination range, including GCP's own implicit subnet routes.
  • network argument's own live-docs example uses .name (bare name), not .self_link/.id — a deviation from this library's usual self_link-preferred convention, resolved (not silently "corrected") in this module's favor of self_link as the recommended, cross-project-safe form. See Architecture Notes.
  • params.resource_manager_tags is immutable/create-time-only. Confirmed verbatim: "The field is immutable and causes resource replacement when mutated... modifying this field after creation will trigger recreation."
  • No labels argument anywhere. Confirmed absent from the full live Argument Reference — a genuine Rule 6 escape-clause case, same precedent as terraform-google-vpc-network.
  • timeouts is create/delete only — confirmed against the live schema's block_types.timeouts, which lists only these two (no update).
  • next_hop_ilb on the GA provider accepts only a partial/full URL, not a bare IP — confirmed: "With the GA provider you can only specify the forwarding rule as a partial or full URL" (the bare-IP form is a google-beta-only capability, out of scope for this GA-only library).

🔑 Required IAM Roles

  • roles/compute.networkAdmin on the target project — create, update, and delete routes.

☁️ GCP Prerequisites

  • compute.googleapis.com enabled on the target project (via terraform-google-project-services, applied before this module).
  • The target network must already exist — typically via terraform-google-vpc-network.
  • If project is supplied to target a Shared VPC host project, the applying principal needs roles/compute.networkAdmin on that HOST project specifically.

📁 Module Structure

terraform-google-compute-route/
├── providers.tf # required_providers (hashicorp/google ~> 7.0) + required_version — no provider {} block
├── variables.tf # name/network/dest_range, 5-way next-hop exclusivity, priority, tags, params, project, deletion_policy, timeouts
├── main.tf # google_compute_route.this
├── outputs.tf # id, self_link, name, route_status
├── README.md # this file
├── SCOPE.md # lightweight cross-module contract
└── examples/ # runnable example matching the Quick Start below

⚙️ Quick Start

module "compute_route" {
  source = "git::https://github.com/microsoftexpert/terraform-google-compute-route.git?ref=v1.0.0"

  name       = "casey-prod-route-to-internet"
  network    = "https://www.googleapis.com/compute/v1/projects/casey-prod-project/global/networks/casey-prod-vpc"
  dest_range = "0.0.0.0/0"

  next_hop_gateway = "default-internet-gateway"
}

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. The call above routes all otherwise-unmatched traffic to the default internet gateway at GCP's own default priority (1000).


🔌 Cross-Module Contract

Consumes

Input Type Source module
network string (self_link recommended, bare name also accepted) terraform-google-vpc-network
next_hop_vpn_tunnel (optional) string (VPN tunnel self_link) terraform-google-cloud-vpn
next_hop_instance (optional) string (instance self_link or name) terraform-google-compute-instance
next_hop_ip (optional) string (instance internal_ip) terraform-google-compute-instance
params.resource_manager_tags (optional) map(string) terraform-google-resource-tags

Emits

Output Description Consumed by
id Route Terraform-internal resource id Diagnostic/audit use
self_link Route self_link (full GCP API URL form) Diagnostic/audit use
name Route name Diagnostic/audit use
route_status ACTIVE/INACTIVE — operational diagnostic only Diagnostic/troubleshooting use

📚 Example Library

1 · Minimal route to the default internet gateway
module "compute_route" {
  source = "git::https://github.com/microsoftexpert/terraform-google-compute-route.git?ref=v1.0.0"

  name       = "casey-prod-route-to-internet"
  network    = module.vpc_network.self_link
  dest_range = "0.0.0.0/0"

  next_hop_gateway = "default-internet-gateway"
}

💡 "default-internet-gateway" is the literal string GCP expects — there is no sibling module that produces this value; it is a caller-supplied literal.

2 · Route to a specific instance via next_hop_instance + zone
module "compute_route" {
  source = "git::https://github.com/microsoftexpert/terraform-google-compute-route.git?ref=v1.0.0"

  name       = "casey-prod-route-via-nva"
  network    = module.vpc_network.self_link
  dest_range = "10.50.0.0/16"

  next_hop_instance      = module.nva_instance.name
  next_hop_instance_zone = "us-east1-b"
}

ℹ️ next_hop_instance_zone is only meaningful when next_hop_instance is a bare instance NAME (not a URL) — this module's validation {} block requires it be unset when next_hop_instance itself is unset.

3 · Route to an instance via self_link — no zone needed
module "compute_route" {
  source = "git::https://github.com/microsoftexpert/terraform-google-compute-route.git?ref=v1.0.0"

  name       = "casey-prod-route-via-nva-selflink"
  network    = module.vpc_network.self_link
  dest_range = "10.51.0.0/16"

  next_hop_instance = module.nva_instance.self_link
}

💡 A self_link already embeds its own zone — next_hop_instance_zone is correctly omitted here.

4 · Route with a literal next_hop_ip
module "compute_route" {
  source = "git::https://github.com/microsoftexpert/terraform-google-compute-route.git?ref=v1.0.0"

  name       = "casey-prod-route-static-ip"
  network    = module.vpc_network.self_link
  dest_range = "10.60.0.0/16"

  next_hop_ip = "10.0.1.25"
}

⚠️ next_hop_ip must be an IPv4 dotted-quad address — enforced via validation {}, mirroring terraform-google-cloud-vpn's own peer_ip precedent.

5 · Route to an internal load balancer via next_hop_ilb
module "compute_route" {
  source = "git::https://github.com/microsoftexpert/terraform-google-compute-route.git?ref=v1.0.0"

  name       = "casey-prod-route-to-ilb"
  network    = module.vpc_network.self_link
  dest_range = "192.0.2.0/24"

  next_hop_ilb = "regions/us-east1/forwardingRules/internal-lb-fr"
}

ℹ️ No sibling module produces an internal forwarding-rule reference yet — this input has no producing module in the current catalog. On the GA provider (this library's target), next_hop_ilb must be a partial or full URL, never a bare IP.

6 · Non-default priority with the tie-break risk called out
module "compute_route" {
  source = "git::https://github.com/microsoftexpert/terraform-google-compute-route.git?ref=v1.0.0"

  name       = "casey-prod-route-high-priority-override"
  network    = module.vpc_network.self_link
  dest_range = "10.70.0.0/16"
  priority   = 100

  next_hop_vpn_tunnel = module.cloud_vpn.tunnel_self_links["to-onprem-primary"]
}

⚠️ priority = 100 deliberately outranks GCP's default (1000) so this route wins any tie against the default-priority routes already present — but it can just as easily outrank a DIFFERENT custom route the caller does not realize exists. Review every route in the target network sorted by priority before relying on a specific outcome.

7 · Route scoped to specific instance tags
module "compute_route" {
  source = "git::https://github.com/microsoftexpert/terraform-google-compute-route.git?ref=v1.0.0"

  name       = "casey-prod-route-tagged-only"
  network    = module.vpc_network.self_link
  dest_range = "10.80.0.0/16"
  tags       = ["restricted-egress"]

  next_hop_vpn_tunnel = module.cloud_vpn.tunnel_self_links["to-onprem-primary"]
}

ℹ️ tags scopes which VMs' routing tables include this route (by their own network_tags) — a distinct mechanism from GCP labels (this resource has none) and from params.resource_manager_tags (a third, different mechanism again).

8 · Route with params.resource_manager_tags populated
module "compute_route" {
  source = "git::https://github.com/microsoftexpert/terraform-google-compute-route.git?ref=v1.0.0"

  name       = "casey-prod-route-tagged-resource"
  network    = module.vpc_network.self_link
  dest_range = "10.90.0.0/16"

  next_hop_gateway = "default-internet-gateway"

  params = {
    resource_manager_tags = {
      (module.resource_tags.id) = module.resource_tags.tag_value_ids["prod"]
    }
  }
}

⚠️ params.resource_manager_tags is immutable — changing it after creation forces a full route replacement. Use terraform-google-tag-bindings to attach a tag to an already-created route without replacing it.

9 · Cross-project route on a Shared VPC host network
module "compute_route" {
  source = "git::https://github.com/microsoftexpert/terraform-google-compute-route.git?ref=v1.0.0"

  name       = "casey-prod-shared-vpc-route"
  network    = module.shared_vpc_host_network.self_link
  dest_range = "10.100.0.0/16"
  project    = "casey-prod-networking-host"

  next_hop_gateway = "default-internet-gateway"
}

🔑 The applying principal needs roles/compute.networkAdmin on the HOST project (casey-prod-networking-host) specifically — not just the service project the caller's default provider configuration targets.

10 · Route with a custom description and explicit deletion_policy
module "compute_route" {
  source = "git::https://github.com/microsoftexpert/terraform-google-compute-route.git?ref=v1.0.0"

  name        = "casey-prod-route-critical-vpn-path"
  network     = module.vpc_network.self_link
  dest_range  = "10.0.0.0/8"
  description = "Sole route to the on-prem datacenter — do not delete without a migration plan"

  next_hop_vpn_tunnel = module.cloud_vpn.tunnel_self_links["to-onprem-primary"]

  deletion_policy = "PREVENT"
}

⚠️ deletion_policy = "PREVENT" blocks the Terraform command itself — it is a Terraform-state-level guard, not a GCP-API-enforced one. This module never defaults to "PREVENT"; opt in explicitly for operationally critical routes.

11 · Custom create/delete timeouts
module "compute_route" {
  source = "git::https://github.com/microsoftexpert/terraform-google-compute-route.git?ref=v1.0.0"

  name       = "casey-prod-route-to-internet"
  network    = module.vpc_network.self_link
  dest_range = "0.0.0.0/0"

  next_hop_gateway = "default-internet-gateway"

  timeouts = {
    create = "10m"
    delete = "10m"
  }
}

ℹ️ This resource's timeouts supports create/delete only — there is no update timeout, since every meaningful field change is force-new.

12 · Route using the bare network name (live-docs example form)
module "compute_route" {
  source = "git::https://github.com/microsoftexpert/terraform-google-compute-route.git?ref=v1.0.0"

  name       = "casey-dev-route-bare-name"
  network    = module.vpc_network.name
  dest_range = "10.110.0.0/16"

  next_hop_gateway = "default-internet-gateway"
}

ℹ️ The live google_compute_route docs' own executable examples use this bare-name form. This module accepts it unmodified — but see Architecture Notes for why self_link (example 1) is the recommended form for any composition that might ever span projects.

13 · Validation failure — zero next-hop fields set
module "compute_route" {
  source = "git::https://github.com/microsoftexpert/terraform-google-compute-route.git?ref=v1.0.0"

  name       = "casey-invalid-route-no-next-hop"
  network    = module.vpc_network.self_link
  dest_range = "10.120.0.0/16"
  # No next_hop_* field set at all.
}

🔒 This fails terraform plan immediately with this module's validation {} error message — "Exactly one of next_hop_gateway, next_hop_instance, next_hop_ip, next_hop_vpn_tunnel, or next_hop_ilb must be set" — rather than reaching the GCP API as an apply-time rejection.

14 · Validation failure — two next-hop fields set
module "compute_route" {
  source = "git::https://github.com/microsoftexpert/terraform-google-compute-route.git?ref=v1.0.0"

  name       = "casey-invalid-route-two-next-hops"
  network    = module.vpc_network.self_link
  dest_range = "10.130.0.0/16"

  next_hop_gateway = "default-internet-gateway"
  next_hop_ip      = "10.0.1.25"
}

🔒 Also fails terraform plan with the same validation {} error — setting more than one next-hop field is exactly as invalid as setting none.

15 · 🏗️ 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 = "casey-prod-use1-vpc"

  subnetworks = {
    "app-subnet-use1" = {
      ip_cidr_range = "10.0.1.0/24"
      region        = "us-east1"
    }
  }

  depends_on = [module.project_services]
}

module "cloud_vpn" {
  source = "git::https://github.com/microsoftexpert/terraform-google-cloud-vpn.git?ref=v1.0.0"

  gateway_name = "casey-prod-use1-havpn-gw"
  network      = module.vpc_network.id
  region       = "us-east1"

  tunnels = {
    "to-onprem-primary" = {
      vpn_gateway_interface    = 0
      peer_external_gateway    = module.external_vpn_gateway.id
      peer_ip                  = "203.0.113.1"
      shared_secret_wo         = var.vpn_shared_secret
      shared_secret_wo_version = 1
    }
  }

  depends_on = [module.vpc_network]
}

module "compute_route" {
  source = "git::https://github.com/microsoftexpert/terraform-google-compute-route.git?ref=v1.0.0"

  name       = "casey-prod-use1-route-to-onprem"
  network    = module.vpc_network.self_link
  dest_range = "192.168.0.0/16" # the on-prem CIDR reachable through this tunnel

  next_hop_vpn_tunnel = module.cloud_vpn.tunnel_self_links["to-onprem-primary"]

  priority = 900 # deliberately below GCP's 1000 default — see example 6's callout

  depends_on = [module.cloud_vpn]
}

💡 This wires terraform-google-project-servicesterraform-google-vpc-networkterraform-google-cloud-vpnterraform-google-compute-route in dependency order: APIs enabled first, then the VPC network, then an HA VPN gateway and tunnel to an on-prem peer, then a static route that sends on-prem-destined traffic through that tunnel by consuming tunnel_self_links["to-onprem-primary"] as next_hop_vpn_tunnel — the realistic hybrid-connectivity static-route scenario this module targets.

⚠️ module.external_vpn_gateway (the on-prem peer's google_compute_external_vpn_gateway) and var.vpn_shared_secret are referenced above but not shown in full — see terraform-google-cloud-vpn's own README for that resource's complete setup.


📥 Inputs

Variable Type Required Default Notes
name string Yes Force-new; 1-63 chars, RFC1035
network string Yes self_link recommended; bare name also accepted; force-new
dest_range string Yes IPv4 CIDR only; force-new
description string No null
priority number No 1000 ⚠️ Silent tie-break risk — see Architecture Notes
tags list(string) No [] Instance tags, NOT labels or Resource Manager tags
next_hop_gateway string No* null *Exactly one of the five next_hop_* fields required
next_hop_instance string No* null Pair with next_hop_instance_zone if a bare name
next_hop_instance_zone string No null Requires next_hop_instance to be set
next_hop_ip string No* null Validated IPv4 dotted-quad when set
next_hop_vpn_tunnel string No* null From terraform-google-cloud-vpn's tunnel_self_links
next_hop_ilb string No* null GA provider: partial/full URL only, no bare IP
params object({ resource_manager_tags }) No {} Immutable/create-time-only when populated
project string No null Cross-project override — Shared VPC host-project scenario
deletion_policy string No null Validated closed set; NOT defaulted to "PREVENT"
timeouts object({ create, delete }) No null Create/delete only — no update

labels is deliberately NOT modeled — confirmed absent from the live schema (see Architecture Notes).

Full variable schemas
variable "name" {
  type = string
}

variable "network" {
  type = string
}

variable "dest_range" {
  type = string
}

variable "description" {
  type    = string
  default = null
}

variable "priority" {
  type    = number
  default = 1000
}

variable "tags" {
  type    = list(string)
  default = []
}

variable "next_hop_gateway" {
  type    = string
  default = null
  # exactly one of next_hop_gateway/next_hop_instance/next_hop_ip/next_hop_vpn_tunnel/next_hop_ilb
}

variable "next_hop_instance" {
  type    = string
  default = null
}

variable "next_hop_instance_zone" {
  type    = string
  default = null
  # requires next_hop_instance to also be set
}

variable "next_hop_ip" {
  type    = string
  default = null
  # validated IPv4 dotted-quad when set
}

variable "next_hop_vpn_tunnel" {
  type    = string
  default = null
}

variable "next_hop_ilb" {
  type    = string
  default = null
}

variable "params" {
  type = object({
    resource_manager_tags = optional(map(string), {})
  })
  default = {}
}

variable "project" {
  type    = string
  default = null
}

variable "deletion_policy" {
  type    = string
  default = null
  # one of DELETE, PREVENT, ABANDON
}

variable "timeouts" {
  type = object({
    create = optional(string)
    delete = optional(string)
  })
  default = null
}

🧾 Outputs

Output Description Sensitive
id Route Terraform-internal resource id (projects/{{project}}/global/routes/{{name}}) No
self_link Route self_link (full GCP API URL form) — confirmed present, unlike this batch's terraform-google-folder/terraform-google-shared-vpc No
name Route name No
route_status ACTIVE/INACTIVE — reflects state as of last apply/refresh, not predictable at plan time No

No secret values exist on this resource; nothing is marked sensitive.


🧠 Architecture Notes

  • Five-way next-hop exclusivity, enforced at plan time, "exactly one," not "at most one." Modeled as five independent optional(string) variables with a single cross-variable validation {} block (attached to next_hop_gateway, Terraform 1.9+) checking the count of non-null entries across all five equals exactly 1 — zero set is exactly as invalid as two or more set.
  • priority's default (1000) is not a "safe" default — it is GCP's own default, carried through unchanged, with a prominent risk callout instead of a house override. There is no universally safer priority value; the correct value always depends on the other routes already present in the target network, which this module cannot see or validate against.
  • network argument reference-form — resolved judgment call. The live docs' own two executable Example Usage blocks both use the bare network NAME (google_compute_network.default.name), a real deviation from this library's usual self_link-preferred convention. This module documents and recommends self_link (this library's house convention, and the cross-project-unambiguous form relevant to the Shared VPC host-project scenario) while still accepting a bare name unmodified, since the live docs' own example proves the API accepts that form too. Not silently "corrected" back to self_link-only.
  • params.resource_manager_tags is create-time-only and immutable. Changing it after creation triggers a full route replacement, confirmed verbatim in the live schema. Use terraform-google-tag-bindings (google_tags_tag_binding) instead to apply a tag to an already-created route without replacing it.
  • No labels argument anywhere in the live schema. A genuine Rule 6 escape-clause case, same precedent as terraform-google-vpc-network. tags (instance tags controlling which VMs use this route) is a distinct, unrelated GCP primitive — do not conflate the two.
  • timeouts supports create/delete only — confirmed against the live schema; there is no update timeout, since every meaningful field change on this resource is force-new.
  • project is a documented cross-project override, not a default addition. Motivated concretely by the Shared VPC host-project scenario — sourced from the caller's own composition, never derived.
  • IAM propagation lag (up to ~60 seconds) applies to roles/compute.networkAdmin grants made immediately before this module applies, per this module suite's house-wide note on IAM propagation.

🧱 Design Principles

Concern Secure default Opt-out (explicit)
Route priority (priority) 1000 — GCP's own default, left unchanged (there is no universally "safer" priority value); documented risk callout instead of a house override Caller supplies an explicit, network-aware value after reviewing all existing routes' priorities
State-level destroy guard (deletion_policy) null (provider default "DELETE") — NOT defaulted to "PREVENT"; a route has materially lower blast radius than the resources this library does default to a protective posture for Caller sets "PREVENT" explicitly for an operationally critical route
Next-hop selection (next_hop_*) No default — the caller MUST supply exactly one; enforced via validation {} before any API call N/A — this is a hard requirement, not an opt-out
Resource Manager tags (params.resource_manager_tags) {} (omitted) — the params {} block is only rendered when populated Caller supplies tag key/value pairs sourced from terraform-google-resource-tags
Network reference form (network) self_link recommended in documentation (not enforced by type) — cross-project-unambiguous Caller may still supply a bare name for a same-project network

🚀 Runbook

cd terraform-google-compute-route
terraform init -backend=false
terraform validate
terraform fmt -check

Pin 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.


🧪 Testing

terraform validate confirms internal type and reference consistency (the five-way next-hop validation {} block, the next_hop_instance_zone conditional check, the params. resource_manager_tags format check, correct resource/output wiring). terraform fmt -check confirms canonical formatting. Neither can catch GCP API-level rejections — most importantly, a route that plans cleanly but is shadowed by a higher-priority-number (i.e. lower-precedence) route elsewhere in the network, or a next-hop target (an instance, a VPN tunnel, an internal load balancer) that does not actually exist yet at apply time. 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.


💬 Example Output

$ terraform output

id = "projects/casey-prod-project/global/routes/casey-prod-use1-route-to-onprem"
name = "casey-prod-use1-route-to-onprem"
self_link = "https://www.googleapis.com/compute/v1/projects/casey-prod-project/global/routes/casey-prod-use1-route-to-onprem"
route_status = "ACTIVE"

🔍 Troubleshooting

Symptom Cause Fix
Route created successfully but traffic isn't taking the expected path A lower-priority-number route elsewhere in the network is winning the tie-break Review all routes in the network sorted by priority, not just this module's own state
plan fails with "Exactly one of next_hop_gateway, next_hop_instance,..." Two or more next-hop fields were set, or none at all Set exactly one of the five next_hop_* fields
apply unexpectedly replaces the route after changing params.resource_manager_tags This field is immutable/create-time-only per the live schema Use terraform-google-tag-bindings to change a Resource Manager tag on an existing route without replacing it
plan fails with "next_hop_instance_zone requires next_hop_instance..." next_hop_instance_zone was set without next_hop_instance Set next_hop_instance (as a bare name) alongside next_hop_instance_zone, or omit both
apply fails with a permission-denied error on a Shared VPC host project The applying principal lacks roles/compute.networkAdmin on the HOST project referenced by project/network Grant roles/compute.networkAdmin on the host project, not just the service project
terraform destroy fails outright deletion_policy = "PREVENT" is set Set deletion_policy to "DELETE" (or remove the argument) before destroying
Route to a VPN tunnel never becomes ACTIVE The referenced next_hop_vpn_tunnel does not exist yet, or its own tunnel establishment failed Confirm terraform-google-cloud-vpn's tunnel is up before or via an explicit depends_on; check route_status

🔗 Related Docs

  • google_compute_route provider resource reference
  • terraform-google-vpc-network (upstream — supplies var.network)
  • terraform-google-cloud-vpn (upstream, optional — supplies var.next_hop_vpn_tunnel via tunnel_self_links)
  • terraform-google-compute-instance (upstream, optional — supplies var.next_hop_instance/var.next_hop_ip)
  • terraform-google-resource-tags, terraform-google-tag-bindings (upstream/downstream — params.resource_manager_tags lifecycle)
  • terraform-google-shared-vpc (related — the Shared VPC host-project scenario motivating var.project)
  • terraform-google-project-services (must enable compute.googleapis.com before this module applies)
  • This module's SCOPE.md

About

Terraform module: terraform-google-compute-route

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages