Skip to content

Google Cloud VPN Module#

This module provisions Google Cloud VPN in an opinionated, production-ready way. It supports both Classic VPN and HA VPN topologies, integrates with Cloud Router for BGP, and manages a single shared IPsec secret via Secret Manager (created or read), keeping sensitive values out of state and source control.

Overview#

  • Topologies: Classic VPN and HA VPN (recommended) using terraform-google-modules/vpn/google.
  • Routing: Optional Cloud Router with BGP, configurable advertise mode, groups and IP ranges.
  • Secrets: Single shared IPsec secret for all tunnels. Can be auto-generated and stored in Secret Manager, or read from an existing secret, or provided as plaintext.

Features#

  • HA VPN and Classic VPN support with pinned module versions.
  • Single shared secret for Classic and HA; Secret Manager integration (create/read, optional generation).
  • Cloud Router/BGP support with customizable advertisement configuration.
  • Clear outputs for integration with downstream modules.

Limitations#

  • This wrapper currently exposes peer_external_gateway for HA VPN. A first-class peer_gcp_gateway input is not exposed yet (GCP-to-GCP HA-to-HA requires adding it or composing two sides).
  • API enabling and Secret Manager IAM bindings are not created by this module.
  • Per‑tunnel secrets are not supported by design (intentionally simplified to one shared secret).

Compatibility#

  • Terraform: 1.x
  • Providers: hashicorp/google >= 6.28.0, < 7.0.0, hashicorp/random >= 3.6.0
  • Upstream modules: terraform-google-modules/vpn/google ~> 6.0.0

Usage#

module "vpn" {
  source = "./"

  project_id = "acme-prod-shared-vpc"
  region     = "me-west1"
  network    = "shared-vpc-network"
  ha_vpn     = true

  # Create a single shared secret in Secret Manager (recommended)
  create_secrets = true
  secret_id      = "gcp-s1-site-to-site-shared-secret"
  shared_secret  = "" # generated if empty

  router_asn = 65000
  peer_external_gateway = {
    redundancy_type = "TWO_IPS_REDUNDANCY"
    interfaces = [
      { id = 0, ip_address = "4.33.217.107" },
      { id = 1, ip_address = "5.197.175.205" }
    ]
  }

  router_advertise_config = {
    mode   = "CUSTOM"
    groups = ["ALL_SUBNETS"]
    ip_ranges = {
      "35.19.192.0/19" = "dns-forwarding"
      "10.240.0.0/16"  = "mysql-shared-prod-me-west1"
      "10.0.1.0/24"    = "subnet-shared-prod-me-west1"
    }
  }

  tunnels = {
    tunnel-0 = {
      bgp_peer                        = { address = "169.254.76.129", asn = 64512 }
      bgp_peer_options                = null
      bgp_session_range               = "169.254.76.130/30"
      ike_version                     = 2
      vpn_gateway_interface           = 0
      peer_external_gateway_interface = 0
      shared_secret                   = "" # ignored; single secret is used for all
    }
    tunnel-1 = {
      bgp_peer                        = { address = "169.254.78.153", asn = 64512 }
      bgp_peer_options                = null
      bgp_session_range               = "169.254.78.154/30"
      ike_version                     = 2
      vpn_gateway_interface           = 0
      peer_external_gateway_interface = 1
      shared_secret                   = "" # ignored; single secret is used for all
    }
  }
}

Requirements#

Name Version
terraform ~> 1
google >= 6.28.0, < 7.0.0
google-beta >= 6.28.0, < 7.0.0
random >= 3.6.0

Providers#

Name Version
google >= 6.28.0, < 7.0.0
random >= 3.6.0

Modules#

Name Source Version
ha_vpn terraform-google-modules/vpn/google//modules/vpn_ha ~> 6.0.0
vpn terraform-google-modules/vpn/google ~> 6.0.0

Resources#

Name Type
google_compute_router.this resource
google_secret_manager_secret.shared resource
google_secret_manager_secret_version.shared resource
random_password.shared resource
google_secret_manager_secret_version.shared data source

Inputs#

Name Description Type Default Required
asn Router asn number null no
bgp_cr_session_range Please enter the cloud-router interface IP/Session IP list(string)
[
"",
""
]
no
bgp_remote_session_range Please enter the remote environments BGP Session IP list(string)
[
"",
""
]
no
classic_secret_id [Deprecated] Legacy secret_id input. Use secret_id instead. string "" no
cr_bgp_advertise_mode User-specified flag to indicate which mode to use for advertisement string "DEFAULT" no
cr_bgp_advertised_groups User-specified list of prefix groups to advertise in custom mode. This field can only be populated if advertiseMode is CUSTOM and is advertised to all peers of the router. These groups will be advertised in addition to any specified prefixes. Leave this field blank to advertise no custom groups. This enum field has the one valid value: ALL_SUBNETS list(string)
[
"ALL_SUBNETS"
]
no
cr_bgp_advertised_ip_ranges User-specified list of individual IP ranges to advertise in custom mode. This field can only be populated if advertiseMode is CUSTOM and is advertised to all peers of the router. These IP ranges will be advertised in addition to any specified groups list(string) [] no
cr_enabled If there is a cloud router for BGP routing bool false no
create_secrets When true, create Secret Manager entries for Classic and HA tunnels. If false, expect existing Secret Manager secrets for HA and use shared_secret for Classic. bool false no
gateway_name The name of VPN gateway string "test-vpn" no
ha_vpn True if you want to create S2S HA VPN bool false no
ike_version Please enter the IKE version used by this tunnel (default is IKEv2) number 2 no
local_traffic_selector Local traffic selector to use when establishing the VPN tunnel with peer VPN gateway. list(string)
[
"0.0.0.0/0"
]
no
network The name of VPC being created string n/a yes
peer_asn Please enter the ASN of the BGP peer that cloud router will use list(string)
[
"65101"
]
no
peer_external_gateway Configuration of an external VPN gateway to which this VPN is connected.
object({
redundancy_type = string
interfaces = list(object({
id = number
ip_address = string
}))
})
null no
peer_ips IP address of remote-peer/gateway list(string) [] no
project_id The ID of the project where this VPC will be created string n/a yes
region The region in which you want to create the VPN gateway string n/a yes
remote_subnet remote subnet ip range in CIDR format - x.x.x.x/x list(string) [] no
remote_traffic_selector Remote traffic selector to use when establishing the VPN tunnel with peer VPN gateway. list(string)
[
"0.0.0.0/0"
]
no
route_priority Priority for static route being created number 1000 no
router_advertise_config Router custom advertisement configuration
object({
groups = list(string)
ip_ranges = map(string)
mode = string
})
null no
router_asn Router ASN used for auto-created router. number 64512 no
router_name Router name string null no
secret_id Secret Manager secret_id to use for the shared VPN secret. If empty and create_secrets=true, defaults to -shared-secret. If set and create_secrets=false, the secret will be read from Secret Manager. string "" no
shared_secret Please enter the shared secret/pre-shared key string "" no
tunnel_count The number of tunnels from each VPN gw (default is 1) number 1 no
tunnel_name_prefix The optional custom name of VPN tunnel being created string "" no
tunnels VPN tunnel configurations, bgp_peer_options is usually null.
map(object({
bgp_peer = object({
address = string
asn = number
})
bgp_peer_options = object({
advertise_groups = list(string)
advertise_ip_ranges = map(string)
advertise_mode = string
route_priority = number
})
bgp_session_range = string
ike_version = number
vpn_gateway_interface = number
peer_external_gateway_interface = number
shared_secret = string
}))
{} no
vpn_gw_ip Please enter the public IP address of the VPN Gateway, if you have already one. Do not set this variable to autocreate one string "" no

Outputs#

Name Description
gateway_ip The VPN Gateway Public IP
gateway_self_link The self-link of the Gateway
ha_gateway n/a
ha_router n/a
ha_tunnels n/a
ipsec_secret-dynamic The secret
ipsec_secret-static The secret
name The name of the Gateway
network The name of the VPC
project_id The Project-ID
vpn_tunnels_names-dynamic The VPN tunnel name is
vpn_tunnels_names-static The VPN tunnel name is
vpn_tunnels_self_link-dynamic The VPN tunnel self-link is
vpn_tunnels_self_link-static The VPN tunnel self-link is

Software#

  • Terraform 1.x
  • Python 3.x (for tests): pytest, tftest, hcl2 parser
  • Optional: gcloud CLI for local auth and project configuration

Service Account#

The identity running this module should have:

  • roles/compute.networkAdmin (manage VPN gateways, tunnels, routers)
  • If creating secrets (create_secrets = true): roles/secretmanager.admin
  • If reading existing secrets: roles/secretmanager.secretAccessor

APIs#

Enable the following APIs in the target project:

  • compute.googleapis.com
  • secretmanager.googleapis.com (when using Secret Manager)

License#

This module is covered by a commercial license. Please see LICENSE for details.

Support#

This module is developed and maintained by MyOps Limited. For any support inquiries, please contact us at info@myops.co.il.