The aws:ec2/vpcPeeringConnection:VpcPeeringConnection resource, part of the Pulumi AWS provider, creates a VPC peering connection request between two VPCs, establishing the foundation for private network connectivity. This guide focuses on three capabilities: same-account and cross-region peering, DNS resolution configuration, and automatic acceptance.
Peering connections reference existing VPCs and require either manual acceptance or automatic acceptance (same account and region only). For cross-account peering, use the VpcPeeringConnectionAccepter resource to manage the accepter side. The examples are intentionally small. Combine them with route table updates and security group rules to enable actual traffic flow.
Connect two VPCs in the same account
Applications often need to share resources across VPCs, such as connecting a production VPC to a shared services VPC for centralized logging or monitoring.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const foo = new aws.ec2.VpcPeeringConnection("foo", {
peerOwnerId: peerOwnerId,
peerVpcId: bar.id,
vpcId: fooAwsVpc.id,
});
import pulumi
import pulumi_aws as aws
foo = aws.ec2.VpcPeeringConnection("foo",
peer_owner_id=peer_owner_id,
peer_vpc_id=bar["id"],
vpc_id=foo_aws_vpc["id"])
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := ec2.NewVpcPeeringConnection(ctx, "foo", &ec2.VpcPeeringConnectionArgs{
PeerOwnerId: pulumi.Any(peerOwnerId),
PeerVpcId: pulumi.Any(bar.Id),
VpcId: pulumi.Any(fooAwsVpc.Id),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var foo = new Aws.Ec2.VpcPeeringConnection("foo", new()
{
PeerOwnerId = peerOwnerId,
PeerVpcId = bar.Id,
VpcId = fooAwsVpc.Id,
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ec2.VpcPeeringConnection;
import com.pulumi.aws.ec2.VpcPeeringConnectionArgs;
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 foo = new VpcPeeringConnection("foo", VpcPeeringConnectionArgs.builder()
.peerOwnerId(peerOwnerId)
.peerVpcId(bar.id())
.vpcId(fooAwsVpc.id())
.build());
}
}
resources:
foo:
type: aws:ec2:VpcPeeringConnection
properties:
peerOwnerId: ${peerOwnerId}
peerVpcId: ${bar.id}
vpcId: ${fooAwsVpc.id}
The vpcId property identifies the requester VPC, while peerVpcId identifies the target VPC. The peerOwnerId specifies the AWS account that owns the peer VPC (defaults to your current account). This creates a peering request that requires manual acceptance via the AWS console or a VpcPeeringConnectionAccepter resource before traffic can flow.
Enable DNS resolution across peered VPCs
When applications in peered VPCs need to resolve each other’s private DNS names, both sides must enable DNS resolution options.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const foo = new aws.ec2.VpcPeeringConnection("foo", {
peerOwnerId: peerOwnerId,
peerVpcId: bar.id,
vpcId: fooAwsVpc.id,
accepter: {
allowRemoteVpcDnsResolution: true,
},
requester: {
allowRemoteVpcDnsResolution: true,
},
});
import pulumi
import pulumi_aws as aws
foo = aws.ec2.VpcPeeringConnection("foo",
peer_owner_id=peer_owner_id,
peer_vpc_id=bar["id"],
vpc_id=foo_aws_vpc["id"],
accepter={
"allow_remote_vpc_dns_resolution": True,
},
requester={
"allow_remote_vpc_dns_resolution": True,
})
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := ec2.NewVpcPeeringConnection(ctx, "foo", &ec2.VpcPeeringConnectionArgs{
PeerOwnerId: pulumi.Any(peerOwnerId),
PeerVpcId: pulumi.Any(bar.Id),
VpcId: pulumi.Any(fooAwsVpc.Id),
Accepter: &ec2.VpcPeeringConnectionAccepterTypeArgs{
AllowRemoteVpcDnsResolution: pulumi.Bool(true),
},
Requester: &ec2.VpcPeeringConnectionRequesterArgs{
AllowRemoteVpcDnsResolution: pulumi.Bool(true),
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var foo = new Aws.Ec2.VpcPeeringConnection("foo", new()
{
PeerOwnerId = peerOwnerId,
PeerVpcId = bar.Id,
VpcId = fooAwsVpc.Id,
Accepter = new Aws.Ec2.Inputs.VpcPeeringConnectionAccepterArgs
{
AllowRemoteVpcDnsResolution = true,
},
Requester = new Aws.Ec2.Inputs.VpcPeeringConnectionRequesterArgs
{
AllowRemoteVpcDnsResolution = true,
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ec2.VpcPeeringConnection;
import com.pulumi.aws.ec2.VpcPeeringConnectionArgs;
import com.pulumi.aws.ec2.inputs.VpcPeeringConnectionAccepterArgs;
import com.pulumi.aws.ec2.inputs.VpcPeeringConnectionRequesterArgs;
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 foo = new VpcPeeringConnection("foo", VpcPeeringConnectionArgs.builder()
.peerOwnerId(peerOwnerId)
.peerVpcId(bar.id())
.vpcId(fooAwsVpc.id())
.accepter(VpcPeeringConnectionAccepterArgs.builder()
.allowRemoteVpcDnsResolution(true)
.build())
.requester(VpcPeeringConnectionRequesterArgs.builder()
.allowRemoteVpcDnsResolution(true)
.build())
.build());
}
}
resources:
foo:
type: aws:ec2:VpcPeeringConnection
properties:
peerOwnerId: ${peerOwnerId}
peerVpcId: ${bar.id}
vpcId: ${fooAwsVpc.id}
accepter:
allowRemoteVpcDnsResolution: true
requester:
allowRemoteVpcDnsResolution: true
The accepter and requester blocks configure connection options for each side of the peering. Setting allowRemoteVpcDnsResolution to true on both sides allows resources to resolve private DNS names across the connection. Without this, you’d need to use IP addresses or manage custom DNS entries.
Auto-accept peering within the same account and region
For same-account, same-region peering, automatic acceptance eliminates the manual approval step and makes the connection immediately active.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const fooVpc = new aws.ec2.Vpc("foo", {cidrBlock: "10.1.0.0/16"});
const bar = new aws.ec2.Vpc("bar", {cidrBlock: "10.2.0.0/16"});
const foo = new aws.ec2.VpcPeeringConnection("foo", {
peerOwnerId: peerOwnerId,
peerVpcId: bar.id,
vpcId: fooVpc.id,
autoAccept: true,
tags: {
Name: "VPC Peering between foo and bar",
},
});
import pulumi
import pulumi_aws as aws
foo_vpc = aws.ec2.Vpc("foo", cidr_block="10.1.0.0/16")
bar = aws.ec2.Vpc("bar", cidr_block="10.2.0.0/16")
foo = aws.ec2.VpcPeeringConnection("foo",
peer_owner_id=peer_owner_id,
peer_vpc_id=bar.id,
vpc_id=foo_vpc.id,
auto_accept=True,
tags={
"Name": "VPC Peering between foo and bar",
})
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
fooVpc, err := ec2.NewVpc(ctx, "foo", &ec2.VpcArgs{
CidrBlock: pulumi.String("10.1.0.0/16"),
})
if err != nil {
return err
}
bar, err := ec2.NewVpc(ctx, "bar", &ec2.VpcArgs{
CidrBlock: pulumi.String("10.2.0.0/16"),
})
if err != nil {
return err
}
_, err = ec2.NewVpcPeeringConnection(ctx, "foo", &ec2.VpcPeeringConnectionArgs{
PeerOwnerId: pulumi.Any(peerOwnerId),
PeerVpcId: bar.ID(),
VpcId: fooVpc.ID(),
AutoAccept: pulumi.Bool(true),
Tags: pulumi.StringMap{
"Name": pulumi.String("VPC Peering between foo and bar"),
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var fooVpc = new Aws.Ec2.Vpc("foo", new()
{
CidrBlock = "10.1.0.0/16",
});
var bar = new Aws.Ec2.Vpc("bar", new()
{
CidrBlock = "10.2.0.0/16",
});
var foo = new Aws.Ec2.VpcPeeringConnection("foo", new()
{
PeerOwnerId = peerOwnerId,
PeerVpcId = bar.Id,
VpcId = fooVpc.Id,
AutoAccept = true,
Tags =
{
{ "Name", "VPC Peering between foo and bar" },
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ec2.Vpc;
import com.pulumi.aws.ec2.VpcArgs;
import com.pulumi.aws.ec2.VpcPeeringConnection;
import com.pulumi.aws.ec2.VpcPeeringConnectionArgs;
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 fooVpc = new Vpc("fooVpc", VpcArgs.builder()
.cidrBlock("10.1.0.0/16")
.build());
var bar = new Vpc("bar", VpcArgs.builder()
.cidrBlock("10.2.0.0/16")
.build());
var foo = new VpcPeeringConnection("foo", VpcPeeringConnectionArgs.builder()
.peerOwnerId(peerOwnerId)
.peerVpcId(bar.id())
.vpcId(fooVpc.id())
.autoAccept(true)
.tags(Map.of("Name", "VPC Peering between foo and bar"))
.build());
}
}
resources:
foo:
type: aws:ec2:VpcPeeringConnection
properties:
peerOwnerId: ${peerOwnerId}
peerVpcId: ${bar.id}
vpcId: ${fooVpc.id}
autoAccept: true
tags:
Name: VPC Peering between foo and bar
fooVpc:
type: aws:ec2:Vpc
name: foo
properties:
cidrBlock: 10.1.0.0/16
bar:
type: aws:ec2:Vpc
properties:
cidrBlock: 10.2.0.0/16
The autoAccept property bypasses the manual acceptance workflow when both VPCs are in the same AWS account and region. The connection becomes active immediately, and you can add tags for organization and cost tracking. This only works for same-account, same-region scenarios; cross-account or cross-region peering requires manual acceptance.
Peer VPCs across AWS regions
Multi-region architectures often need to connect VPCs in different regions for disaster recovery or global application deployments.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const fooVpc = new aws.ec2.Vpc("foo", {cidrBlock: "10.1.0.0/16"});
const bar = new aws.ec2.Vpc("bar", {cidrBlock: "10.2.0.0/16"});
const foo = new aws.ec2.VpcPeeringConnection("foo", {
peerOwnerId: peerOwnerId,
peerVpcId: bar.id,
vpcId: fooVpc.id,
peerRegion: "us-east-1",
});
import pulumi
import pulumi_aws as aws
foo_vpc = aws.ec2.Vpc("foo", cidr_block="10.1.0.0/16")
bar = aws.ec2.Vpc("bar", cidr_block="10.2.0.0/16")
foo = aws.ec2.VpcPeeringConnection("foo",
peer_owner_id=peer_owner_id,
peer_vpc_id=bar.id,
vpc_id=foo_vpc.id,
peer_region="us-east-1")
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
fooVpc, err := ec2.NewVpc(ctx, "foo", &ec2.VpcArgs{
CidrBlock: pulumi.String("10.1.0.0/16"),
})
if err != nil {
return err
}
bar, err := ec2.NewVpc(ctx, "bar", &ec2.VpcArgs{
CidrBlock: pulumi.String("10.2.0.0/16"),
})
if err != nil {
return err
}
_, err = ec2.NewVpcPeeringConnection(ctx, "foo", &ec2.VpcPeeringConnectionArgs{
PeerOwnerId: pulumi.Any(peerOwnerId),
PeerVpcId: bar.ID(),
VpcId: fooVpc.ID(),
PeerRegion: pulumi.String("us-east-1"),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var fooVpc = new Aws.Ec2.Vpc("foo", new()
{
CidrBlock = "10.1.0.0/16",
});
var bar = new Aws.Ec2.Vpc("bar", new()
{
CidrBlock = "10.2.0.0/16",
});
var foo = new Aws.Ec2.VpcPeeringConnection("foo", new()
{
PeerOwnerId = peerOwnerId,
PeerVpcId = bar.Id,
VpcId = fooVpc.Id,
PeerRegion = "us-east-1",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ec2.Vpc;
import com.pulumi.aws.ec2.VpcArgs;
import com.pulumi.aws.ec2.VpcPeeringConnection;
import com.pulumi.aws.ec2.VpcPeeringConnectionArgs;
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 fooVpc = new Vpc("fooVpc", VpcArgs.builder()
.cidrBlock("10.1.0.0/16")
.build());
var bar = new Vpc("bar", VpcArgs.builder()
.cidrBlock("10.2.0.0/16")
.build());
var foo = new VpcPeeringConnection("foo", VpcPeeringConnectionArgs.builder()
.peerOwnerId(peerOwnerId)
.peerVpcId(bar.id())
.vpcId(fooVpc.id())
.peerRegion("us-east-1")
.build());
}
}
resources:
foo:
type: aws:ec2:VpcPeeringConnection
properties:
peerOwnerId: ${peerOwnerId}
peerVpcId: ${bar.id}
vpcId: ${fooVpc.id}
peerRegion: us-east-1
fooVpc:
type: aws:ec2:Vpc
name: foo
properties:
cidrBlock: 10.1.0.0/16
bar:
type: aws:ec2:Vpc
properties:
cidrBlock: 10.2.0.0/16
The peerRegion property specifies the AWS region of the accepter VPC. Cross-region peering requires manual acceptance via a VpcPeeringConnectionAccepter resource in the peer region; autoAccept cannot be used. The connection enables private communication between regions without traversing the public internet.
Beyond these examples
These snippets focus on specific peering connection features: same-account and cross-region peering, DNS resolution options, and automatic acceptance for same-account connections. They’re intentionally minimal rather than full network architectures.
The examples reference pre-existing infrastructure such as VPCs in requester and accepter accounts or regions. They focus on creating the peering connection rather than provisioning the surrounding network infrastructure.
To keep things focused, common peering patterns are omitted, including:
- Cross-account peering (requires VpcPeeringConnectionAccepter)
- Route table updates to enable traffic flow
- Security group rules for cross-VPC access
- Connection options management via VpcPeeringConnectionOptions
These omissions are intentional: the goal is to illustrate how each peering feature is wired, not provide drop-in networking modules. See the VPC Peering Connection resource reference for all available configuration options.
Let's configure AWS VPC Peering Connections
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Resource Management & Conflicts
aws.ec2.VpcPeeringConnection resources with the same peerVpcId and vpcId doesn’t produce an error. Instead, AWS returns the existing connection ID, resulting in multiple resources managing the same peering connection. Ensure unique VPC pairs or use data sources to reference existing connections.VpcPeeringConnectionOptions resource and the accepter/requester attributes causes conflicts and overwrites settings. Choose one approach: either use the standalone Options resource (recommended for cross-account scenarios) or the inline attributes.autoAccept for automatic activation, or manually activate the connection via AWS Console, CLI, or SDKs before modifying options.Cross-Account & Inter-Region Setup
autoAccept: true only when both VPCs are in the same AWS account and region. For cross-account or inter-region peering, set autoAccept: false and use aws.ec2.VpcPeeringConnectionAccepter in the accepter’s account to manage their side of the connection.peerRegion to the accepter VPC’s region, ensure autoAccept is false, and use aws.ec2.VpcPeeringConnectionAccepter to manage the accepter side. The peerRegion property is immutable after creation.Configuration & Options
allowRemoteVpcDnsResolution: true in the accepter and/or requester blocks to enable DNS hostname resolution across the peering connection.peerOwnerId, peerRegion, peerVpcId, and vpcId properties are immutable. Changing these requires recreating the peering connection.Using a different cloud?
Explore networking guides for other cloud providers: