Azure Networking

How to Build a Site-to-Site VPN to Azure: IPsec/IKE Policies, Tunnels and On-Prem Config

Your on-premises data centre runs the ERP, file servers and a SQL cluster that new Azure workloads need to reach over private IPs — no public exposure, no exceptions from the security team. The answer they’ll sign off on is a Site-to-Site (S2S) VPN: an encrypted IPsec/IKE tunnel between your on-prem edge device and an Azure VPN Gateway, carrying traffic between your corporate 10.x space and your Azure VNet 10.y space across the public internet as if the two were one routed network. It’s the workhorse of hybrid connectivity — cheaper than ExpressRoute, provisioned in under an hour, good for steady tens-to-hundreds of Mbps per tunnel.

The trouble is that an S2S VPN has two ends, and Azure controls only one. Half the config lives in the portal or az; the other half lives on a Cisco ASA, Palo Alto, FortiGate or strongSwan box a different team owns. The two ends must agree, byte for byte, on the pre-shared key, IKE version, encryption and integrity algorithms, Diffie-Hellman group, SA lifetimes and — the part that silently breaks more tunnels than anything else — the address prefixes each side may send. Get one wrong and the tunnel never comes up, or comes up and drops traffic, with the error buried in an IKE log on a device you may not have access to.

This guide builds the whole thing end to end and teaches you to reason about every knob. You will stand up a VPN Gateway with the right GatewaySubnet, create a local network gateway describing your on-prem side, wire a connection with a strong custom IPsec/IKE policy, and configure the matching on-prem device — in the portal, in az, and as repeatable Bicep. You will choose route-based over policy-based, decide between static routing and BGP, size the SKU against real throughput and tunnel limits, and walk away with a validation checklist and a phase-1/phase-2 troubleshooting playbook. By the end the tunnel is not a black box — it is a system you can debug at 2 a.m.

What problem this solves

Without a private link, Azure workloads can only reach on-prem services over the public internet — so you either expose them publicly (a firewall and audit nightmare) or copy data into Azure and live with it going stale. Neither survives a security review for regulated data, so teams resort to brittle reverse proxies, inbound holes for specific IPs, or batch exports — all workarounds for the absence of a routed private path.

What breaks without it is concrete: an App Service app needing on-prem AD for LDAP can’t reach a domain controller; an Azure Data Factory pipeline can’t pull from the on-prem warehouse; a lift-and-shift VM expects its old 10.50.x database one hop away and times out. The S2S VPN makes “the network is flat between on-prem and Azure” true. It is the default first hybrid-connectivity build for nearly every migration — fast and inexpensive, with ExpressRoute reserved for guaranteed bandwidth or a non-internet path later.

The moving-parts inventory — the four Azure objects you create plus the one device you don’t own, and what each is responsible for:

Component Where it lives What it represents Who owns/configures it Break it and…
VPN Gateway Azure VNet (GatewaySubnet) Azure’s encryption endpoint You (Azure) No tunnel at all; or wrong SKU caps throughput
GatewaySubnet A reserved subnet in the VNet The IP range the gateway VMs use You (Azure) Deployment fails or future SKUs can’t grow
Local network gateway (LNG) Azure resource group A description of your on-prem side You (Azure) Azure can’t route to on-prem prefixes
Connection Azure resource group The tunnel binding gateway↔LNG + PSK + policy You (Azure) Mismatched policy → tunnel won’t negotiate
On-prem VPN device Your data centre / branch The other IPsec endpoint Network team (not Azure) Phase 1/2 mismatch → tunnel down or flapping

Learning objectives

By the end of this article you can:

Prerequisites & where this fits

You should already be comfortable with the Azure networking basics: a virtual network (VNet) is your private address space in Azure, carved into subnets, and resources get private IPs from those subnets. You should understand CIDR notation and non-overlapping address planning — the single most important precondition for any VPN, covered in depth in Azure VNet IP Address Planning: CIDR & Subnetting. You should know how to run az in Cloud Shell or locally and read JSON output. The concrete checklist before you start:

Prerequisite Why it’s needed How to confirm
Azure subscription + Network Contributor on the RG Create gateways, LNGs, connections az role assignment list --assignee <you>
Non-overlapping Azure and on-prem CIDRs Overlapping ranges cannot be routed Compare VNet prefix vs on-prem prefix
A VNet with room for a GatewaySubnet (/27+) The gateway VMs need reserved space az network vnet show address space
On-prem device’s public IP (static) The tunnel destination for the LNG Ask the network team / check the WAN IP
Admin access to the on-prem firewall To configure the matching IPsec policy You or a teammate owns the device
Agreed IPsec/IKE policy + PSK Both ends must match every field Runbook with exact crypto fields

This sits in the Hybrid Networking track — the next step after VNets and NSGs from Azure Virtual Network, Subnets & NSGs, and the sibling of How to Configure Point-to-Site VPN with Microsoft Entra Authentication (same gateway, a per-user connection type instead of device-to-Azure). Once a VNet is connected you extend reach with VNet Peering, Gateway Transit & Global Peering, and when a tunnel misbehaves you fall back on Azure Network Watcher Connectivity Diagnostics. In an enterprise build the gateway lives in the hub of an Azure enterprise-scale landing zone.

A map of who confirms what when a tunnel misbehaves, so you escalate fast:

Layer What lives here Who usually owns it Failure classes it causes
On-prem device IPsec config, PSK, policy, routing Network / firewall team Phase 1/2 mismatch, tunnel down
Internet path UDP 500/4500, ESP, public IPs ISP / both edges Tunnel won’t establish; intermittent drops
VPN Gateway Azure endpoint, SKU, BGP You (Azure) Throughput cap, no BGP on Basic
Local network gateway On-prem prefixes + public IP You (Azure) Azure can’t route to on-prem; stale IP
Connection PSK, IPsec policy, prefixes You (Azure) Negotiation fails; selectors mismatch
VNet routing / NSG Effective routes, subnet rules You (Azure) Tunnel up but traffic blocked

Core concepts

Six mental models make every later decision obvious.

An S2S VPN is two IPsec endpoints agreeing on everything. IPsec negotiates in two phases: Phase 1 (the IKE SA) authenticates the peers with the pre-shared key and builds a secure channel; Phase 2 (the IPsec SA) negotiates the keys that encrypt your data packets. Both must match on encryption, integrity, Diffie-Hellman group and lifetime — Azure terms its tunable set the IPsec/IKE policy. A mismatch in any single field means the tunnel never comes up, and the error is logged on the device, not always surfaced cleanly in Azure. The two phases differ in what they negotiate and how they fail — the key to fast diagnosis:

Aspect IKE Phase 1 (IKE SA) IKE Phase 2 (IPsec SA)
Purpose Authenticate peers, build a secure channel Negotiate the keys that encrypt data packets
Authenticates with The pre-shared key (Inherits the phase-1 trust)
Negotiates Encryption, integrity, DH group, lifetime Encryption, integrity, PFS group, SA lifetime, selectors
Failure looks like Connection stuck Connecting; “no proposal chosen” Tunnel “Connecting” after phase 1 succeeds
Most common cause PSK mismatch, DH-group mismatch Traffic-selector / proxy-ID mismatch
Cisco / device term crypto ikev2 proposal crypto ipsec transform-set / proxy IDs

The gateway is a managed pair of VMs in a reserved subnet. Azure deploys (at least) two VMs into a subnet that must be named GatewaySubnet — the name is load-bearing. You don’t see, patch or SSH them; you see only the gateway resource and its public IP(s). Because they’re real VMs, creation takes 30–45 minutes — the longest wait in this build. Size the GatewaySubnet at least /27 (/26 recommended) so the SKU can grow and coexist with ExpressRoute later.

Route-based, not policy-based, for anything new. A route-based gateway (routing table; multiple tunnels, BGP, active-active, IKEv2) is the right choice over policy-based (static ACL; one tunnel, IKEv1, Basic only) for essentially every build. The --vpn-type is fixed at creation — the dedicated section below covers the full comparison and the selector nuance.

The local network gateway is “on-prem, described to Azure.” Despite the name, the local network gateway (LNG) is not a device — it’s an Azure object telling Azure two things about your on-prem side: the public IP of your VPN device (the tunnel destination) and the prefixes behind it (what to send down the tunnel, for static routing). Grow a new on-prem subnet and you must add its prefix to the LNG — or use BGP to advertise routes dynamically and skip the list.

Resilience is built from tunnels, not single connections. A single gateway gives one tunnel to one device — a single point of failure on both ends. The 99.95% SLA (non-Basic SKUs) assumes redundant tunnels, not a lone link. Plan the topology first, because converting to active-active touches the public IP config. The HA designs, least to most resilient:

Topology Azure public IPs On-prem devices Tunnels Survives
Single (active-standby) 1 1 1 Nothing (single point of failure)
Active-active (Azure) 2 1 2 One Azure gateway instance failing
Active-active → two devices 2 2 2 One Azure instance or one on-prem device
Dual-redundancy 2 2 4 Any single instance or device, both ends

The vocabulary in one table

Every term, side by side, before the deep sections (the glossary repeats these for lookup):

Term One-line definition Where it lives Why it matters
VPN Gateway Azure’s managed IPsec endpoint (≥2 VMs) GatewaySubnet in the VNet The Azure side of every tunnel
GatewaySubnet The reserved, exactly-named subnet The VNet Wrong name/size → deploy fails
Local network gateway (LNG) On-prem described to Azure (IP + prefixes) A resource group Wrong prefixes → no on-prem routing
Connection Binds gateway↔LNG with PSK + policy A resource group The tunnel object you toggle/debug
Pre-shared key (PSK) Shared secret authenticating both ends On the connection + on-prem device Mismatch → phase 1 fails
IKE Phase 1 (IKE SA) Authenticates peers, builds secure channel IPsec negotiation Mismatch → “no proposal chosen”
IKE Phase 2 (IPsec SA) Negotiates the data-encryption keys IPsec negotiation Mismatch → tunnel “connecting” forever
IPsec/IKE policy Azure’s tunable crypto parameter set On the connection Custom policy = lock down ciphers
Traffic selectors Prefix pairs the tunnel will carry Phase 2 Mismatch → no traffic flows
Route-based gateway Routing-table tunnel; BGP/multi-tunnel Gateway type (fixed at create) The right choice for new builds
BGP Dynamic route exchange over the tunnel Gateway + LNG + connection Auto-propagate prefixes; faster failover
APIPA 169.254.x.x BGP peer IPs over VPN BGP config Required for some on-prem BGP peers

Route-based vs policy-based: choose once, live with it

This is the first irreversible decision. The gateway’s vpnType (RouteBased or PolicyBased) is set at creation and cannot be changed; switching means deleting the gateway (another 30–45-minute rebuild) and every connection on it.

Capability Route-based Policy-based
Tunnels supported Many (multi-site, multiple LNGs) Exactly one
IKE version IKEv2 (and IKEv1) IKEv1 only
BGP Yes No
Active-active gateway Yes No
ExpressRoute coexistence Yes No
Point-to-Site coexistence Yes No
Supported SKUs VpnGw1VpnGw5 (+AZ), Basic Basic only
Custom IPsec/IKE policy Yes (full control) Limited
Traffic selectors Any-to-any by default (narrowable) Specific by definition
When to choose Almost always Only a legacy peer that requires it

The practical read: choose route-based unless a legacy peer’s vendor explicitly cannot do route-based / IKEv2. It unlocks everything else here — strong custom policy, BGP, redundancy, growing from one site to many without touching the gateway. For one stubborn legacy peer, the middle path is a route-based gateway with --use-policy-based-traffic-selectors true on that single connection — the specific selectors it wants, a modern gateway everywhere else.

Sizing the gateway SKU

The SKU sets aggregate throughput, tunnel count, BGP support and the SLA. Unlike the gateway type, you can resize within the same generation later (VpnGw1VpnGw2), so size for today and plan to bump it. Two reading notes: throughput is aggregate across all tunnels (not per tunnel), and a single tunnel is practically capped at the low hundreds of Mbps regardless of SKU because one flow over one IPsec tunnel is the limit.

SKU Aggregate VPN throughput (approx) S2S tunnels (max) BGP Zone-redundant Typical fit
Basic ~100 Mbps 10 No No Dev/test only; avoid in prod (no BGP, no SLA on HA)
VpnGw1 / VpnGw1AZ ~650 Mbps 30 Yes AZ variant only Small prod, single site
VpnGw2 / VpnGw2AZ ~1 Gbps 30 Yes AZ variant only Mid prod, multi-site
VpnGw3 / VpnGw3AZ ~1.25 Gbps 30 Yes AZ variant only Larger prod, busy hub
VpnGw4 / VpnGw4AZ ~5 Gbps 100 Yes AZ variant only High-throughput hub
VpnGw5 / VpnGw5AZ ~10 Gbps 100 Yes AZ variant only Max VPN; consider ExpressRoute instead

Hard rules: don’t run Basic in production (no BGP, no active-active — no HA story). Choose an AZ SKU (VpnGw1AZ+) where zones exist for zone-redundancy — modest premium, the production default. Past a few Gbps sustained or with strict latency needs, evaluate ExpressRoute rather than chasing VpnGw5. The 99.95% SLA (non-Basic) still assumes redundant tunnels.

Designing the IPsec/IKE policy

By default Azure negotiates from a broad interoperability list that includes weak combinations (it accepts SHA-1 and DH Group 2 if the peer offers them first). For real data, define a custom IPsec/IKE policy on the connection. A custom policy is all-or-nothing: Azure then proposes only that single combination, so the on-prem device must match it precisely — no fallback.

The field-by-field menu, with a strong modern recommendation. AES-GCM is an AEAD cipher (encryption + integrity together), so for IPsec the integrity is implied — but IKE phase 1 still needs its own integrity choice.

Policy field Phase Azure-accepted values Strong recommendation Notes
IKE encryption 1 AES256, AES192, AES128, GCMAES256, GCMAES128, DES, DES3 GCMAES256 Avoid DES/3DES (legacy, weak)
IKE integrity 1 SHA384, SHA256, SHA1, MD5, GCMAES256, GCMAES128 SHA384 Never SHA1/MD5 for new builds
DH Group 1 DHGroup24, ECP384, ECP256, DHGroup14, DHGroup2, DHGroup1, None DHGroup24 Group 1/2 are weak; ECP for ECDH peers
IPsec encryption 2 GCMAES256, GCMAES128, AES256/192/128, DES, DES3, None GCMAES256 GCM = AEAD; integrity implied
IPsec integrity 2 GCMAES256, GCMAES128, SHA256, SHA1, MD5 GCMAES256 Must pair with the GCM cipher
PFS Group 2 PFS24, ECP384, ECP256, PFS14, PFS2, PFS1, PFSMM, None PFS24 Perfect Forward Secrecy; rekey uses fresh DH
SA lifetime (sec) 2 300 – 172,799 seconds 27000 (7.5 h) Time before phase-2 rekey
SA data size (KB) 2 1024 – 2,147,483,647 KB; 0 = no limit 102400000 Rekey after N KB; many peers ignore
DPD timeout (sec) 9 – 3,600 seconds 45 Dead-peer detection on the connection

Two cautions. GCMAES for IPsec must use the same key length for encryption and integrity (both 256 or both 128) — mixing is rejected. And SA lifetimes need not match exactly (each side rekeys on its own shorter timer), but wildly different values cause confusing renegotiations, so align them. The DH/PFS group is the field most likely to differ in vendors’ naming — confirm the numeric group, not the label.

Recommended starting policy

A strong, broadly-interoperable policy most modern firewalls (Palo Alto, FortiGate, recent Cisco) support: IKEv2, phase 1 GCMAES256 / SHA384 / DHGroup24, phase 2 GCMAES256 / PFS24, SA lifetime 27000 s. If a peer can’t do Group 24 or GCM, the common fallback is AES256 / SHA256 / DHGroup14 both phases — still strong, near-universal. Whatever you choose, document every field in your runbook; “we think it’s AES-256” is how a 2 a.m. negotiation failure starts.

Static routing vs BGP

The last fork is how Azure learns which prefixes live on-prem. Static routing means you list the on-prem prefixes on the LNG by hand; Azure installs routes to them via the tunnel. BGP means the two gateways peer across the tunnel and exchange prefixes dynamically — each advertises its networks and both adjust automatically when a network is added or a tunnel fails.

Aspect Static routing BGP
On-prem prefixes Listed by hand on the LNG Advertised dynamically
Adding a new on-prem subnet Edit the LNG manually Automatic (re-advertised)
Failover speed (multi-tunnel) Slow; relies on re-config Fast; BGP withdraws/repaths
Transit / multi-site routing Manual and fragile Natural and scalable
SKU requirement Any (incl. Basic) VpnGw1+ (not Basic)
Extra config None ASN + BGP peer IP each side
Overlap safety You control prefixes exactly Watch for advertising overlaps
When to choose One site, stable prefixes Multi-site, ExpressRoute coexist, churn

For a single site with stable subnets, static routing is simpler and perfectly fine — no ASN to coordinate. Choose BGP for multiple tunnels (real failover), an evolving address plan (no manual LNG edits), or ExpressRoute coexistence. BGP needs an ASN per side — Azure’s default is 65515; your on-prem side picks a private ASN (6451265534, avoiding 65515). Azure assigns each gateway a BGP peer IP from the GatewaySubnet, or you use APIPA (169.254.21.x) for devices that require a link-local peer. The lab uses static routing, then shows the BGP add-ons.

Architecture at a glance

Follow the path left to right. A workload VM in the Azure VNet sends a packet to an on-prem address (10.50.10.20). The VNet’s route table — populated by the gateway and the LNG’s prefix list — marks that destination reachable via the virtual network gateway, so the packet is steered to the VPN Gateway in the reserved GatewaySubnet (10.10.255.0/27). The gateway encrypts it under the negotiated IPsec SA and sends it wrapped in ESP over UDP 4500 (NAT-traversal) or UDP 500 (IKE) across the internet to the on-prem device’s public IP. That device — the peer described by the local network gateway object — decrypts and delivers onto the corporate 10.50.0.0/16 network. Return traffic reverses: the on-prem device matches Azure’s 10.10.0.0/16, encrypts, and the Azure gateway decrypts into the VNet.

The diagram marks the five places this path breaks — the GatewaySubnet (badge 1, wrong name → no deploy), the connection (badge 2, PSK/policy mismatch → phase 1 stalls), the internet path (badge 3, missing UDP/ESP → no negotiation), the LNG (badge 4, stale IP/missing prefix → no routing), and on-prem (badge 5, selector mismatch → “connecting” forever). The legend narrates each as symptom · confirm · fix, and the troubleshooting playbook refers back to these exact hops.

Left-to-right Site-to-Site IPsec VPN architecture: an Azure VNet workload subnet routes on-prem-bound traffic through a route-based VPN Gateway in a reserved GatewaySubnet, which encrypts under an IKEv2 IPsec SA and sends ESP over UDP 500/4500 across the internet to an on-prem firewall described by a local network gateway, delivering onto the corporate network; numbered badges mark the GatewaySubnet, connection PSK/policy, internet UDP/ESP path, local network gateway prefixes, and on-prem traffic-selector failure points.

Real-world scenario

Meridian Logistics runs a 14-year-old on-prem stack in a Mumbai data centre — Oracle ERP, a Windows file cluster and Active Directory on 10.50.0.0/16 behind a pair of FortiGates at 203.0.113.40. They were migrating a tracking portal to Azure App Service in Central India, but it needs LDAP auth against on-prem AD and read access to an Oracle replica — both private-only, and the security team’s rule was absolute: nothing on 10.50.x gets a public listener. ExpressRoute meant a multi-month lead time and five-figure monthly cost they couldn’t justify for ~80 Mbps. So: Site-to-Site VPN.

The architect, Priya, planned the address space first and caught the landmine early — the Azure VNet had been drafted as 10.50.0.0/16, directly overlapping the on-prem range. Overlap makes a VPN impossible to route, so she re-IP’d Azure to 10.10.0.0/16 with a GatewaySubnet of 10.10.255.0/27, deployed a VpnGw1AZ route-based gateway, and created an LNG for 203.0.113.40 (prefix 10.50.0.0/16) and a connection with a custom policy: IKEv2, GCMAES256 / SHA384 / DHGroup24 / PFS24.

The first attempt failed exactly as the playbook predicts. Phase 1 came up, phase 2 didn’t — stuck “Connecting.” The FortiGate had specific traffic selectors (10.50.0.0/16 ↔ 10.10.0.0/16) while Azure offered wildcards; setting --use-policy-based-traffic-selectors true brought the tunnel up in seconds. A week later, a DH-group mismatch (the reused FortiGate template defaulted phase 1 to Group 14, not 24) threw “no proposal chosen” in the FortiGate IKE log — they aligned on Group 24 and documented it.

Once stable, they hardened: the gateway went active-active, the team added a second FortiGate, and BGP repathed automatically on failure instead of black-holing. Six months on, the portal authenticates against on-prem AD, the Oracle replica is reachable privately, and the cost is a fraction of the ExpressRoute quote. The lessons that generalised: plan non-overlapping IP first, expect a phase-2 selector mismatch with picky firewalls, pin the exact crypto policy in the runbook, and don’t ship a single tunnel to production.

Advantages and disadvantages

Weigh the S2S VPN against ExpressRoute and against doing nothing before committing.

Advantages Disadvantages
Fast to stand up (under an hour, minus gateway-create wait) Runs over the public internet — variable latency/jitter
Cheap relative to ExpressRoute (gateway-hour + egress) Throughput practically capped at low hundreds of Mbps per tunnel
No carrier/circuit lead time; you control both ends Two ends to keep in sync; on-prem device is yours to manage
Works with any IPsec-capable on-prem device No bandwidth guarantee; subject to internet weather
Encrypted end to end (IPsec) by design Gateway-create and resize operations are slow (30–45 min)
Multi-site, BGP, active-active on route-based SKUs Basic SKU is a trap (no BGP, weak HA) — must size up
Backstops/fails over for ExpressRoute Mismatched policy fails opaquely (errors live on the device)

When each side matters: the advantages dominate for hybrid integration, migrations, branch connectivity and dev/test — anywhere you need a private path quickly at tens-to-hundreds of Mbps. The disadvantages dominate when you need guaranteed multi-Gbps, predictable low latency, a contractually private path off the internet, or very large sustained data movement — ExpressRoute’s job, with the VPN as its failover. A common mature pattern is both: ExpressRoute primary, S2S VPN as automatic failover (which BGP makes clean).

Hands-on lab

This is the centrepiece. You will build a complete, working S2S VPN three ways. Because a real on-prem device is rarely available in a lab, the standard, fully-supported technique is a second Azure VNet with its own VPN Gateway, joined by a VNet-to-VNet IPsec tunnel — this exercises the exact same gateway, connection and IPsec/IKE machinery as an on-prem tunnel (same negotiation, SKUs and validation), both ends under your control. A final section maps each step to the equivalent on-prem-device config. Everything is teardown-able; the gateways bill per hour.

Cost warning: A VPN Gateway bills per hour while it exists, even idle — roughly ₹3–₹4/hour (~$0.04) for VpnGw1, and this lab uses two. Budget a few hundred rupees per day and run the teardown the moment you’re done. There is no free-tier VPN Gateway.

Here is the whole build at a glance — the nine steps, what each produces, and where the time goes:

Step What you do Key Azure object Time Validate
0 Set variables, create resource group resource group seconds az group createSucceeded
1 Two VNets, each with a GatewaySubnet virtual networks + subnets seconds subnet named GatewaySubnet exists
2 Two Standard public IPs public IP addresses seconds two IPs allocated
3 Two VPN Gateways (--no-wait) virtual network gateways 30–45 min both provisioningState = Succeeded
4 Two local network gateways local network gateways seconds correct peer IP + prefixes
5 Two connections + custom IPsec policy connections seconds policy applied
6 Check tunnel status + effective routes seconds both Connected; route via gateway
7 Two test VMs, ping across the tunnel virtual machines minutes private-to-private ICMP replies
8 (Optional) enable BGP, check learned routes gateway/connection BGP seconds remote prefix learned via EBgp
9 Teardown (delete RG) minutes az group existsfalse

Step 0 — Prerequisites and variables

You need an Azure subscription with Network Contributor on a resource group, and az (Cloud Shell is easiest). Set shell variables so every later command is copy-paste-safe.

# --- Lab variables (Bash / Cloud Shell) ---
LOCATION="centralindia"
RG="rg-s2s-vpn-lab"

# "Azure" side (stands in for your cloud VNet)
AZ_VNET="vnet-azure"; AZ_PREFIX="10.10.0.0/16"
AZ_WORKLOAD_SUBNET="snet-workload"; AZ_WORKLOAD_PREFIX="10.10.1.0/24"
AZ_GWSUBNET_PREFIX="10.10.255.0/27"          # GatewaySubnet — name is fixed, NOT the prefix
AZ_GW="vng-azure"; AZ_PIP="pip-vng-azure"

# "On-prem" side (a 2nd Azure VNet standing in for the data centre)
OP_VNET="vnet-onprem"; OP_PREFIX="10.50.0.0/16"
OP_WORKLOAD_SUBNET="snet-onprem"; OP_WORKLOAD_PREFIX="10.50.1.0/24"
OP_GWSUBNET_PREFIX="10.50.255.0/27"
OP_GW="vng-onprem"; OP_PIP="pip-vng-onprem"

SKU="VpnGw1"                                  # smallest BGP-capable SKU; resize later if needed
PSK="K1oudVin-S2S-Lab-$(openssl rand -hex 8)" # strong shared secret; same value both ends
echo "Pre-shared key for this lab: $PSK"

Expected output: the printed PSK. Save it — you set the identical value on both connections.

Create the resource group:

az group create --name "$RG" --location "$LOCATION" -o table

Expected output: a table row showing Succeeded for $RG.

Step 1 — Create both VNets with a GatewaySubnet each

The most common deploy failure is an absent or misnamed GatewaySubnet. Create each VNet with its workload subnet, then add the exactly-named GatewaySubnet.

# Azure-side VNet + workload subnet
az network vnet create -g "$RG" -n "$AZ_VNET" -l "$LOCATION" \
  --address-prefix "$AZ_PREFIX" \
  --subnet-name "$AZ_WORKLOAD_SUBNET" --subnet-prefix "$AZ_WORKLOAD_PREFIX" -o table

# The reserved subnet — the name MUST be exactly "GatewaySubnet"
az network vnet subnet create -g "$RG" --vnet-name "$AZ_VNET" \
  -n "GatewaySubnet" --address-prefix "$AZ_GWSUBNET_PREFIX" -o table

# On-prem-side VNet + workload subnet + GatewaySubnet
az network vnet create -g "$RG" -n "$OP_VNET" -l "$LOCATION" \
  --address-prefix "$OP_PREFIX" \
  --subnet-name "$OP_WORKLOAD_SUBNET" --subnet-prefix "$OP_WORKLOAD_PREFIX" -o table
az network vnet subnet create -g "$RG" --vnet-name "$OP_VNET" \
  -n "GatewaySubnet" --address-prefix "$OP_GWSUBNET_PREFIX" -o table

Expected output: four Succeeded rows. Validation: confirm both GatewaySubnets exist and are sized right:

az network vnet subnet show -g "$RG" --vnet-name "$AZ_VNET" -n GatewaySubnet \
  --query "{name:name, prefix:addressPrefix}" -o table

You should see GatewaySubnet at 10.10.255.0/27. If the name is anything else, the gateway create in the next step fails — always create it exactly as GatewaySubnet.

Step 2 — Create two public IPs

A route-based VPN Gateway needs a Standard public IP (required for AZ gateways, and the modern default).

az network public-ip create -g "$RG" -n "$AZ_PIP" -l "$LOCATION" \
  --sku Standard --allocation-method Static -o table
az network public-ip create -g "$RG" -n "$OP_PIP" -l "$LOCATION" \
  --sku Standard --allocation-method Static -o table

Expected output: two Succeeded rows. These IPs are the tunnel endpoints; you’ll reference them when building the local network gateways.

Step 3 — Create both VPN Gateways (the long wait)

The 30–45-minute step, per gateway. Start both in parallel with --no-wait so you wait once, not twice.

# Azure-side gateway (route-based, VpnGw1) — kick off and return immediately
az network vnet-gateway create -g "$RG" -n "$AZ_GW" -l "$LOCATION" \
  --vnet "$AZ_VNET" --public-ip-addresses "$AZ_PIP" \
  --gateway-type Vpn --vpn-type RouteBased --sku "$SKU" \
  --no-wait

# On-prem-side gateway — also in the background
az network vnet-gateway create -g "$RG" -n "$OP_GW" -l "$LOCATION" \
  --vnet "$OP_VNET" --public-ip-addresses "$OP_PIP" \
  --gateway-type Vpn --vpn-type RouteBased --sku "$SKU" \
  --no-wait

echo "Both gateways provisioning. This takes ~30-45 minutes."

Validation (wait for both): poll provisioning state until both read Succeeded. Do not proceed until they do.

# Poll both gateways' provisioning state
for GW in "$AZ_GW" "$OP_GW"; do
  echo -n "$GW: "
  az network vnet-gateway show -g "$RG" -n "$GW" --query provisioningState -o tsv
done
# Re-run until BOTH print "Succeeded"

Expected output (eventually): both lines print Succeeded. While you wait, grab the public IPs — you need them for the local network gateways:

AZ_GW_IP=$(az network public-ip show -g "$RG" -n "$AZ_PIP" --query ipAddress -o tsv)
OP_GW_IP=$(az network public-ip show -g "$RG" -n "$OP_PIP" --query ipAddress -o tsv)
echo "Azure GW public IP: $AZ_GW_IP"
echo "On-prem GW public IP: $OP_GW_IP"

Step 4 — Create the local network gateways (describe each “remote” side)

A local network gateway describes the other end: its public IP and prefixes. Each side sees the opposite VNet as remote, so you create two LNGs pointing at the opposite gateway’s IP and address space. In a real build, $OP_GW_IP is your on-prem firewall’s public IP and $OP_PREFIX your corporate address space.

# LNG describing the "on-prem" side, used by the Azure-side gateway's connection
az network local-gateway create -g "$RG" -n "lng-to-onprem" -l "$LOCATION" \
  --gateway-ip-address "$OP_GW_IP" \
  --local-address-prefixes "$OP_PREFIX" -o table

# LNG describing the "Azure" side, used by the on-prem-side gateway's connection
az network local-gateway create -g "$RG" -n "lng-to-azure" -l "$LOCATION" \
  --gateway-ip-address "$AZ_GW_IP" \
  --local-address-prefixes "$AZ_PREFIX" -o table

Expected output: two Succeeded rows. Validation: confirm prefixes and IPs — a stale IP or missing prefix here is Badge 4:

az network local-gateway show -g "$RG" -n "lng-to-onprem" \
  --query "{ip:gatewayIpAddress, prefixes:localNetworkAddressSpace.addressPrefixes}" -o jsonc

Step 5 — Author the custom IPsec/IKE policy and create both connections

Bind each gateway to its LNG with a connection carrying the identical PSK and a matched custom policy. Both ends are Azure here so the policy matches by construction; against a real device you’d mirror these fields on the firewall.

# Azure-side connection: gateway -> lng-to-onprem, with a strong custom IPsec/IKE policy
az network vpn-connection create -g "$RG" -n "cn-azure-to-onprem" -l "$LOCATION" \
  --vnet-gateway1 "$AZ_GW" --local-gateway2 "lng-to-onprem" \
  --shared-key "$PSK"

# Apply the custom policy (IKEv2 / GCMAES256 / SHA384 / DHGroup24 / PFS24)
az network vpn-connection ipsec-policy add -g "$RG" --connection-name "cn-azure-to-onprem" \
  --ike-encryption GCMAES256 --ike-integrity SHA384 --dh-group DHGroup24 \
  --ipsec-encryption GCMAES256 --ipsec-integrity GCMAES256 --pfs-group PFS24 \
  --sa-lifetime 27000 --sa-max-size 102400000

# On-prem-side connection: the mirror image, SAME PSK
az network vpn-connection create -g "$RG" -n "cn-onprem-to-azure" -l "$LOCATION" \
  --vnet-gateway1 "$OP_GW" --local-gateway2 "lng-to-azure" \
  --shared-key "$PSK"

az network vpn-connection ipsec-policy add -g "$RG" --connection-name "cn-onprem-to-azure" \
  --ike-encryption GCMAES256 --ike-integrity SHA384 --dh-group DHGroup24 \
  --ipsec-encryption GCMAES256 --ipsec-integrity GCMAES256 --pfs-group PFS24 \
  --sa-lifetime 27000 --sa-max-size 102400000

Expected output: the connections created, then the policy applied (the gateway briefly re-negotiates). Note: both connections must exist for the tunnel to come up — each side needs an IPsec endpoint actively negotiating. Against a real on-prem device you create only the Azure connection; the matching config lives on the firewall.

Step 6 — Validate the tunnel is connected

Check both connection statuses:

for CN in "cn-azure-to-onprem" "cn-onprem-to-azure"; do
  echo -n "$CN: "
  az network vpn-connection show -g "$RG" -n "$CN" \
    --query "{status:connectionStatus, ingressBytes:ingressBytesTransferred, egressBytes:egressBytesTransferred}" -o tsv
done

Expected output: both connections report Connected, with non-zero byte counters once traffic flows. If you see Connecting for more than a couple of minutes, jump to the troubleshooting playbook — it’s almost always a policy or selector mismatch (in this all-Azure lab the fields match by construction, so re-check that both connections exist and the PSKs are identical).

Deeper validation — effective routes. Prove the route to the remote prefix is installed via the gateway. Create a small workload VM and inspect its effective routes:

# (Optional) quick test VM on the Azure workload subnet
az vm create -g "$RG" -n "vm-test-azure" --image Ubuntu2204 \
  --vnet-name "$AZ_VNET" --subnet "$AZ_WORKLOAD_SUBNET" \
  --admin-username azureuser --generate-ssh-keys --public-ip-address "" --size Standard_B1s -o table

NIC_ID=$(az vm show -g "$RG" -n "vm-test-azure" --query "networkProfile.networkInterfaces[0].id" -o tsv)
az network nic show-effective-route-table --ids "$NIC_ID" \
  --query "value[?contains(addressPrefix, '10.50')].{prefix:addressPrefix, nextHop:nextHopType}" -o table

Expected output: a row with prefix 10.50.0.0/16 and next-hop VirtualNetworkGateway — proof Azure will steer on-prem-bound traffic into the tunnel.

Step 7 — End-to-end reachability test

Put a VM on each side and confirm private connectivity. Build the on-prem-side VM and ping the Azure-side VM by its private IP (open ICMP if you’ve added NSGs; default rules allow intra-tunnel traffic).

az vm create -g "$RG" -n "vm-test-onprem" --image Ubuntu2204 \
  --vnet-name "$OP_VNET" --subnet "$OP_WORKLOAD_SUBNET" \
  --admin-username azureuser --generate-ssh-keys --public-ip-address "" --size Standard_B1s -o table

AZ_VM_IP=$(az vm show -g "$RG" -n "vm-test-azure" -d --query privateIps -o tsv)
echo "Azure test VM private IP: $AZ_VM_IP"
# From vm-test-onprem (e.g. via Bastion or serial console), run:  ping $AZ_VM_IP

Expected output: ICMP replies between 10.10.1.x and 10.50.1.x across the IPsec tunnel — private-to-private, no public hop. If ping fails while the connection shows Connected, it’s routing/NSG, not the tunnel (Badge 6). Watch the Step 6 byte counters increment as traffic flows — live proof data is crossing.

Step 8 — (Optional) Convert to BGP

To exercise dynamic routing, enable BGP on both gateways and connections. BGP needs an ASN per side and advertises each gateway’s prefixes automatically.

# Enable BGP on each gateway with distinct private ASNs (avoid Azure's default 65515)
az network vnet-gateway update -g "$RG" -n "$AZ_GW" --asn 65010 -o none
az network vnet-gateway update -g "$RG" -n "$OP_GW" --asn 65020 -o none

# Discover each gateway's BGP peering address (from the GatewaySubnet)
az network vnet-gateway show -g "$RG" -n "$AZ_GW" \
  --query "bgpSettings.bgpPeeringAddresses[0].defaultBgpIpAddresses" -o tsv

# Turn BGP on for the connections
az network vpn-connection update -g "$RG" -n "cn-azure-to-onprem" --enable-bgp true -o none
az network vpn-connection update -g "$RG" -n "cn-onprem-to-azure" --enable-bgp true -o none

Validation: check learned routes — each gateway should learn the other’s VNet prefix via BGP:

az network vnet-gateway list-learned-routes -g "$RG" -n "$AZ_GW" \
  --query "value[].{network:network, origin:origin, asPath:asPath, nextHop:nextHop}" -o table

Expected output: the remote prefix (10.50.0.0/16) appears with origin EBgp. For a real on-prem peer you’d also set --bgp-peering-address (often an APIPA 169.254.21.x address) and the peer’s ASN on the LNG.

Step 9 — Teardown (do this when finished)

Gateways bill per hour, so tear the lab down completely. Deleting the resource group removes everything in one shot.

# Nuke the entire lab resource group (gateways, VNets, VMs, IPs, connections)
az group delete --name "$RG" --yes --no-wait

Expected output: the command returns immediately (--no-wait); deletion of the gateways takes several minutes in the background. Validation: confirm it’s gone:

az group exists --name "$RG"   # should eventually print: false

If you want to keep the VNets but stop the bill, delete just the gateways (az network vnet-gateway delete) and their public IPs — those are the only hourly-billed resources here.

Doing it as Bicep (repeatable, reviewable)

For anything beyond a one-off lab, define the build as Bicep — version-controlled and reproducible. This template provisions one side (VNet + GatewaySubnet + public IP + gateway + LNG + connection with the custom policy). Deploy it twice for a VNet-to-VNet lab, or once pointing the LNG at your real on-prem device; dependsOn is implicit through the symbolic references.

// s2s-vpn.bicep — one side of a Site-to-Site VPN
@description('Deployment region')
param location string = resourceGroup().location

@description('VNet name and address space for the Azure side')
param vnetName string = 'vnet-azure'
param vnetPrefix string = '10.10.0.0/16'
param workloadSubnetPrefix string = '10.10.1.0/24'
param gatewaySubnetPrefix string = '10.10.255.0/27'

@description('On-prem (remote) device public IP and prefixes')
param onPremPublicIp string
param onPremPrefixes array = [ '10.50.0.0/16' ]

@description('Pre-shared key — pass as a secure parameter, never hardcode')
@secure()
param sharedKey string

@description('VPN Gateway SKU (VpnGw1 = smallest BGP-capable)')
@allowed([ 'VpnGw1', 'VpnGw2', 'VpnGw3', 'VpnGw1AZ', 'VpnGw2AZ' ])
param gatewaySku string = 'VpnGw1'

resource vnet 'Microsoft.Network/virtualNetworks@2023-11-01' = {
  name: vnetName
  location: location
  properties: {
    addressSpace: { addressPrefixes: [ vnetPrefix ] }
    subnets: [
      { name: 'snet-workload', properties: { addressPrefix: workloadSubnetPrefix } }
      // The reserved subnet — name MUST be exactly 'GatewaySubnet'
      { name: 'GatewaySubnet', properties: { addressPrefix: gatewaySubnetPrefix } }
    ]
  }
}

resource gwPip 'Microsoft.Network/publicIPAddresses@2023-11-01' = {
  name: 'pip-${vnetName}-gw'
  location: location
  sku: { name: 'Standard' }                 // Standard required for AZ gateways
  properties: { publicIPAllocationMethod: 'Static' }
}

resource gateway 'Microsoft.Network/virtualNetworkGateways@2023-11-01' = {
  name: 'vng-${vnetName}'
  location: location
  properties: {
    gatewayType: 'Vpn'
    vpnType: 'RouteBased'                    // route-based — fixed at create; the right default
    sku: { name: gatewaySku, tier: gatewaySku }
    ipConfigurations: [ {
      name: 'gwIpConfig'
      properties: {
        privateIPAllocationMethod: 'Dynamic'
        subnet: { id: '${vnet.id}/subnets/GatewaySubnet' }
        publicIPAddress: { id: gwPip.id }
      }
    } ]
  }
}

resource lng 'Microsoft.Network/localNetworkGateways@2023-11-01' = {
  name: 'lng-to-onprem'
  location: location
  properties: {
    gatewayIpAddress: onPremPublicIp                 // your on-prem firewall's public IP
    localNetworkAddressSpace: { addressPrefixes: onPremPrefixes }
  }
}

resource connection 'Microsoft.Network/connections@2023-11-01' = {
  name: 'cn-azure-to-onprem'
  location: location
  properties: {
    connectionType: 'IPsec'
    virtualNetworkGateway1: { id: gateway.id, properties: {} }
    localNetworkGateway2: { id: lng.id, properties: {} }
    sharedKey: sharedKey
    // Custom IPsec/IKE policy — IKEv2, GCMAES256/SHA384/DHGroup24, PFS24
    ipsecPolicies: [ {
      ikeEncryption: 'GCMAES256'
      ikeIntegrity: 'SHA384'
      dhGroup: 'DHGroup24'
      ipsecEncryption: 'GCMAES256'
      ipsecIntegrity: 'GCMAES256'
      pfsGroup: 'PFS24'
      saLifeTimeSeconds: 27000
      saDataSizeKilobytes: 102400000
    } ]
    // Uncomment for picky peers (e.g. Cisco ASA) that need specific selectors:
    // usePolicyBasedTrafficSelectors: true
  }
}

output gatewayPublicIp string = gwPip.properties.ipAddress

Deploy and validate:

az deployment group create -g "$RG" \
  --template-file s2s-vpn.bicep \
  --parameters onPremPublicIp="203.0.113.40" sharedKey="$PSK" \
  --query "properties.outputs.gatewayPublicIp.value" -o tsv

Expected output: the gateway’s public IP — hand this number to the on-prem network engineer; it is the tunnel destination they configure on the firewall.

Mapping the lab to a real on-prem device

In production the “on-prem” VNet and its gateway/connection are replaced by your firewall. What each Azure object maps to on the common platforms, so the network team can mirror your config:

Azure object / field Cisco ASA / IOS Palo Alto (PAN-OS) FortiGate strongSwan (Linux)
Gateway public IP (peer) crypto map ... set peer IKE Gateway → Peer Address Phase 1 → Remote Gateway right=
On-prem prefixes (LNG) crypto ACL / VTI route Proxy IDs / route Phase 2 selectors / route leftsubnet=
Pre-shared key tunnel-group ... pre-shared-key IKE Gateway → PSK Phase 1 → Pre-shared Key secret in ipsec.secrets
IKEv2 / phase 1 crypto crypto ikev2 proposal IKE Crypto Profile Phase 1 Proposal ike=
Phase 2 crypto / PFS crypto ipsec transform-set IPSec Crypto Profile Phase 2 Proposal esp=
Specific selectors crypto ACL (always specific) Proxy IDs (specific) Phase 2 selectors leftsubnet/rightsubnet
BGP ASN / peer router bgp / neighbor Virtual Router → BGP Router → BGP FRR/BIRD alongside

The recurring gotcha across all of them: specific traffic selectors / proxy IDs. ASA, Palo Alto and FortiGate templates often default to specific subnet pairs while Azure offers wildcards — set usePolicyBasedTrafficSelectors: true on the connection (or configure the device for any-to-any) to make phase 2 agree.

Common mistakes & troubleshooting

When a tunnel won’t come up or won’t pass traffic, work it as a layered playbook — phase 1, then phase 2, then routing. The table is the playbook; the prose after expands the trickiest rows.

# Symptom Root cause Confirm (exact command / path) Fix
1 Gateway create fails immediately No subnet named exactly GatewaySubnet, or it’s too small az network vnet subnet show -g RG --vnet-name V -n GatewaySubnet Create GatewaySubnet at /27 or larger
2 Connection stuck Connecting, phase 1 never up PSK mismatch, or IKE version/crypto mismatch Portal → Connection → Connection status; device IKE log “no proposal chosen” Re-enter identical PSK both ends; align IKE policy fields
3 Phase 1 up, phase 2 fails, tunnel “Connecting” Traffic-selector / proxy-ID mismatch Device phase-2 log; check selectors Set usePolicyBasedTrafficSelectors: true, or narrow both ends
4 Tunnel won’t establish at all, no logs on Azure side UDP 500/4500 or ESP blocked on the internet path On-prem firewall WAN rules; ISP filtering Allow UDP 500 + 4500 inbound/outbound and ESP (IP proto 50)
5 Tunnel Connected but no traffic to on-prem Missing/wrong prefix on the local network gateway az network local-gateway show ... --query localNetworkAddressSpace Add the on-prem subnet prefix to the LNG (or use BGP)
6 Tunnel Connected, ping fails one direction NSG / UDR / host firewall blocking, not the tunnel az network nic show-effective-route-table; NSG rules; OS firewall Allow ICMP/TCP in NSG + OS firewall; check effective routes
7 DH-group / “no proposal chosen” error Phase-1 DH group mismatch (24 vs 14) Device IKE log shows offered vs accepted groups Align the numeric DH group on both ends
8 Tunnel flaps every few hours SA-lifetime mismatch or DPD too aggressive Connection status history; device rekey logs Align SA lifetimes; set DPD timeout ~45 s
9 Works, then breaks after on-prem change On-prem device public IP changed Compare LNG gatewayIpAddress to the device’s real WAN IP Update LNG --gateway-ip-address; use BGP for churn
10 Two VNets can’t reach via the gateway Forgot gateway transit / peering for spoke VNets Peering settings; effective routes Enable allowGatewayTransit + useRemoteGateways
11 Throughput plateaus well below SKU Single-tunnel/single-flow limit, not the gateway SnatConnectionCount/TunnelEgressBytes metrics; iperf Multiple tunnels/flows; consider ExpressRoute
12 Overlapping address spaces Azure VNet range overlaps on-prem range Compare AZ_PREFIX vs OP_PREFIX Re-IP one side to non-overlapping CIDR (cannot coexist)

Row 3 — the phase-2 selector trap (the most common real failure). Phase 1 succeeds (PSK fine) but phase 2 fails because Azure offers wildcard 0.0.0.0/0 selectors and the peer insists on specific subnet pairs. The connection sits at “Connecting” with no obvious Azure-side error; the tell is a phase-2 / “IPsec SA” failure in the device’s log while phase 1 shows established. Fix: set usePolicyBasedTrafficSelectors: true on the connection, or configure the peer for any-to-any.

Row 4 — the silent internet-path block. If neither side logs a negotiation attempt, the IKE packets aren’t reaching the peer. IPsec needs three things allowed end to end — Azure’s gateway already permits these inbound by design, so the block is almost always on the on-prem perimeter or the ISP:

Protocol Port / number Role Direction When it bites
UDP 500 IKE (key exchange) Both ways Always required; blocked → no negotiation
UDP 4500 IKE NAT-Traversal Both ways Required whenever a NAT is in the path (almost always)
ESP IP protocol 50 Encrypted data payload Both ways Some firewalls/ISPs drop ESP → phase 1 ok, no data

Confirm by checking the WAN-facing rules on the on-prem device and asking the ISP whether ESP and UDP 4500 are permitted; many consumer/ISP edges silently drop ESP.

Row 6 — “Connected but can’t ping.” Connected means the tunnel is healthy, not that a given packet is allowed. If reachability fails while the connection is up, the block is in the VNet (an NSG denying ICMP, a UDR mis-routing the return path) or the guest OS firewall (Windows blocks ICMP by default). Confirm the route with az network nic show-effective-route-table and that the NSG permits the traffic — this is where Azure Network Watcher Connectivity Diagnostics and Azure NSG Flow Debugging with IP Flow Verify pay off.

The Azure-side diagnostic you should know. When the device logs are inaccessible, capture IKE/IPsec packets on the gateway itself:

# Start a gateway packet capture (writes to a SAS-addressed blob)
az network vnet-gateway packet-capture start -g "$RG" -n "$AZ_GW"
# ...reproduce the failure for ~60s...
az network vnet-gateway packet-capture stop -g "$RG" -n "$AZ_GW" \
  --sas-url "<blob-SAS-URL-with-write>"

When a peer claims “the PSK is right,” az network vpn-connection shared-key show -g "$RG" --connection-name cn-azure-to-onprem prints exactly what Azure has — ending the “no, you have it wrong” argument in one command.

Best practices

Security notes

The tunnel is an encrypted pipe, but a pipe carries whatever you let through it. Treat the connection as a trust boundary, not a flat extension of the corporate LAN.

Cost & sizing

Three things drive the VPN bill, and the gateway-hour is by far the largest and most-overlooked:

Cost driver How it’s billed Rough figure Notes
Gateway-hour Per hour the gateway exists (idle or not) VpnGw1 ≈ ₹3–4/hr (~$0.04); VpnGw1AZ a bit more The dominant cost; bills even with no traffic
Outbound data (egress) Per GB leaving Azure over the tunnel First GB/month free, then per-GB (region-dependent) Inbound to Azure is free
Public IP Standard static public IP per hour ~₹0.30/hr (~$0.004) each Two for active-active
SKU upgrade Higher gateway-hour at larger SKUs Scales VpnGw1VpnGw5 Resize within generation without rebuild

Start at the smallest SKU that meets today’s aggregate throughput and supports BGPVpnGw1/VpnGw1AZ for most single-site prod (≈650 Mbps, 30 tunnels). Resize up only when sustained TunnelEgressBytes nears the cap (no rebuild within the generation), so don’t over-buy. Past a few Gbps sustained, model ExpressRoute — predictable and often cheaper at high throughput. There’s no free-tier VPN Gateway, so in dev/test delete the idle gateway (the only meaningful charge); active-active adds only a second public IP, since the second instance is in the SKU.

Interview & exam questions

These map to AZ-700 (Azure Network Engineer Associate) and the networking portions of AZ-104 and AZ-305.

  1. What is the difference between a route-based and a policy-based VPN gateway, and which should you choose? Route-based uses a routing table (a route to remote prefixes points at the tunnel) and supports multiple tunnels, BGP, active-active, IKEv2 and ExpressRoute coexistence. Policy-based uses static ACL-style policy, supports a single tunnel, IKEv1 only, Basic SKU only. Choose route-based for essentially all new builds; policy-based only for a legacy peer that requires it.

  2. What must the gateway subnet be named, and why does its size matter? It must be named exactly GatewaySubnet — the platform places gateway VMs there by name. Size it /27 or larger (/26 recommended) so larger SKUs and ExpressRoute coexistence have address room; too small and deployment or future expansion fails.

  3. What does a local network gateway represent? It is an Azure object describing the remote (on-prem) side: the public IP of the on-prem VPN device and the address prefixes behind it. With static routing you list those prefixes by hand; with BGP they’re learned dynamically and the LNG carries the peer’s BGP IP/ASN.

  4. Phase 1 establishes but phase 2 fails and the tunnel stays “Connecting.” What’s the likely cause? A traffic-selector / proxy-ID mismatch: Azure offers wildcard any-to-any selectors while the peer (often Cisco ASA/FortiGate) wants specific subnet pairs. Fix by enabling usePolicyBasedTrafficSelectors on the connection or configuring the peer for any-to-any.

  5. Which ports/protocols must be open for an IPsec S2S VPN? UDP 500 (IKE), UDP 4500 (IKE NAT-Traversal, used when NAT is in the path), and ESP (IP protocol 50). If the tunnel won’t establish with no logs on the Azure side, suspect these being blocked on the on-prem perimeter or by the ISP.

  6. When do you choose static routing vs BGP? Static for a single site with stable prefixes — simplest, no ASN coordination. BGP for multiple tunnels (real failover), an evolving on-prem address plan (no manual LNG edits), or ExpressRoute coexistence. BGP requires VpnGw1+ (not Basic) and an ASN on each side; Azure’s default gateway ASN is 65515.

  7. What does a custom IPsec/IKE policy do, and what’s the catch? It pins the exact IKE/IPsec algorithms (encryption, integrity, DH/PFS group, SA lifetimes) instead of Azure’s permissive defaults. The catch: it’s all-or-nothing — Azure proposes only that single combination, so the on-prem device must match every field exactly or negotiation fails.

  8. How do you make an S2S VPN highly available? Run the gateway active-active (two public IPs, two tunnels) terminating on two on-prem devices, and use BGP so a device/tunnel failure repaths automatically. Use a non-Basic SKU (AZ variant for zone redundancy). The 99.95% SLA assumes redundant tunnels, not a single link.

  9. Two networks need to talk but their address spaces overlap. What do you do? You cannot route between overlapping ranges over a standard S2S VPN — re-IP one side to a non-overlapping CIDR. (Some NVAs offer NAT-over-VPN, but the clean answer for the exam and production is to fix the addressing.)

  10. A spoke VNet (peered to the hub that holds the gateway) can’t reach on-prem. Why? Gateway transit isn’t configured: the hub peering needs allowGatewayTransit and the spoke peering needs useRemoteGateways, so the spoke uses the hub’s gateway. Without it the spoke has no route to the tunnel.

  11. The connection shows “Connected” but a VM can’t ping across the tunnel. Where do you look? Not the tunnel — it’s healthy. Check VNet routing (effective routes via az network nic show-effective-route-table), NSG rules, UDRs and the guest OS firewall (Windows blocks ICMP by default). “Connected” is about IPsec, not about whether a given packet is permitted.

  12. What practically limits throughput on a single tunnel? A single IPsec tunnel and single flow are bounded well below the gateway’s aggregate SKU number (which is across all tunnels). To go faster, use multiple tunnels/flows or, for guaranteed high bandwidth and low latency, ExpressRoute.

Quick check

  1. What is the one mandatory name for the subnet that hosts a VPN Gateway, and the recommended minimum size?
  2. Your tunnel reaches phase 1 but phase 2 fails and stays “Connecting” — what’s the first thing to suspect?
  3. Name the three ports/protocols that must be permitted end-to-end for IPsec.
  4. Which gateway type (route-based or policy-based) supports BGP and multiple tunnels — and can you change the type after creation?
  5. The connection status is “Connected” but a VM can’t reach on-prem. Is the tunnel the problem?

Answers

  1. GatewaySubnet (exact name, case-sensitive in intent), at least /27 (prefer /26). A wrong name or too-small range fails the gateway deployment.
  2. A traffic-selector / proxy-ID mismatch — Azure offers wildcard selectors while the peer wants specific subnet pairs. Set usePolicyBasedTrafficSelectors: true or configure the peer for any-to-any.
  3. UDP 500 (IKE), UDP 4500 (NAT-T), and ESP (IP protocol 50). If they’re blocked, the tunnel never establishes and Azure logs nothing.
  4. Route-based supports BGP, multiple tunnels, active-active and IKEv2. The type is fixed at creation — changing it means deleting and rebuilding the gateway.
  5. No. “Connected” means the IPsec tunnel is healthy. A reachability failure is routing (effective routes/UDRs), an NSG, or the guest OS firewall — not the tunnel itself.

Glossary

Next steps

AzureVPN GatewaySite-to-SiteIPsecIKEv2BGPNetworkingBicep
Need this built for real?

Vinod is a Senior Cloud Architect (22+ yrs) — available for Azure / AWS / GCP architecture, landing zones, and migrations.

Work with me

Comments

Keep Reading