Theoretical Foundation: Create and configure virtual network peering
1. Initial Intuitionβ
In the previous module, we learned that VNets are completely isolated from each other by default. A VM in vnet-production and a VM in vnet-development cannot communicate, just like two separate buildings have no connecting corridor.
VNet Peering is that corridor: a direct connection between two VNets that allows resources in each one to communicate as if they were on the same network. There's no additional hardware, no encryption (traffic already travels through Microsoft's private backbone network), no gateway, and latency is the same as within a single VNet.
The most accurate analogy is a walkway between two corporate buildings: the buildings remain independent structures, with their own internal rules, but people can move between them directly and quickly, without going outside to the street.
2. Contextβ
VNet Peering solves one of the most common problems in medium and large-scale Azure environments: how to efficiently connect multiple VNets. It forms the foundation of two widely used architectures:
Alternatives to VNet Peering for connecting VNets are VPN Gateway (more expensive, slower, via internet or encrypted tunnel) or ExpressRoute (for dedicated connections with on-premises). Peering is always the first choice when the need is to connect two Azure VNets, due to its simplicity and performance.
3. Building the Conceptsβ
3.1 What is a Peering: Bidirectional Nature with Two Entitiesβ
This is the most important and most frequently confused concept about VNet Peering: a peering is not a single object; it's always two separate objects.
When you connect VNet-A and VNet-B, you create:
- A peering from
VNet-AtowardsVNet-B(configured on VNet-A's side) - A peering from
VNet-BtowardsVNet-A(configured on VNet-B's side)
Both need to exist and be in the Connected state for communication to work. If only one side is created, the state remains Initiated, and traffic doesn't flow.
3.2 Types of Peeringβ
| Type | Description | Latency |
|---|---|---|
| Local VNet Peering | VNets in the same Azure region | Same as within a single VNet |
| Global VNet Peering | VNets in different regions | Slightly higher, uses Microsoft's global backbone |
Global VNet Peering allows connecting, for example, a VNet in Brazil South with a VNet in East US. Traffic travels through Microsoft's private network, not the public internet.
Global Peering Restriction: some resources don't support traffic via Global VNet Peering. The most important is the Azure Load Balancer Standard (Basic tier): VMs in one VNet cannot be reached by resources in a peered VNet in a different region using the Load Balancer IP. Direct access via VM IP works normally.
3.3 Peering Configurationsβ
Each side of the peering has independent configurations. The most important ones are:
| Configuration | On side | What it controls |
|---|---|---|
| Allow virtual network access | Both | Whether resources from the destination VNet are accessible. If disabled, traffic doesn't flow even with the peering connected. |
| Allow forwarded traffic | Both | Whether traffic that was forwarded by an NVA (network virtual appliance) can use this peering. Crucial for hub-spoke. |
| Allow gateway transit | Side that has the gateway | Allows the other VNet to use this VNet's gateway (VPN or ExpressRoute). Enables on-premises connectivity via hub. |
| Use remote gateways | Side that does NOT have the gateway | Enables using the other VNet's gateway. Must be enabled together with "Allow gateway transit" on the other side. |
The combination of Allow gateway transit + Use remote gateways is what allows spoke VNets in a hub-spoke architecture to access the on-premises network through the VPN Gateway located in the hub.
3.4 Non-Transitivity: The Fundamental Conceptβ
VNet Peering is non-transitive. This means that if A is connected to B, and B is connected to C, this doesn't imply that A can communicate with C.
For A to communicate with C, it's necessary to:
- Option 1: Create a direct peering between A and C
- Option 2: Use an NVA (Network Virtual Appliance, like Azure Firewall) in VNet-B to forward traffic, with User Defined Routes (UDRs) configured and "Allow forwarded traffic" enabled in the peerings
Non-transitivity is why hub-spoke architecture requires special configuration: the hub needs to have an NVA or firewall to route traffic between spokes, and the peerings need to have "Allow forwarded traffic" enabled.
4. Structural Viewβ
Point-to-Point Peeringβ
Hub-Spoke Architecture with Peering and Gateway Transitβ
In this topology:
- Spoke1 accesses on-premises via gateway in the Hub (thanks to gateway transit)
- Spoke2 accesses on-premises via gateway in the Hub (same mechanism)
- Traffic between Spoke1 and Spoke2 goes through the Firewall in the Hub (by UDRs, not direct peering)
- The Hub centralizes all shared services
5. Practical Operationβ
How Traffic Flows in a Peeringβ
When a peering is created, Azure automatically adds peering routes to the effective route tables of NICs in both VNets. These routes have the remote VNet prefix and next hop type VNetPeering. You can see these routes by checking the "Effective Routes" of any NIC via Network Watcher.
Important and Non-Obvious Behaviorsβ
Peering doesn't prevent NSGs from blocking traffic: creating a peering between two VNets doesn't automatically open all traffic flows. NSGs associated with subnets or NICs in the VNets still apply. An NSG that blocks traffic from 10.2.0.0/16 will continue blocking even after the peering is created.
Address space changes require peering action: if you add a new CIDR block to a VNet that already has peerings, you need to synchronize the peerings for the new routes to be propagated. In the portal, there's a "Sync" button in the peering configuration. Without synchronization, peered VNets cannot reach the new addresses.
Peering between different subscriptions: fully supported, but requires the user creating the peering to have permission in both VNets. The minimum required role is Network Contributor (or a custom role with Microsoft.Network/virtualNetworks/virtualNetworkPeerings/write) in both VNets.
Peering cost: unlike VNet creation itself, VNet Peering has costs based on transferred traffic volume. Local peering (same region) and global peering (between regions) have different prices, with global being more expensive. Plan this into cost sizing.
Cannot use overlapping address spaces: if VNet-A has 10.0.0.0/16 and VNet-B also has 10.0.0.0/16, the peering fails. The spaces must be completely distinct and non-overlapping.
6. Implementation Methodsβ
6.1 Azure Portalβ
When to use: one-time peering creation, status visualization, diagnostics.
Path: Virtual Networks > [select VNet] > Peerings > Add
The peering creation form in the portal has an important convenience: when creating a peering from VNet-A to VNet-B, the portal offers the option to create the reverse peering (VNet-B to VNet-A) at the same time, provided the user has permission in both VNets. This avoids the error of creating only one side.
Creation fields:
- Peering link name (local side): name of the peering in the source VNet
- Peering link name (remote side): name of the peering in the destination VNet
- Virtual network: the destination VNet (supports search by name, different subscription, different tenant)
- Traffic forwarded from remote virtual network: "Allow forwarded traffic"
- Virtual network gateway or Route Server: "Allow gateway transit" or "Use remote gateways"
6.2 Azure CLIβ
Create peering on both sides (recommended operation: create both in sequence):
# Peering from VNet-A to VNet-B
az network vnet peering create \
--name peering-production-to-dev \
--vnet-name vnet-production \
--resource-group rg-networking \
--remote-vnet /subscriptions/<sub-id>/resourceGroups/rg-networking/providers/Microsoft.Network/virtualNetworks/vnet-dev \
--allow-vnet-access true \
--allow-forwarded-traffic true
# Peering from VNet-B to VNet-A
az network vnet peering create \
--name peering-dev-to-production \
--vnet-name vnet-dev \
--resource-group rg-networking \
--remote-vnet /subscriptions/<sub-id>/resourceGroups/rg-networking/providers/Microsoft.Network/virtualNetworks/vnet-production \
--allow-vnet-access true \
--allow-forwarded-traffic true
Create peering between VNets in different subscriptions:
# Get full VNet IDs
VNET_PROD_ID=$(az network vnet show \
--name vnet-production \
--resource-group rg-networking \
--subscription <sub-production-id> \
--query id -o tsv)
VNET_DEV_ID=$(az network vnet show \
--name vnet-dev \
--resource-group rg-dev \
--subscription <sub-dev-id> \
--query id -o tsv)
# Peering from Prod to Dev (execute with Prod subscription context)
az network vnet peering create \
--name peering-prod-to-dev \
--vnet-name vnet-production \
--resource-group rg-networking \
--subscription <sub-production-id> \
--remote-vnet $VNET_DEV_ID \
--allow-vnet-access true
# Peering from Dev to Prod (execute with Dev subscription context)
az network vnet peering create \
--name peering-dev-to-prod \
--vnet-name vnet-dev \
--resource-group rg-dev \
--subscription <sub-dev-id> \
--remote-vnet $VNET_PROD_ID \
--allow-vnet-access true
Check peering status:
az network vnet peering list \
--vnet-name vnet-production \
--resource-group rg-networking \
--query "[].{Name:name, State:peeringState, RemoteVNet:remoteVirtualNetwork.id}" \
--output table
Sync peering after address space change:
az network vnet peering sync \
--name peering-production-to-dev \
--vnet-name vnet-production \
--resource-group rg-networking
Create peering with gateway transit (Hub side):
az network vnet peering create \
--name peering-hub-to-spoke1 \
--vnet-name vnet-hub \
--resource-group rg-hub \
--remote-vnet <spoke1-vnet-id> \
--allow-vnet-access true \
--allow-forwarded-traffic true \
--allow-gateway-transit true
Create peering with use remote gateways (Spoke side):
az network vnet peering create \
--name peering-spoke1-to-hub \
--vnet-name vnet-spoke1 \
--resource-group rg-spoke1 \
--remote-vnet <hub-vnet-id> \
--allow-vnet-access true \
--allow-forwarded-traffic true \
--use-remote-gateways true
Prerequisite: --use-remote-gateways only works if the gateway in the Hub is already provisioned and --allow-gateway-transit true is configured in the Hub-side peering.
6.3 PowerShellβ
# Get VNet references
$vnetProd = Get-AzVirtualNetwork -Name "vnet-production" -ResourceGroupName "rg-networking"
$vnetDev = Get-AzVirtualNetwork -Name "vnet-dev" -ResourceGroupName "rg-networking"
# Create peering from Prod to Dev
Add-AzVirtualNetworkPeering `
-Name "peering-production-to-dev" `
-VirtualNetwork $vnetProd `
-RemoteVirtualNetworkId $vnetDev.Id `
-AllowForwardedTraffic `
-AllowVirtualNetworkAccess
# Create peering from Dev to Prod
Add-AzVirtualNetworkPeering `
-Name "peering-dev-to-production" `
-VirtualNetwork $vnetDev `
-RemoteVirtualNetworkId $vnetProd.Id `
-AllowForwardedTraffic `
-AllowVirtualNetworkAccess
# Check status
Get-AzVirtualNetworkPeering `
-VirtualNetworkName "vnet-production" `
-ResourceGroupName "rg-networking" |
Select-Object Name, PeeringState, RemoteVirtualNetwork
6.4 Bicepβ
// Peering from VNet-A to VNet-B
resource peeringAtoB 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2023-05-01' = {
name: '${vnetA.name}/peering-a-to-b'
properties: {
remoteVirtualNetwork: {
id: vnetB.id
}
allowVirtualNetworkAccess: true
allowForwardedTraffic: true
allowGatewayTransit: false
useRemoteGateways: false
}
}
// Peering from VNet-B to VNet-A
resource peeringBtoA 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2023-05-01' = {
name: '${vnetB.name}/peering-b-to-a'
properties: {
remoteVirtualNetwork: {
id: vnetA.id
}
allowVirtualNetworkAccess: true
allowForwardedTraffic: true
allowGatewayTransit: false
useRemoteGateways: false
}
dependsOn: [peeringAtoB]
}
The dependsOn ensures the first peering is created before the second, avoiding race conditions.
7. Control and Securityβ
NSGs Still Apply with Peeringβ
Creating a peering doesn't change NSG behavior. An NSG that blocks traffic from 10.2.0.0/16 continues blocking even with active peering. This is positive from a security perspective: peering connects the networks, but traffic controls (NSGs, Azure Firewall, UDRs) determine what can flow.
Recommended practice: when creating peerings, review NSGs in both VNets to ensure necessary rules are configured. NSGs that use "VirtualNetwork" as source/destination automatically include peered VNets, which may be more permissive than expected.
Required Permissions to Create Peeringsβ
| Operation | Required permission | Role that includes |
|---|---|---|
| Create peering in VNet-A | Microsoft.Network/virtualNetworks/virtualNetworkPeerings/write in VNet-A | Network Contributor, Owner |
| Create peering in VNet-B (reverse side) | Microsoft.Network/virtualNetworks/virtualNetworkPeerings/write in VNet-B | Network Contributor in VNet-B |
| Create peering between subscriptions | Permissions in both VNets in their respective subscriptions | Network Contributor in both |
For peering between different tenants, explicit permission must be assigned to the principal from the other tenant before creating the peering.
Tracking Traffic between Peered VNetsβ
Network Watcher Flow Logs can be enabled in each VNet to log traffic flowing through NICs. To specifically track traffic via peering, the effective routes of a NIC will show entries with NextHopType: VNetPeering.
8. Decision Makingβ
VNet Peering vs. other VNet connectivity optionsβ
| Situation | Best option | Reason |
|---|---|---|
| Two VNets in the same Azure region | Local VNet Peering | Lower latency, traffic-based cost, simple to configure |
| Two VNets in different regions | Global VNet Peering | Same model, uses Microsoft private backbone |
| Azure VNet and on-premises network | VPN Gateway or ExpressRoute | Peering doesn't connect to on-premises; requires gateway |
| Many VNets need centralized connectivity | Hub-spoke with peerings | Scales well, centralizes services |
| Need high security and inspection between VNets | Hub-spoke with Azure Firewall in hub | Peering + UDRs + Firewall for traffic inspection |
| Transitive connectivity between dozens of VNets | Azure Virtual WAN | Solves transitivity natively, Microsoft-managed |
When to use "Allow forwarded traffic"?β
| Situation | Enable? | Reason |
|---|---|---|
| Direct peering without NVA in the path | Not necessary | Traffic goes directly between the VNets |
| Hub-spoke architecture with Firewall/NVA in hub | Required | The NVA forwards traffic between spokes; without this, forwarded traffic is dropped |
| Spoke needs to exit to internet via NVA in hub | Required | Same principle: outbound traffic is forwarded by the NVA |
9. Best Practicesβ
Create both sides of peering in the same operation or pipeline: the window between creating one side and the other is a period where traffic still doesn't flow. In Infrastructure as Code pipelines, create both resources in the same template to ensure atomicity.
Use descriptive and standardized peering names: a pattern like peering-[source-vnet]-to-[destination-vnet] (e.g., peering-hub-to-production) makes the direction and involved VNets immediately clear. In environments with dozens of peerings, ambiguous names make maintenance much more difficult.
Document the peering topology: in complex environments, maintain an updated diagram of which VNets are connected to each other, via what type of peering (local or global) and what special configurations (gateway transit, etc.) are active. Tools like Network Watcher Topology can generate this view automatically.
Plan address spaces without overlap for all future VNets: the biggest obstacle to peering is address overlap. In growing companies, defining from the beginning an IPAM plan that allocates unique blocks for each VNet (production, dev, staging, hub, and each region) avoids future rework.
For hub-spoke: enable "Allow forwarded traffic" on spoke peerings from creation: even if you don't have a Firewall/NVA in the hub now, enabling this option from the start avoids having to modify the peerings later, when you add the Firewall and discover that forwarded traffic is being dropped.
10. Common Errorsβ
Creating only one side of peering and confusing "Initiated" state with "Connected"
Peering remains in Initiated state when only one side has been created. Resources in both VNets cannot communicate. The administrator checks the peering on side A, sees the state and thinks something is wrong with the network, spends hours diagnosing, when the solution is simply creating the peering on side B. The portal facilitates this by offering simultaneous creation of both sides.
Adding address space to VNet without synchronizing peerings
The company adds a second CIDR block (10.1.0.0/16) to the production VNet that already has peering. VMs in the new addresses (10.1.x.x) cannot communicate with VMs in the peered VNet. Peering routes were not automatically updated. The solution is to synchronize the peering ("Sync" button in portal or az network vnet peering sync in CLI).
Assuming peering is transitive
The development team creates VNet-Dev peered with VNet-Hub, and the data team creates VNet-Data also peered with VNet-Hub. They expect Dev and Data to communicate via Hub. It doesn't work because peering is not transitive. To connect Dev and Data, a direct peering between them is needed, or a Firewall in Hub with UDRs forwarding traffic.
Enabling "Use remote gateways" when the hub gateway doesn't exist yet
You create the spoke-to-hub peering with --use-remote-gateways true, but the VPN Gateway in the hub hasn't been provisioned yet (gateways take 30-45 minutes to provision). The peering fails with an error. The correct approach is to enable this option only after the gateway is completely provisioned and operational.
Not updating NSGs when creating peerings
Peering created between VNet-Frontend and VNet-Database. Administrators assume that now frontend VMs access the database. But the NSG on the database subnet only allows traffic from 10.0.2.0/24 (backend), not from 10.0.1.0/24 (frontend via peering). The peering exists, but the NSG blocks. Always review NSGs in both VNets when creating peerings.
11. Operation and Maintenanceβ
Monitor Status of All Peerings in a Subscriptionβ
# List all peerings in all VNets of a subscription
$vnets = Get-AzVirtualNetwork
foreach ($vnet in $vnets) {
$peerings = Get-AzVirtualNetworkPeering `
-VirtualNetworkName $vnet.Name `
-ResourceGroupName $vnet.ResourceGroupName
foreach ($peering in $peerings) {
[PSCustomObject]@{
VNet = $vnet.Name
PeeringName = $peering.Name
Status = $peering.PeeringState
RemoteVNet = $peering.RemoteVirtualNetwork.Id.Split('/')[-1]
ForwardedTraffic = $peering.AllowForwardedTraffic
GatewayTransit = $peering.AllowGatewayTransit
UseRemoteGateways = $peering.UseRemoteGateways
}
}
} | Format-Table -AutoSize
Check Effective Routes to Confirm Active Peeringβ
# Check effective routes of a specific NIC
az network nic show-effective-route-table \
--name nic-vm-frontend \
--resource-group rg-producao \
--output table
Entries with nextHopType: VNetPeering confirm that peering is active and routes have been propagated.
Connectivity Diagnosis via Network Watcherβ
# Test connectivity between two VMs (requires Network Watcher enabled)
az network watcher test-connectivity \
--source-resource /subscriptions/<sub-id>/resourceGroups/rg-producao/providers/Microsoft.Compute/virtualMachines/vm-frontend \
--dest-resource /subscriptions/<sub-id>/resourceGroups/rg-dev/providers/Microsoft.Compute/virtualMachines/vm-dev \
--protocol TCP \
--dest-port 80
The result shows if the connection was successful, and in case of failure, where in the path the blocking occurred (NSG, route, etc.).
Important Limitsβ
| Item | Default limit |
|---|---|
| Peerings per VNet | 500 |
| VNets in chain for Gateway Transit | 1 (cannot chain: Spoke1-Hub1-Hub2-OnPrem) |
| Transfer cost via peering | Charged per GB transferred (varies by region and direction) |
| Peering bandwidth | Limited by VM NIC bandwidth, not by peering |
The limit of 500 peerings per VNet is rarely reached in traditional hub-spoke architectures, but in very large enterprise environments with Azure Virtual WAN it can be relevant.
12. Integration and Automationβ
VNet Peering in Infrastructure Pipelinesβ
In infrastructure CI/CD pipelines (Azure DevOps, GitHub Actions), creating peerings should be part of the new VNet provisioning process. A new environment (e.g., new production region) should automatically create:
- The new VNet
- Peerings to the Hub
- Reverse peerings from Hub to the new VNet
- Necessary UDRs for routing
Terraform example for creating bidirectional peerings:
# Peering from Hub to Spoke
resource "azurerm_virtual_network_peering" "hub_to_spoke" {
name = "peering-hub-to-${var.spoke_name}"
resource_group_name = var.hub_rg
virtual_network_name = var.hub_vnet_name
remote_virtual_network_id = azurerm_virtual_network.spoke.id
allow_forwarded_traffic = true
allow_gateway_transit = true
allow_virtual_network_access = true
}
# Peering from Spoke to Hub
resource "azurerm_virtual_network_peering" "spoke_to_hub" {
name = "peering-${var.spoke_name}-to-hub"
resource_group_name = var.spoke_rg
virtual_network_name = azurerm_virtual_network.spoke.name
remote_virtual_network_id = var.hub_vnet_id
allow_forwarded_traffic = true
use_remote_gateways = var.use_remote_gateway
allow_virtual_network_access = true
depends_on = [azurerm_virtual_network_peering.hub_to_spoke]
}
Azure Virtual WAN: Managed Alternative for Connectivity at Scaleβ
For organizations with dozens or hundreds of VNets and multiple on-premises connections, Azure Virtual WAN solves the transitivity problem in a managed way:
In Virtual WAN, transitive connectivity is native: Spoke1 can talk to Spoke2 without additional NVA or UDRs. Microsoft manages the routing internally.
13. Final Summaryβ
Essential points:
- A VNet Peering is always two objects (one in each VNet). Both need to exist and be in
Connectedstate for traffic to flow. - Peering is non-transitive: A peered with B, B peered with C, doesn't mean A communicates with C.
- Two types: Local VNet Peering (same region) and Global VNet Peering (different regions). Both use Microsoft's private backbone network.
- Address spaces of peered VNets cannot overlap.
- NSGs continue to apply after peering. Peering connects the networks; NSGs control traffic.
Critical differences:
- "Connected" state vs. "Initiated": Connected means both sides exist and communication works. Initiated means only one side was created; no communication.
- Allow forwarded traffic vs. Allow gateway transit: the first allows traffic forwarded by an NVA to pass through peering; the second allows the other VNet to use this VNet's gateway.
- Peering vs. VPN Gateway: peering is direct, no hardware, uses private backbone, no additional encryption. VPN uses gateway, has gateway hourly cost, more suitable for on-premises.
- Local peering vs. Global peering: slightly different latency; global peering has higher transfer cost and some limitations with Basic Load Balancer.
What needs to be remembered:
- When adding address space to a VNet that has peerings, it's necessary to synchronize the peerings to propagate new routes.
- For hub-spoke with gateway in hub: Allow gateway transit on hub peering + Use remote gateways on spoke peering. Gateway must be provisioned before enabling "Use remote gateways".
- Peering between subscriptions requires
Network Contributorpermissions on both VNets, in their respective subscriptions. - Limit is 500 peerings per VNet. In very large environments, consider Azure Virtual WAN.
- The combination peering + UDRs + NSGs forms the basis of network segmentation in Azure. Peering provides connectivity; UDRs control routing; NSGs control allowed traffic.