The gcp:compute/haVpnGateway:HaVpnGateway resource, part of the Pulumi GCP provider, provisions a Google-managed HA VPN gateway that terminates VPN tunnels with automatic failover. This guide focuses on three capabilities: basic gateway creation, dual-stack IPv4/IPv6 support, and encrypted Cloud Interconnect integration.
HA VPN gateways attach to VPC networks and provide higher availability than classic Target VPN Gateways through redundant interfaces. The examples are intentionally small. Combine them with VPN tunnel resources, peer gateway configuration, and BGP routing for complete connectivity.
Create a basic HA VPN gateway
Most VPN deployments start with a gateway attached to a VPC network, providing automatic failover through redundant interfaces.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const network1 = new gcp.compute.Network("network1", {
name: "network1",
autoCreateSubnetworks: false,
});
const haGateway1 = new gcp.compute.HaVpnGateway("ha_gateway1", {
region: "us-central1",
name: "ha-vpn-1",
network: network1.id,
});
import pulumi
import pulumi_gcp as gcp
network1 = gcp.compute.Network("network1",
name="network1",
auto_create_subnetworks=False)
ha_gateway1 = gcp.compute.HaVpnGateway("ha_gateway1",
region="us-central1",
name="ha-vpn-1",
network=network1.id)
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
network1, err := compute.NewNetwork(ctx, "network1", &compute.NetworkArgs{
Name: pulumi.String("network1"),
AutoCreateSubnetworks: pulumi.Bool(false),
})
if err != nil {
return err
}
_, err = compute.NewHaVpnGateway(ctx, "ha_gateway1", &compute.HaVpnGatewayArgs{
Region: pulumi.String("us-central1"),
Name: pulumi.String("ha-vpn-1"),
Network: network1.ID(),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var network1 = new Gcp.Compute.Network("network1", new()
{
Name = "network1",
AutoCreateSubnetworks = false,
});
var haGateway1 = new Gcp.Compute.HaVpnGateway("ha_gateway1", new()
{
Region = "us-central1",
Name = "ha-vpn-1",
Network = network1.Id,
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.Network;
import com.pulumi.gcp.compute.NetworkArgs;
import com.pulumi.gcp.compute.HaVpnGateway;
import com.pulumi.gcp.compute.HaVpnGatewayArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var network1 = new Network("network1", NetworkArgs.builder()
.name("network1")
.autoCreateSubnetworks(false)
.build());
var haGateway1 = new HaVpnGateway("haGateway1", HaVpnGatewayArgs.builder()
.region("us-central1")
.name("ha-vpn-1")
.network(network1.id())
.build());
}
}
resources:
haGateway1:
type: gcp:compute:HaVpnGateway
name: ha_gateway1
properties:
region: us-central1
name: ha-vpn-1
network: ${network1.id}
network1:
type: gcp:compute:Network
properties:
name: network1
autoCreateSubnetworks: false
The gateway sits in a specific region and attaches to your VPC network. The network property references the VPC where VPN traffic will flow. Google manages the gateway infrastructure; you configure tunnels separately to establish actual connectivity.
Enable dual-stack IPv4 and IPv6 support
Networks supporting both IPv4 and IPv6 clients configure the gateway’s stack type to handle both IP families.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const network1 = new gcp.compute.Network("network1", {
name: "network1",
autoCreateSubnetworks: false,
});
const haGateway1 = new gcp.compute.HaVpnGateway("ha_gateway1", {
region: "us-central1",
name: "ha-vpn-1",
network: network1.id,
stackType: "IPV4_IPV6",
labels: {
mykey: "myvalue",
},
});
import pulumi
import pulumi_gcp as gcp
network1 = gcp.compute.Network("network1",
name="network1",
auto_create_subnetworks=False)
ha_gateway1 = gcp.compute.HaVpnGateway("ha_gateway1",
region="us-central1",
name="ha-vpn-1",
network=network1.id,
stack_type="IPV4_IPV6",
labels={
"mykey": "myvalue",
})
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
network1, err := compute.NewNetwork(ctx, "network1", &compute.NetworkArgs{
Name: pulumi.String("network1"),
AutoCreateSubnetworks: pulumi.Bool(false),
})
if err != nil {
return err
}
_, err = compute.NewHaVpnGateway(ctx, "ha_gateway1", &compute.HaVpnGatewayArgs{
Region: pulumi.String("us-central1"),
Name: pulumi.String("ha-vpn-1"),
Network: network1.ID(),
StackType: pulumi.String("IPV4_IPV6"),
Labels: pulumi.StringMap{
"mykey": pulumi.String("myvalue"),
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var network1 = new Gcp.Compute.Network("network1", new()
{
Name = "network1",
AutoCreateSubnetworks = false,
});
var haGateway1 = new Gcp.Compute.HaVpnGateway("ha_gateway1", new()
{
Region = "us-central1",
Name = "ha-vpn-1",
Network = network1.Id,
StackType = "IPV4_IPV6",
Labels =
{
{ "mykey", "myvalue" },
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.Network;
import com.pulumi.gcp.compute.NetworkArgs;
import com.pulumi.gcp.compute.HaVpnGateway;
import com.pulumi.gcp.compute.HaVpnGatewayArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var network1 = new Network("network1", NetworkArgs.builder()
.name("network1")
.autoCreateSubnetworks(false)
.build());
var haGateway1 = new HaVpnGateway("haGateway1", HaVpnGatewayArgs.builder()
.region("us-central1")
.name("ha-vpn-1")
.network(network1.id())
.stackType("IPV4_IPV6")
.labels(Map.of("mykey", "myvalue"))
.build());
}
}
resources:
haGateway1:
type: gcp:compute:HaVpnGateway
name: ha_gateway1
properties:
region: us-central1
name: ha-vpn-1
network: ${network1.id}
stackType: IPV4_IPV6
labels:
mykey: myvalue
network1:
type: gcp:compute:Network
properties:
name: network1
autoCreateSubnetworks: false
The stackType property controls which IP protocols the gateway supports. Setting it to IPV4_IPV6 enables dual-stack operation, allowing the gateway to terminate both IPv4 and IPv6 VPN tunnels. The labels property adds metadata for organization and cost tracking.
Connect to Cloud Interconnect with IPsec encryption
Organizations using Cloud Interconnect can layer IPsec encryption over dedicated links by attaching the VPN gateway to Interconnect attachments.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const network = new gcp.compute.Network("network", {
name: "test-network",
autoCreateSubnetworks: false,
});
const address1 = new gcp.compute.Address("address1", {
name: "test-address1",
addressType: "INTERNAL",
purpose: "IPSEC_INTERCONNECT",
address: "192.168.1.0",
prefixLength: 29,
network: network.selfLink,
});
const router = new gcp.compute.Router("router", {
name: "test-router",
network: network.name,
encryptedInterconnectRouter: true,
bgp: {
asn: 16550,
},
});
const attachment1 = new gcp.compute.InterconnectAttachment("attachment1", {
name: "test-interconnect-attachment1",
edgeAvailabilityDomain: "AVAILABILITY_DOMAIN_1",
type: "PARTNER",
router: router.id,
encryption: "IPSEC",
ipsecInternalAddresses: [address1.selfLink],
});
const address2 = new gcp.compute.Address("address2", {
name: "test-address2",
addressType: "INTERNAL",
purpose: "IPSEC_INTERCONNECT",
address: "192.168.2.0",
prefixLength: 29,
network: network.selfLink,
});
const attachment2 = new gcp.compute.InterconnectAttachment("attachment2", {
name: "test-interconnect-attachment2",
edgeAvailabilityDomain: "AVAILABILITY_DOMAIN_2",
type: "PARTNER",
router: router.id,
encryption: "IPSEC",
ipsecInternalAddresses: [address2.selfLink],
});
const vpn_gateway = new gcp.compute.HaVpnGateway("vpn-gateway", {
name: "test-ha-vpngw",
network: network.id,
vpnInterfaces: [
{
id: 0,
interconnectAttachment: attachment1.selfLink,
},
{
id: 1,
interconnectAttachment: attachment2.selfLink,
},
],
});
import pulumi
import pulumi_gcp as gcp
network = gcp.compute.Network("network",
name="test-network",
auto_create_subnetworks=False)
address1 = gcp.compute.Address("address1",
name="test-address1",
address_type="INTERNAL",
purpose="IPSEC_INTERCONNECT",
address="192.168.1.0",
prefix_length=29,
network=network.self_link)
router = gcp.compute.Router("router",
name="test-router",
network=network.name,
encrypted_interconnect_router=True,
bgp={
"asn": 16550,
})
attachment1 = gcp.compute.InterconnectAttachment("attachment1",
name="test-interconnect-attachment1",
edge_availability_domain="AVAILABILITY_DOMAIN_1",
type="PARTNER",
router=router.id,
encryption="IPSEC",
ipsec_internal_addresses=[address1.self_link])
address2 = gcp.compute.Address("address2",
name="test-address2",
address_type="INTERNAL",
purpose="IPSEC_INTERCONNECT",
address="192.168.2.0",
prefix_length=29,
network=network.self_link)
attachment2 = gcp.compute.InterconnectAttachment("attachment2",
name="test-interconnect-attachment2",
edge_availability_domain="AVAILABILITY_DOMAIN_2",
type="PARTNER",
router=router.id,
encryption="IPSEC",
ipsec_internal_addresses=[address2.self_link])
vpn_gateway = gcp.compute.HaVpnGateway("vpn-gateway",
name="test-ha-vpngw",
network=network.id,
vpn_interfaces=[
{
"id": 0,
"interconnect_attachment": attachment1.self_link,
},
{
"id": 1,
"interconnect_attachment": attachment2.self_link,
},
])
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
network, err := compute.NewNetwork(ctx, "network", &compute.NetworkArgs{
Name: pulumi.String("test-network"),
AutoCreateSubnetworks: pulumi.Bool(false),
})
if err != nil {
return err
}
address1, err := compute.NewAddress(ctx, "address1", &compute.AddressArgs{
Name: pulumi.String("test-address1"),
AddressType: pulumi.String("INTERNAL"),
Purpose: pulumi.String("IPSEC_INTERCONNECT"),
Address: pulumi.String("192.168.1.0"),
PrefixLength: pulumi.Int(29),
Network: network.SelfLink,
})
if err != nil {
return err
}
router, err := compute.NewRouter(ctx, "router", &compute.RouterArgs{
Name: pulumi.String("test-router"),
Network: network.Name,
EncryptedInterconnectRouter: pulumi.Bool(true),
Bgp: &compute.RouterBgpArgs{
Asn: pulumi.Int(16550),
},
})
if err != nil {
return err
}
attachment1, err := compute.NewInterconnectAttachment(ctx, "attachment1", &compute.InterconnectAttachmentArgs{
Name: pulumi.String("test-interconnect-attachment1"),
EdgeAvailabilityDomain: pulumi.String("AVAILABILITY_DOMAIN_1"),
Type: pulumi.String("PARTNER"),
Router: router.ID(),
Encryption: pulumi.String("IPSEC"),
IpsecInternalAddresses: pulumi.StringArray{
address1.SelfLink,
},
})
if err != nil {
return err
}
address2, err := compute.NewAddress(ctx, "address2", &compute.AddressArgs{
Name: pulumi.String("test-address2"),
AddressType: pulumi.String("INTERNAL"),
Purpose: pulumi.String("IPSEC_INTERCONNECT"),
Address: pulumi.String("192.168.2.0"),
PrefixLength: pulumi.Int(29),
Network: network.SelfLink,
})
if err != nil {
return err
}
attachment2, err := compute.NewInterconnectAttachment(ctx, "attachment2", &compute.InterconnectAttachmentArgs{
Name: pulumi.String("test-interconnect-attachment2"),
EdgeAvailabilityDomain: pulumi.String("AVAILABILITY_DOMAIN_2"),
Type: pulumi.String("PARTNER"),
Router: router.ID(),
Encryption: pulumi.String("IPSEC"),
IpsecInternalAddresses: pulumi.StringArray{
address2.SelfLink,
},
})
if err != nil {
return err
}
_, err = compute.NewHaVpnGateway(ctx, "vpn-gateway", &compute.HaVpnGatewayArgs{
Name: pulumi.String("test-ha-vpngw"),
Network: network.ID(),
VpnInterfaces: compute.HaVpnGatewayVpnInterfaceArray{
&compute.HaVpnGatewayVpnInterfaceArgs{
Id: pulumi.Int(0),
InterconnectAttachment: attachment1.SelfLink,
},
&compute.HaVpnGatewayVpnInterfaceArgs{
Id: pulumi.Int(1),
InterconnectAttachment: attachment2.SelfLink,
},
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var network = new Gcp.Compute.Network("network", new()
{
Name = "test-network",
AutoCreateSubnetworks = false,
});
var address1 = new Gcp.Compute.Address("address1", new()
{
Name = "test-address1",
AddressType = "INTERNAL",
Purpose = "IPSEC_INTERCONNECT",
IPAddress = "192.168.1.0",
PrefixLength = 29,
Network = network.SelfLink,
});
var router = new Gcp.Compute.Router("router", new()
{
Name = "test-router",
Network = network.Name,
EncryptedInterconnectRouter = true,
Bgp = new Gcp.Compute.Inputs.RouterBgpArgs
{
Asn = 16550,
},
});
var attachment1 = new Gcp.Compute.InterconnectAttachment("attachment1", new()
{
Name = "test-interconnect-attachment1",
EdgeAvailabilityDomain = "AVAILABILITY_DOMAIN_1",
Type = "PARTNER",
Router = router.Id,
Encryption = "IPSEC",
IpsecInternalAddresses = new[]
{
address1.SelfLink,
},
});
var address2 = new Gcp.Compute.Address("address2", new()
{
Name = "test-address2",
AddressType = "INTERNAL",
Purpose = "IPSEC_INTERCONNECT",
IPAddress = "192.168.2.0",
PrefixLength = 29,
Network = network.SelfLink,
});
var attachment2 = new Gcp.Compute.InterconnectAttachment("attachment2", new()
{
Name = "test-interconnect-attachment2",
EdgeAvailabilityDomain = "AVAILABILITY_DOMAIN_2",
Type = "PARTNER",
Router = router.Id,
Encryption = "IPSEC",
IpsecInternalAddresses = new[]
{
address2.SelfLink,
},
});
var vpn_gateway = new Gcp.Compute.HaVpnGateway("vpn-gateway", new()
{
Name = "test-ha-vpngw",
Network = network.Id,
VpnInterfaces = new[]
{
new Gcp.Compute.Inputs.HaVpnGatewayVpnInterfaceArgs
{
Id = 0,
InterconnectAttachment = attachment1.SelfLink,
},
new Gcp.Compute.Inputs.HaVpnGatewayVpnInterfaceArgs
{
Id = 1,
InterconnectAttachment = attachment2.SelfLink,
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.Network;
import com.pulumi.gcp.compute.NetworkArgs;
import com.pulumi.gcp.compute.Address;
import com.pulumi.gcp.compute.AddressArgs;
import com.pulumi.gcp.compute.Router;
import com.pulumi.gcp.compute.RouterArgs;
import com.pulumi.gcp.compute.inputs.RouterBgpArgs;
import com.pulumi.gcp.compute.InterconnectAttachment;
import com.pulumi.gcp.compute.InterconnectAttachmentArgs;
import com.pulumi.gcp.compute.HaVpnGateway;
import com.pulumi.gcp.compute.HaVpnGatewayArgs;
import com.pulumi.gcp.compute.inputs.HaVpnGatewayVpnInterfaceArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var network = new Network("network", NetworkArgs.builder()
.name("test-network")
.autoCreateSubnetworks(false)
.build());
var address1 = new Address("address1", AddressArgs.builder()
.name("test-address1")
.addressType("INTERNAL")
.purpose("IPSEC_INTERCONNECT")
.address("192.168.1.0")
.prefixLength(29)
.network(network.selfLink())
.build());
var router = new Router("router", RouterArgs.builder()
.name("test-router")
.network(network.name())
.encryptedInterconnectRouter(true)
.bgp(RouterBgpArgs.builder()
.asn(16550)
.build())
.build());
var attachment1 = new InterconnectAttachment("attachment1", InterconnectAttachmentArgs.builder()
.name("test-interconnect-attachment1")
.edgeAvailabilityDomain("AVAILABILITY_DOMAIN_1")
.type("PARTNER")
.router(router.id())
.encryption("IPSEC")
.ipsecInternalAddresses(address1.selfLink())
.build());
var address2 = new Address("address2", AddressArgs.builder()
.name("test-address2")
.addressType("INTERNAL")
.purpose("IPSEC_INTERCONNECT")
.address("192.168.2.0")
.prefixLength(29)
.network(network.selfLink())
.build());
var attachment2 = new InterconnectAttachment("attachment2", InterconnectAttachmentArgs.builder()
.name("test-interconnect-attachment2")
.edgeAvailabilityDomain("AVAILABILITY_DOMAIN_2")
.type("PARTNER")
.router(router.id())
.encryption("IPSEC")
.ipsecInternalAddresses(address2.selfLink())
.build());
var vpn_gateway = new HaVpnGateway("vpn-gateway", HaVpnGatewayArgs.builder()
.name("test-ha-vpngw")
.network(network.id())
.vpnInterfaces(
HaVpnGatewayVpnInterfaceArgs.builder()
.id(0)
.interconnectAttachment(attachment1.selfLink())
.build(),
HaVpnGatewayVpnInterfaceArgs.builder()
.id(1)
.interconnectAttachment(attachment2.selfLink())
.build())
.build());
}
}
resources:
vpn-gateway:
type: gcp:compute:HaVpnGateway
properties:
name: test-ha-vpngw
network: ${network.id}
vpnInterfaces:
- id: 0
interconnectAttachment: ${attachment1.selfLink}
- id: 1
interconnectAttachment: ${attachment2.selfLink}
attachment1:
type: gcp:compute:InterconnectAttachment
properties:
name: test-interconnect-attachment1
edgeAvailabilityDomain: AVAILABILITY_DOMAIN_1
type: PARTNER
router: ${router.id}
encryption: IPSEC
ipsecInternalAddresses:
- ${address1.selfLink}
attachment2:
type: gcp:compute:InterconnectAttachment
properties:
name: test-interconnect-attachment2
edgeAvailabilityDomain: AVAILABILITY_DOMAIN_2
type: PARTNER
router: ${router.id}
encryption: IPSEC
ipsecInternalAddresses:
- ${address2.selfLink}
address1:
type: gcp:compute:Address
properties:
name: test-address1
addressType: INTERNAL
purpose: IPSEC_INTERCONNECT
address: 192.168.1.0
prefixLength: 29
network: ${network.selfLink}
address2:
type: gcp:compute:Address
properties:
name: test-address2
addressType: INTERNAL
purpose: IPSEC_INTERCONNECT
address: 192.168.2.0
prefixLength: 29
network: ${network.selfLink}
router:
type: gcp:compute:Router
properties:
name: test-router
network: ${network.name}
encryptedInterconnectRouter: true
bgp:
asn: 16550
network:
type: gcp:compute:Network
properties:
name: test-network
autoCreateSubnetworks: false
The vpnInterfaces array maps gateway interfaces to Interconnect attachments, enabling IPsec encryption over dedicated connectivity. Each interface references an InterconnectAttachment configured with IPSEC encryption and internal IP addresses allocated for the IPSEC_INTERCONNECT purpose. The router must have encryptedInterconnectRouter enabled to support this configuration.
Beyond these examples
These snippets focus on specific HA VPN gateway features: basic gateway provisioning, dual-stack IPv4/IPv6 configuration, and encrypted Interconnect integration. They’re intentionally minimal rather than full VPN solutions.
The examples may reference pre-existing infrastructure such as VPC networks (examples create inline, but typically reference existing) and Cloud Interconnect partner connections for encrypted Interconnect scenarios. They focus on gateway configuration rather than complete VPN connectivity.
To keep things focused, common VPN patterns are omitted, including:
- VPN tunnel configuration (separate resource)
- Peer gateway setup and external VPN endpoints
- BGP session configuration for dynamic routing
- Custom gateway IP version settings (gatewayIpVersion)
These omissions are intentional: the goal is to illustrate how each gateway feature is wired, not provide drop-in VPN modules. See the HA VPN Gateway resource reference for all available configuration options.
Let's configure GCP HA VPN Gateways
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Configuration & Immutability
name, network, region, vpnInterfaces, description, params, gatewayIpVersion, and stackType. Changes to these require resource replacement.[a-z][-a-z0-9]*[a-z0-9]?.IP Protocol Support
stackType identifies which IP protocols are enabled on the gateway (IPV4_ONLY, IPV4_IPV6, or IPV6_ONLY), defaulting to IPV4_ONLY. gatewayIpVersion specifies the IP family of the gateway IPs for interfaces (IPV4 or IPV6), defaulting to IPV4. Both are immutable.stackType to IPV4_IPV6 when creating the gateway. You can also optionally set gatewayIpVersion to IPV6.Advanced Features
labels field is non-authoritative and only manages labels present in your configuration. Use effectiveLabels to see all labels on the resource in GCP, including those set by other clients and services.vpnInterfaces with interconnectAttachment references pointing to IPSEC-encrypted interconnect attachments, as shown in the encrypted interconnect example.Import & Management
projects/{{project}}/regions/{{region}}/vpnGateways/{{name}}, {{project}}/{{region}}/{{name}}, {{region}}/{{name}}, or just {{name}}.Using a different cloud?
Explore networking guides for other cloud providers: