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 FREEFrequently Asked Questions
Gateway Configuration
useRemoteGateways to true if your virtual network already has a gateway. Only one peering can have this flag set to true.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
peerCompleteVnets to false and specify the subnet names in localSubnetNames and remoteSubnetNames arrays.enableOnlyIPv6Peering to true and peerCompleteVnets to false, then specify the subnets to peer in localSubnetNames and remoteSubnetNames.Traffic & Access Control
You can control three types of traffic:
- Virtual network access -
allowVirtualNetworkAccesscontrols whether VMs can communicate across peered VNets - Forwarded traffic -
allowForwardedTrafficcontrols whether traffic forwarded by VMs (not originating from them) is allowed - Gateway transit -
allowGatewayTransitcontrols whether the remote VNet can use your VNet’s gateway
Address Space Management
syncRemoteAddressSpace to “true” to automatically sync the peering with address space updates on the remote VNet.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
resourceGroupName, virtualNetworkName, and virtualNetworkPeeringName properties are immutable and cannot be changed after creation.