Configure Azure Virtual Network Peering

The azure-native:network:VirtualNetworkPeering resource, part of the Pulumi Azure Native provider, defines a peering connection between two Azure virtual networks, enabling private IP connectivity without traversing the public internet. This guide focuses on three capabilities: full VNet peering for bidirectional access, subnet-level peering for scoped connectivity, and address space synchronization.

Peerings connect two existing VNets by referencing their resource IDs. Each peering is unidirectional, so bidirectional connectivity requires creating two peering resources (one in each direction). The examples are intentionally small. Combine them with your own VNet infrastructure and reciprocal peering configurations.

Connect two virtual networks for full bidirectional access

Most deployments connect two VNets to enable resources in each network to communicate privately.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const virtualNetworkPeering = new azure_native.network.VirtualNetworkPeering("virtualNetworkPeering", {
    allowForwardedTraffic: true,
    allowGatewayTransit: false,
    allowVirtualNetworkAccess: true,
    remoteVirtualNetwork: {
        id: "/subscriptions/subid/resourceGroups/peerTest/providers/Microsoft.Network/virtualNetworks/vnet2",
    },
    resourceGroupName: "peerTest",
    useRemoteGateways: false,
    virtualNetworkName: "vnet1",
    virtualNetworkPeeringName: "peer",
});
import pulumi
import pulumi_azure_native as azure_native

virtual_network_peering = azure_native.network.VirtualNetworkPeering("virtualNetworkPeering",
    allow_forwarded_traffic=True,
    allow_gateway_transit=False,
    allow_virtual_network_access=True,
    remote_virtual_network={
        "id": "/subscriptions/subid/resourceGroups/peerTest/providers/Microsoft.Network/virtualNetworks/vnet2",
    },
    resource_group_name="peerTest",
    use_remote_gateways=False,
    virtual_network_name="vnet1",
    virtual_network_peering_name="peer")
package main

import (
	network "github.com/pulumi/pulumi-azure-native-sdk/network/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := network.NewVirtualNetworkPeering(ctx, "virtualNetworkPeering", &network.VirtualNetworkPeeringArgs{
			AllowForwardedTraffic:     pulumi.Bool(true),
			AllowGatewayTransit:       pulumi.Bool(false),
			AllowVirtualNetworkAccess: pulumi.Bool(true),
			RemoteVirtualNetwork: &network.SubResourceArgs{
				Id: pulumi.String("/subscriptions/subid/resourceGroups/peerTest/providers/Microsoft.Network/virtualNetworks/vnet2"),
			},
			ResourceGroupName:         pulumi.String("peerTest"),
			UseRemoteGateways:         pulumi.Bool(false),
			VirtualNetworkName:        pulumi.String("vnet1"),
			VirtualNetworkPeeringName: pulumi.String("peer"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var virtualNetworkPeering = new AzureNative.Network.VirtualNetworkPeering("virtualNetworkPeering", new()
    {
        AllowForwardedTraffic = true,
        AllowGatewayTransit = false,
        AllowVirtualNetworkAccess = true,
        RemoteVirtualNetwork = new AzureNative.Network.Inputs.SubResourceArgs
        {
            Id = "/subscriptions/subid/resourceGroups/peerTest/providers/Microsoft.Network/virtualNetworks/vnet2",
        },
        ResourceGroupName = "peerTest",
        UseRemoteGateways = false,
        VirtualNetworkName = "vnet1",
        VirtualNetworkPeeringName = "peer",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.network.VirtualNetworkPeering;
import com.pulumi.azurenative.network.VirtualNetworkPeeringArgs;
import com.pulumi.azurenative.network.inputs.SubResourceArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var virtualNetworkPeering = new VirtualNetworkPeering("virtualNetworkPeering", VirtualNetworkPeeringArgs.builder()
            .allowForwardedTraffic(true)
            .allowGatewayTransit(false)
            .allowVirtualNetworkAccess(true)
            .remoteVirtualNetwork(SubResourceArgs.builder()
                .id("/subscriptions/subid/resourceGroups/peerTest/providers/Microsoft.Network/virtualNetworks/vnet2")
                .build())
            .resourceGroupName("peerTest")
            .useRemoteGateways(false)
            .virtualNetworkName("vnet1")
            .virtualNetworkPeeringName("peer")
            .build());

    }
}
resources:
  virtualNetworkPeering:
    type: azure-native:network:VirtualNetworkPeering
    properties:
      allowForwardedTraffic: true
      allowGatewayTransit: false
      allowVirtualNetworkAccess: true
      remoteVirtualNetwork:
        id: /subscriptions/subid/resourceGroups/peerTest/providers/Microsoft.Network/virtualNetworks/vnet2
      resourceGroupName: peerTest
      useRemoteGateways: false
      virtualNetworkName: vnet1
      virtualNetworkPeeringName: peer

The remoteVirtualNetwork property references the target VNet by resource ID. The allowVirtualNetworkAccess property enables VM-to-VM communication across the peering. The allowForwardedTraffic property controls whether traffic from third-party network appliances can traverse the peering. The allowGatewayTransit and useRemoteGateways properties manage VPN or ExpressRoute gateway sharing between networks. This example creates one direction of the peering; you must create a reciprocal peering from vnet2 to vnet1 for bidirectional connectivity.

Peer specific subnets instead of entire networks

Organizations with large address spaces often connect only specific subnets for security or compliance.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const virtualNetworkPeering = new azure_native.network.VirtualNetworkPeering("virtualNetworkPeering", {
    allowForwardedTraffic: true,
    allowGatewayTransit: false,
    allowVirtualNetworkAccess: true,
    enableOnlyIPv6Peering: false,
    localSubnetNames: [
        "Subnet1",
        "Subnet4",
    ],
    peerCompleteVnets: false,
    remoteSubnetNames: ["Subnet2"],
    remoteVirtualNetwork: {
        id: "/subscriptions/subid/resourceGroups/peerTest/providers/Microsoft.Network/virtualNetworks/vnet2",
    },
    resourceGroupName: "peerTest",
    useRemoteGateways: false,
    virtualNetworkName: "vnet1",
    virtualNetworkPeeringName: "peer",
});
import pulumi
import pulumi_azure_native as azure_native

virtual_network_peering = azure_native.network.VirtualNetworkPeering("virtualNetworkPeering",
    allow_forwarded_traffic=True,
    allow_gateway_transit=False,
    allow_virtual_network_access=True,
    enable_only_i_pv6_peering=False,
    local_subnet_names=[
        "Subnet1",
        "Subnet4",
    ],
    peer_complete_vnets=False,
    remote_subnet_names=["Subnet2"],
    remote_virtual_network={
        "id": "/subscriptions/subid/resourceGroups/peerTest/providers/Microsoft.Network/virtualNetworks/vnet2",
    },
    resource_group_name="peerTest",
    use_remote_gateways=False,
    virtual_network_name="vnet1",
    virtual_network_peering_name="peer")
package main

import (
	network "github.com/pulumi/pulumi-azure-native-sdk/network/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := network.NewVirtualNetworkPeering(ctx, "virtualNetworkPeering", &network.VirtualNetworkPeeringArgs{
			AllowForwardedTraffic:     pulumi.Bool(true),
			AllowGatewayTransit:       pulumi.Bool(false),
			AllowVirtualNetworkAccess: pulumi.Bool(true),
			EnableOnlyIPv6Peering:     pulumi.Bool(false),
			LocalSubnetNames: pulumi.StringArray{
				pulumi.String("Subnet1"),
				pulumi.String("Subnet4"),
			},
			PeerCompleteVnets: pulumi.Bool(false),
			RemoteSubnetNames: pulumi.StringArray{
				pulumi.String("Subnet2"),
			},
			RemoteVirtualNetwork: &network.SubResourceArgs{
				Id: pulumi.String("/subscriptions/subid/resourceGroups/peerTest/providers/Microsoft.Network/virtualNetworks/vnet2"),
			},
			ResourceGroupName:         pulumi.String("peerTest"),
			UseRemoteGateways:         pulumi.Bool(false),
			VirtualNetworkName:        pulumi.String("vnet1"),
			VirtualNetworkPeeringName: pulumi.String("peer"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var virtualNetworkPeering = new AzureNative.Network.VirtualNetworkPeering("virtualNetworkPeering", new()
    {
        AllowForwardedTraffic = true,
        AllowGatewayTransit = false,
        AllowVirtualNetworkAccess = true,
        EnableOnlyIPv6Peering = false,
        LocalSubnetNames = new[]
        {
            "Subnet1",
            "Subnet4",
        },
        PeerCompleteVnets = false,
        RemoteSubnetNames = new[]
        {
            "Subnet2",
        },
        RemoteVirtualNetwork = new AzureNative.Network.Inputs.SubResourceArgs
        {
            Id = "/subscriptions/subid/resourceGroups/peerTest/providers/Microsoft.Network/virtualNetworks/vnet2",
        },
        ResourceGroupName = "peerTest",
        UseRemoteGateways = false,
        VirtualNetworkName = "vnet1",
        VirtualNetworkPeeringName = "peer",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.network.VirtualNetworkPeering;
import com.pulumi.azurenative.network.VirtualNetworkPeeringArgs;
import com.pulumi.azurenative.network.inputs.SubResourceArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var virtualNetworkPeering = new VirtualNetworkPeering("virtualNetworkPeering", VirtualNetworkPeeringArgs.builder()
            .allowForwardedTraffic(true)
            .allowGatewayTransit(false)
            .allowVirtualNetworkAccess(true)
            .enableOnlyIPv6Peering(false)
            .localSubnetNames(            
                "Subnet1",
                "Subnet4")
            .peerCompleteVnets(false)
            .remoteSubnetNames("Subnet2")
            .remoteVirtualNetwork(SubResourceArgs.builder()
                .id("/subscriptions/subid/resourceGroups/peerTest/providers/Microsoft.Network/virtualNetworks/vnet2")
                .build())
            .resourceGroupName("peerTest")
            .useRemoteGateways(false)
            .virtualNetworkName("vnet1")
            .virtualNetworkPeeringName("peer")
            .build());

    }
}
resources:
  virtualNetworkPeering:
    type: azure-native:network:VirtualNetworkPeering
    properties:
      allowForwardedTraffic: true
      allowGatewayTransit: false
      allowVirtualNetworkAccess: true
      enableOnlyIPv6Peering: false
      localSubnetNames:
        - Subnet1
        - Subnet4
      peerCompleteVnets: false
      remoteSubnetNames:
        - Subnet2
      remoteVirtualNetwork:
        id: /subscriptions/subid/resourceGroups/peerTest/providers/Microsoft.Network/virtualNetworks/vnet2
      resourceGroupName: peerTest
      useRemoteGateways: false
      virtualNetworkName: vnet1
      virtualNetworkPeeringName: peer

The peerCompleteVnets property set to false enables subnet-level peering. The localSubnetNames and remoteSubnetNames properties specify which subnets participate in the peering. The enableOnlyIPv6Peering property restricts connectivity to IPv6 address spaces only. This configuration limits the scope of connectivity to the specified subnets rather than the entire VNet address ranges.

Synchronize address space changes automatically

When remote VNets add or remove address ranges, peerings can become out of sync.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const virtualNetworkPeering = new azure_native.network.VirtualNetworkPeering("virtualNetworkPeering", {
    allowForwardedTraffic: true,
    allowGatewayTransit: false,
    allowVirtualNetworkAccess: true,
    remoteVirtualNetwork: {
        id: "/subscriptions/subid/resourceGroups/peerTest/providers/Microsoft.Network/virtualNetworks/vnet2",
    },
    resourceGroupName: "peerTest",
    syncRemoteAddressSpace: "true",
    useRemoteGateways: false,
    virtualNetworkName: "vnet1",
    virtualNetworkPeeringName: "peer",
});
import pulumi
import pulumi_azure_native as azure_native

virtual_network_peering = azure_native.network.VirtualNetworkPeering("virtualNetworkPeering",
    allow_forwarded_traffic=True,
    allow_gateway_transit=False,
    allow_virtual_network_access=True,
    remote_virtual_network={
        "id": "/subscriptions/subid/resourceGroups/peerTest/providers/Microsoft.Network/virtualNetworks/vnet2",
    },
    resource_group_name="peerTest",
    sync_remote_address_space="true",
    use_remote_gateways=False,
    virtual_network_name="vnet1",
    virtual_network_peering_name="peer")
package main

import (
	network "github.com/pulumi/pulumi-azure-native-sdk/network/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := network.NewVirtualNetworkPeering(ctx, "virtualNetworkPeering", &network.VirtualNetworkPeeringArgs{
			AllowForwardedTraffic:     pulumi.Bool(true),
			AllowGatewayTransit:       pulumi.Bool(false),
			AllowVirtualNetworkAccess: pulumi.Bool(true),
			RemoteVirtualNetwork: &network.SubResourceArgs{
				Id: pulumi.String("/subscriptions/subid/resourceGroups/peerTest/providers/Microsoft.Network/virtualNetworks/vnet2"),
			},
			ResourceGroupName:         pulumi.String("peerTest"),
			SyncRemoteAddressSpace:    pulumi.String("true"),
			UseRemoteGateways:         pulumi.Bool(false),
			VirtualNetworkName:        pulumi.String("vnet1"),
			VirtualNetworkPeeringName: pulumi.String("peer"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var virtualNetworkPeering = new AzureNative.Network.VirtualNetworkPeering("virtualNetworkPeering", new()
    {
        AllowForwardedTraffic = true,
        AllowGatewayTransit = false,
        AllowVirtualNetworkAccess = true,
        RemoteVirtualNetwork = new AzureNative.Network.Inputs.SubResourceArgs
        {
            Id = "/subscriptions/subid/resourceGroups/peerTest/providers/Microsoft.Network/virtualNetworks/vnet2",
        },
        ResourceGroupName = "peerTest",
        SyncRemoteAddressSpace = "true",
        UseRemoteGateways = false,
        VirtualNetworkName = "vnet1",
        VirtualNetworkPeeringName = "peer",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.network.VirtualNetworkPeering;
import com.pulumi.azurenative.network.VirtualNetworkPeeringArgs;
import com.pulumi.azurenative.network.inputs.SubResourceArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var virtualNetworkPeering = new VirtualNetworkPeering("virtualNetworkPeering", VirtualNetworkPeeringArgs.builder()
            .allowForwardedTraffic(true)
            .allowGatewayTransit(false)
            .allowVirtualNetworkAccess(true)
            .remoteVirtualNetwork(SubResourceArgs.builder()
                .id("/subscriptions/subid/resourceGroups/peerTest/providers/Microsoft.Network/virtualNetworks/vnet2")
                .build())
            .resourceGroupName("peerTest")
            .syncRemoteAddressSpace("true")
            .useRemoteGateways(false)
            .virtualNetworkName("vnet1")
            .virtualNetworkPeeringName("peer")
            .build());

    }
}
resources:
  virtualNetworkPeering:
    type: azure-native:network:VirtualNetworkPeering
    properties:
      allowForwardedTraffic: true
      allowGatewayTransit: false
      allowVirtualNetworkAccess: true
      remoteVirtualNetwork:
        id: /subscriptions/subid/resourceGroups/peerTest/providers/Microsoft.Network/virtualNetworks/vnet2
      resourceGroupName: peerTest
      syncRemoteAddressSpace: 'true'
      useRemoteGateways: false
      virtualNetworkName: vnet1
      virtualNetworkPeeringName: peer

The syncRemoteAddressSpace property set to “true” triggers synchronization with the remote VNet’s current address space. This updates the peering to reflect any address range changes made to the remote network, avoiding manual reconfiguration when the remote topology evolves.

Beyond these examples

These snippets focus on specific peering-level features: full VNet and subnet-level peering, gateway transit and forwarded traffic, and address space synchronization. They’re intentionally minimal rather than complete network topologies.

The examples reference pre-existing infrastructure such as both local and remote virtual networks, subnets within those VNets for subnet peering, and VPN or ExpressRoute gateways for gateway transit scenarios. They focus on configuring the peering rather than provisioning the underlying networks.

To keep things focused, common peering patterns are omitted, including:

  • Reciprocal peering configuration (each peering is unidirectional)
  • BGP community configuration (remoteBgpCommunities)
  • Gateway verification controls (doNotVerifyRemoteGateways)
  • Peering state management (peeringState, peeringSyncLevel)

These omissions are intentional: the goal is to illustrate how each peering feature is wired, not provide drop-in network modules. See the VirtualNetworkPeering resource reference for all available configuration options.

Let's configure Azure Virtual Network Peering

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Gateway Configuration
Can I use remote gateways if my virtual network already has a gateway?
No, you cannot set useRemoteGateways to true if your virtual network already has a gateway. Only one peering can have this flag set to true.
What's the difference between allowGatewayTransit and useRemoteGateways?
allowGatewayTransit allows the remote VNet to use your VNet’s gateway for transit, while useRemoteGateways allows your VNet to use the remote VNet’s gateway. Both must be configured appropriately on both sides of the peering.
Subnet-Level Peering
How do I peer specific subnets instead of entire virtual networks?
Set peerCompleteVnets to false and specify the subnet names in localSubnetNames and remoteSubnetNames arrays.
Can I create an IPv6-only peering between subnets?
Yes, set enableOnlyIPv6Peering to true and peerCompleteVnets to false, then specify the subnets to peer in localSubnetNames and remoteSubnetNames.
Traffic & Access Control
What traffic control options are available for VNet peering?

You can control three types of traffic:

  1. Virtual network access - allowVirtualNetworkAccess controls whether VMs can communicate across peered VNets
  2. Forwarded traffic - allowForwardedTraffic controls whether traffic forwarded by VMs (not originating from them) is allowed
  3. Gateway transit - allowGatewayTransit controls whether the remote VNet can use your VNet’s gateway
Address Space Management
How do I keep my peering synchronized when the remote VNet's address space changes?
Set syncRemoteAddressSpace to “true” to automatically sync the peering with address space updates on the remote VNet.
What's the difference between remoteAddressSpace and remoteVirtualNetworkAddressSpace?
remoteAddressSpace is the address space that was peered initially, while remoteVirtualNetworkAddressSpace is the current address space of the remote VNet. These may differ if the remote VNet’s address space has been updated.
Cross-Region & Advanced Features
Can I peer virtual networks in different Azure regions?
Yes, the remote virtual network can be in the same or different region. Cross-region peering is a preview feature.
What properties can't be changed after creating a peering?
The resourceGroupName, virtualNetworkName, and virtualNetworkPeeringName properties are immutable and cannot be changed after creation.

Using a different cloud?

Explore networking guides for other cloud providers: