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