Configure AWS VPC Peering Connections

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 FREE

Frequently Asked Questions

Resource Management & Conflicts
Why do I have multiple Pulumi resources with the same peering connection ID?
Creating multiple 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.
Can I use both VpcPeeringConnectionOptions and the accepter/requester attributes?
No, managing options in both a standalone 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.
Why can't I modify my peering connection options?
VPC Peering Connection options can only be modified when the peering connection is active. Use autoAccept for automatic activation, or manually activate the connection via AWS Console, CLI, or SDKs before modifying options.
Cross-Account & Inter-Region Setup
When should I use autoAccept vs VpcPeeringConnectionAccepter?
Use 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.
How do I set up inter-region VPC peering?
Set 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
How do I enable DNS resolution across peered VPCs?
Configure allowRemoteVpcDnsResolution: true in the accepter and/or requester blocks to enable DNS hostname resolution across the peering connection.
What properties can't I change after creating a peering connection?
The 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: