Skip to content

Virtual Network Gateway

Virtual Network Gateway

Azure Virtual Network Gateway provides encrypted connectivity between Azure virtual networks and on-premises networks (VPN Gateway) or dedicated private connections (ExpressRoute Gateway). The Virtual Network Gateway module allows you to create and configure gateways with BGP, active-active mode, point-to-site VPN, and custom routing.

Basic Configuration

Below is the most basic example of creating a VPN gateway:

virtual_network_gateway = {
    vpn01 = {
        resource_group = "hub"
        type           = "Vpn"
        sku            = "VpnGw1AZ"

        ip_configuration = [{
            subnet    = "hub.GatewaySubnet"
            public_ip = "vpn01"
        }]
    }
}

This creates a VPN gateway with the following defaults: - VPN Type: RouteBased - Active-Active: Disabled - BGP: Disabled - IPSec Replay Protection: Enabled - Private IP Address Allocation: Dynamic - IP Configuration Name: vnetGatewayConfig - Location: Uses the location defined in the main configuration

Advanced Configuration

The following example demonstrates a VPN gateway with BGP enabled and an ExpressRoute gateway in the same configuration:

virtual_network_gateway = {
    vpn01 = {
        resource_group = "hub"
        type           = "Vpn"
        sku            = "VpnGw2AZ"
        generation     = "Generation2"
        vpn_type       = "RouteBased"

        location = "centralus" #Override default location for DR resource

        active_active = true
        bgp_enabled   = true

        bgp_settings = {
            asn         = 65515 #CHANGEME - this is Azure default value and should not be used in production deployments
            peer_weight = 0
            peering_addresses = [
                {
                    ip_configuration_name = "activeConfig"
                    apipa_addresses       = ["169.254.21.1"]
                },
                {
                    ip_configuration_name = "standbyConfig"
                    apipa_addresses       = ["169.254.21.5"]
                }
            ]
        }

        ip_configuration = [
            {
                name      = "activeConfig"
                subnet    = "hub.GatewaySubnet"
                public_ip = "vpn01-active"
            },
            {
                name      = "standbyConfig"
                subnet    = "hub.GatewaySubnet"
                public_ip = "vpn01-standby"
            }
        ]

        custom_route = {
            address_prefixes = ["10.100.0.0/16"]
        }

        tags = {
            environment = "dr"
        }
    }

    er01 = {
        resource_group = "hub"
        type           = "ExpressRoute"
        sku            = "ErGw1AZ"

        ip_configuration = [{
            name   = "default"
            subnet = "hub.GatewaySubnet"
        }]
    }
}

Configuration Parameters

Parameter Type Required Default Description
resource_group string Yes - Key of the resource group where the gateway is deployed
type string Yes - Gateway type: Vpn or ExpressRoute
sku string Yes - Gateway SKU (e.g. VpnGw1AZ, VpnGw2AZ, ErGw1AZ, Standard)
ip_configuration list(object) Yes - One or more IP configuration blocks (see IP Configuration)
name string No Auto-generated Custom name. If not specified, uses naming convention
location string No Global location Azure region where the gateway is created
vpn_type string No RouteBased VPN routing type: RouteBased or PolicyBased
generation string No null Gateway generation: Generation1, Generation2, or None
active_active bool No false Enables active-active mode (requires two ip_configuration entries and two public IPs)
bgp_enabled bool No false Enables BGP on the gateway
bgp_settings object No null BGP configuration (see BGP Settings)
bgp_route_translation_for_nat_enabled bool No false Enables BGP route translation for NAT
custom_route object No null Custom routes to advertise; specify address_prefixes
default_local_network_gateway_id string No null Resource ID of the default local network gateway for forced tunneling
dns_forwarding_enabled bool No null Enables DNS forwarding on the gateway
edge_zone string No null Edge zone for the gateway
ip_sec_replay_protection_enabled bool No true Enables IPSec replay protection
policy_group list(object) No null Policy groups for per-user VPN (see Policy Groups)
private_ip_address_enabled bool No null Enables private IP on the gateway
remote_vnet_traffic_enabled bool No false Enables remote VNet traffic
virtual_wan_traffic_enabled bool No false Enables Virtual WAN traffic
vpn_client_configuration object No null Point-to-site VPN settings (see VPN Client Configuration)
timeouts object No null Custom Terraform operation timeouts (create, read, update, delete)
tags map(string) No {} Tags merged with default tags

IP Configuration

Each entry in ip_configuration maps to one gateway IP configuration block. At minimum, provide a subnet key pointing to the GatewaySubnet.

ip_configuration = [
    {
        name                          = "vnetGatewayConfig" # Default if omitted
        private_ip_address_allocation = "Dynamic"           # Default if omitted
        subnet                        = "hub.GatewaySubnet" # Required — key from the subnet variable
        public_ip                     = "vpn01"             # Optional — key from the public_ip variable
    }
]
Field Type Required Default Description
name string No vnetGatewayConfig Name for this IP configuration
private_ip_address_allocation string No Dynamic Private IP allocation method: Dynamic or Static
subnet string Yes - Key of the subnet entry (must be the GatewaySubnet)
public_ip string No null Key of the public IP entry. Required for VPN gateways; DO NOT DEFINE for ExpressRoute

Active-active mode requires two ip_configuration entries with distinct names and two separate public IPs.

BGP Settings

BGP settings are optional but required when bgp_enabled = true is set for production deployments.

bgp_settings = {
    asn         = 65515 #CHANGEME - this is Azure default value and should not be used in production deployments
    peer_weight = 0

    peering_addresses = [
        {
            ip_configuration_name = "vnetGatewayConfig"
            apipa_addresses       = ["169.254.21.1"] # APIPA range: 169.254.21.0 - 169.254.22.255
        }
    ]
}
Field Type Required Description
asn number No BGP autonomous system number for the Azure gateway. Azure default is 65515 and also reserves 65518, 65519, 65520, 4294967293, 4294967294. Avoid all when setting for production deployments
peer_weight number No Weight added to BGP routes learned from this peer
peering_addresses list(object) No Per-IP-configuration BGP peering address overrides
peering_addresses[].ip_configuration_name string No Name of the IP configuration this peering address applies to
peering_addresses[].apipa_addresses list(string) No Custom APIPA BGP addresses (must be in 169.254.21.0/24 or 169.254.22.0/24)

VPN Client Configuration

Point-to-site VPN configuration. Supports Azure AD, certificate, and RADIUS authentication.

vpn_client_configuration = {
    address_space      = ["172.16.100.0/24"]
    vpn_client_protocols = ["OpenVPN"]
    vpn_auth_types       = ["AAD"]

    aad_tenant   = "https://login.microsoftonline.com/<TENANT_ID>/"
    aad_audience = "41b23e61-6c1e-4545-b367-cd054e0ed4b4" # Azure VPN app ID
    aad_issuer   = "https://sts.windows.net/<TENANT_ID>/"
}

Certificate authentication:

vpn_client_configuration = {
    address_space        = ["172.16.100.0/24"]
    vpn_client_protocols = ["IkeV2", "SSTP"]
    vpn_auth_types       = ["Certificate"]

    root_certificate = [
        {
            name             = "VPNRootCert"
            public_cert_data = "<base64-encoded-cert-data>"
        }
    ]

    revoked_certificate = [
        {
            name       = "RevokedCert"
            thumbprint = "<thumbprint>"
        }
    ]
}
Field Type Required Description
address_space list(string) Yes IP address pool for VPN clients
vpn_client_protocols list(string) No Protocols: IkeV2, OpenVPN, SSTP
vpn_auth_types list(string) No Auth types: AAD, Certificate, Radius
aad_tenant string No AAD tenant URL (required for AAD auth)
aad_audience string No AAD application audience (required for AAD auth)
aad_issuer string No AAD issuer URL (required for AAD auth)
radius_server_address string No Primary RADIUS server address
radius_server_secret string No Primary RADIUS server secret
radius_server list(object) No Multiple RADIUS servers with address, secret, score
root_certificate list(object) No Trusted root certificates for certificate auth
revoked_certificate list(object) No Revoked client certificates
ipsec_policy object No Custom IPSec policy for the VPN client
virtual_network_gateway_client_connection list(object) No Per-policy-group client connections

Policy Groups

Policy groups enable per-user or per-group VPN routing policies for point-to-site scenarios.

policy_group = [
    {
        name       = "DevGroup"
        is_default = true
        priority   = 0
        policy_member = [
            {
                name  = "devuser"
                type  = "AADGroupId"
                value = "<AAD-GROUP-OBJECT-ID>"
            }
        ]
    }
]
Field Type Required Default Description
name string Yes - Name of the policy group
is_default bool No false Whether this is the default policy group
priority number No 0 Priority of the policy group
policy_member list(object) Yes - Members of the policy group
policy_member[].name string Yes - Name for this policy member
policy_member[].type string Yes - Member type: AADGroupId, CertificateGroupId, RadiusAzureGroupId
policy_member[].value string Yes - Value for the member type (e.g. AAD group object ID)

Naming Convention

Gateway names are automatically generated using the following pattern:

{name_prefix}virtual_network_gateway{key}{name_suffix}virtual_network_gateway

For example, with the following prefixes and suffixes:

name_prefixes = {
    virtual_network_gateway = "vgw-connect-"
}

name_suffixes = {
    virtual_network_gateway = "-westus2"
}

virtual_network_gateway = {
    vpn01 = {
        resource_group = "hub"
        type           = "Vpn"
        sku            = "VpnGw1AZ"
        ip_configuration = [{
            subnet    = "hub.GatewaySubnet"
            public_ip = "vpn01"
        }]
    }
}

The resulting gateway name would be: vgw-connect-vpn01-westus2

To override automatic naming, specify a custom name:

virtual_network_gateway = {
    vpn01 = {
        name           = "my-custom-vpn-gateway"
        resource_group = "hub"
        type           = "Vpn"
        sku            = "VpnGw1AZ"
        ip_configuration = [{
            subnet    = "hub.GatewaySubnet"
            public_ip = "vpn01"
        }]
    }
}

Gateway Types and SKUs

VPN Gateway SKUs:

SKU Max Throughput Max S2S Tunnels Supports Active-Active Generation
VpnGw1 / VpnGw1AZ 650 Mbps 30 Yes Generation1
VpnGw2 / VpnGw2AZ 1 Gbps 30 Yes Generation1/2
VpnGw3 / VpnGw3AZ 1.25 Gbps 30 Yes Generation1/2
VpnGw4 / VpnGw4AZ 5 Gbps 100 Yes Generation2
VpnGw5 / VpnGw5AZ 10 Gbps 100 Yes Generation2

SKUs ending in AZ are zone-redundant and recommended for production. Basic SKU is legacy and not recommended for new deployments.

ExpressRoute Gateway SKUs: Standard, HighPerformance, UltraPerformance, ErGw1AZ, ErGw2AZ, ErGw3AZ

Best Practices

Shared

  1. Zone-Redundant SKUs: Use AZ-suffixed SKUs (VpnGw1AZ, ErGw1AZ) for production to protect against zone failures
  2. GatewaySubnet: Always use a dedicated subnet named GatewaySubnet with a minimum /27 prefix; do not place other resources in it
  3. Separate Gateways per Type: Deploy VPN and ExpressRoute gateways as separate resources, even when colocating in the same VNet

VPN Gateway

  1. BGP over Static Routes: Enable BGP (bgp_enabled = true) for dynamic routing — required for active-active and more resilient failover
  2. Unique ASNs: Ensure the Azure gateway ASN (bgp_settings.asn) differs from your on-premises device ASN; avoid reserved ranges 65515–65520
  3. Active-Active for HA: Use active_active = true with two ip_configuration entries and two public IPs; pair with BGP for full redundancy
  4. Generation2 for Performance: Use generation = "Generation2" with VpnGw2AZ or higher for improved throughput and lower latency
  5. APIPA for BGP: Configure peering_addresses[].apipa_addresses in the 169.254.21.0/24 or 169.254.22.0/24 range to avoid BGP peer IP conflicts with address space

ExpressRoute Gateway

  1. Right-Size the SKU: Match the SKU to your circuit bandwidth — ErGw1AZ supports up to 1 Gbps, ErGw2AZ up to 2 Gbps, ErGw3AZ up to 10 Gbps; under-sizing causes dropped traffic at the gateway, not at the circuit
  2. FastPath for High Throughput: For UltraPerformance or ErGw3AZ SKUs, enable ExpressRoute FastPath on the connection resource to bypass the gateway data plane for latency-sensitive workloads
  3. Redundant Circuits: Use two ExpressRoute circuits from different peering locations for resilience; a single circuit with a single gateway is a single point of failure even with a zone-redundant SKU
  4. Private Peering Only: Set private_ip_address_enabled = false (default) unless Microsoft peering is explicitly required; private peering is the standard path for hybrid connectivity
  5. Co-exist with VPN: When deploying both an ExpressRoute and VPN gateway in the same VNet for failover, both must be in the same GatewaySubnet — size it /27 or larger to accommodate both gateway IPs