The aws:ec2/vpc:Vpc resource, part of the Pulumi AWS provider, provisions the VPC network container itself: its CIDR block, DNS settings, and instance tenancy. This guide focuses on two capabilities: CIDR block allocation (explicit and IPAM-managed) and tagging with instance tenancy configuration.
VPCs are foundational network containers. Subnets, route tables, security groups, and other networking resources are created separately and reference the VPC. The examples are intentionally small. Combine them with your own subnet, routing, and security group configuration.
Create a VPC with a CIDR block
Most AWS deployments begin by defining a VPC with an IPv4 CIDR block that determines the private IP address range available for resources.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const main = new aws.ec2.Vpc("main", {cidrBlock: "10.0.0.0/16"});
import pulumi
import pulumi_aws as aws
main = aws.ec2.Vpc("main", cidr_block="10.0.0.0/16")
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.NewVpc(ctx, "main", &ec2.VpcArgs{
CidrBlock: pulumi.String("10.0.0.0/16"),
})
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 main = new Aws.Ec2.Vpc("main", new()
{
CidrBlock = "10.0.0.0/16",
});
});
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 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 main = new Vpc("main", VpcArgs.builder()
.cidrBlock("10.0.0.0/16")
.build());
}
}
resources:
main:
type: aws:ec2:Vpc
properties:
cidrBlock: 10.0.0.0/16
The cidrBlock property sets the private IPv4 address range using CIDR notation. Common choices include 10.0.0.0/16 (65,536 addresses) or 172.16.0.0/12 (1,048,576 addresses). This range determines how many subnets and instances you can create within the VPC.
Add tags and instance tenancy settings
Production VPCs typically include organizational tags for cost tracking and resource identification, along with tenancy settings that control EC2 instance placement.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const main = new aws.ec2.Vpc("main", {
cidrBlock: "10.0.0.0/16",
instanceTenancy: "default",
tags: {
Name: "main",
},
});
import pulumi
import pulumi_aws as aws
main = aws.ec2.Vpc("main",
cidr_block="10.0.0.0/16",
instance_tenancy="default",
tags={
"Name": "main",
})
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.NewVpc(ctx, "main", &ec2.VpcArgs{
CidrBlock: pulumi.String("10.0.0.0/16"),
InstanceTenancy: pulumi.String("default"),
Tags: pulumi.StringMap{
"Name": pulumi.String("main"),
},
})
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 main = new Aws.Ec2.Vpc("main", new()
{
CidrBlock = "10.0.0.0/16",
InstanceTenancy = "default",
Tags =
{
{ "Name", "main" },
},
});
});
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 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 main = new Vpc("main", VpcArgs.builder()
.cidrBlock("10.0.0.0/16")
.instanceTenancy("default")
.tags(Map.of("Name", "main"))
.build());
}
}
resources:
main:
type: aws:ec2:Vpc
properties:
cidrBlock: 10.0.0.0/16
instanceTenancy: default
tags:
Name: main
The tags property adds key-value metadata for organization and cost allocation. The instanceTenancy property controls whether EC2 instances run on shared hardware (default) or dedicated hardware (dedicated). Dedicated tenancy incurs a $2 per hour regional fee plus per-instance charges, regardless of instance-level tenancy settings.
Allocate CIDR blocks from AWS IPAM
Organizations managing IP addresses across multiple accounts and regions use IPAM (IP Address Manager) to centralize allocation and prevent overlapping ranges.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const current = aws.getRegion({});
const test = new aws.ec2.VpcIpam("test", {operatingRegions: [{
regionName: current.then(current => current.region),
}]});
const testVpcIpamPool = new aws.ec2.VpcIpamPool("test", {
addressFamily: "ipv4",
ipamScopeId: test.privateDefaultScopeId,
locale: current.then(current => current.region),
});
const testVpcIpamPoolCidr = new aws.ec2.VpcIpamPoolCidr("test", {
ipamPoolId: testVpcIpamPool.id,
cidr: "172.20.0.0/16",
});
const testVpc = new aws.ec2.Vpc("test", {
ipv4IpamPoolId: testVpcIpamPool.id,
ipv4NetmaskLength: 28,
}, {
dependsOn: [testVpcIpamPoolCidr],
});
import pulumi
import pulumi_aws as aws
current = aws.get_region()
test = aws.ec2.VpcIpam("test", operating_regions=[{
"region_name": current.region,
}])
test_vpc_ipam_pool = aws.ec2.VpcIpamPool("test",
address_family="ipv4",
ipam_scope_id=test.private_default_scope_id,
locale=current.region)
test_vpc_ipam_pool_cidr = aws.ec2.VpcIpamPoolCidr("test",
ipam_pool_id=test_vpc_ipam_pool.id,
cidr="172.20.0.0/16")
test_vpc = aws.ec2.Vpc("test",
ipv4_ipam_pool_id=test_vpc_ipam_pool.id,
ipv4_netmask_length=28,
opts = pulumi.ResourceOptions(depends_on=[test_vpc_ipam_pool_cidr]))
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws"
"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 {
current, err := aws.GetRegion(ctx, &aws.GetRegionArgs{}, nil)
if err != nil {
return err
}
test, err := ec2.NewVpcIpam(ctx, "test", &ec2.VpcIpamArgs{
OperatingRegions: ec2.VpcIpamOperatingRegionArray{
&ec2.VpcIpamOperatingRegionArgs{
RegionName: pulumi.String(current.Region),
},
},
})
if err != nil {
return err
}
testVpcIpamPool, err := ec2.NewVpcIpamPool(ctx, "test", &ec2.VpcIpamPoolArgs{
AddressFamily: pulumi.String("ipv4"),
IpamScopeId: test.PrivateDefaultScopeId,
Locale: pulumi.String(current.Region),
})
if err != nil {
return err
}
testVpcIpamPoolCidr, err := ec2.NewVpcIpamPoolCidr(ctx, "test", &ec2.VpcIpamPoolCidrArgs{
IpamPoolId: testVpcIpamPool.ID(),
Cidr: pulumi.String("172.20.0.0/16"),
})
if err != nil {
return err
}
_, err = ec2.NewVpc(ctx, "test", &ec2.VpcArgs{
Ipv4IpamPoolId: testVpcIpamPool.ID(),
Ipv4NetmaskLength: pulumi.Int(28),
}, pulumi.DependsOn([]pulumi.Resource{
testVpcIpamPoolCidr,
}))
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 current = Aws.GetRegion.Invoke();
var test = new Aws.Ec2.VpcIpam("test", new()
{
OperatingRegions = new[]
{
new Aws.Ec2.Inputs.VpcIpamOperatingRegionArgs
{
RegionName = current.Apply(getRegionResult => getRegionResult.Region),
},
},
});
var testVpcIpamPool = new Aws.Ec2.VpcIpamPool("test", new()
{
AddressFamily = "ipv4",
IpamScopeId = test.PrivateDefaultScopeId,
Locale = current.Apply(getRegionResult => getRegionResult.Region),
});
var testVpcIpamPoolCidr = new Aws.Ec2.VpcIpamPoolCidr("test", new()
{
IpamPoolId = testVpcIpamPool.Id,
Cidr = "172.20.0.0/16",
});
var testVpc = new Aws.Ec2.Vpc("test", new()
{
Ipv4IpamPoolId = testVpcIpamPool.Id,
Ipv4NetmaskLength = 28,
}, new CustomResourceOptions
{
DependsOn =
{
testVpcIpamPoolCidr,
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.AwsFunctions;
import com.pulumi.aws.inputs.GetRegionArgs;
import com.pulumi.aws.ec2.VpcIpam;
import com.pulumi.aws.ec2.VpcIpamArgs;
import com.pulumi.aws.ec2.inputs.VpcIpamOperatingRegionArgs;
import com.pulumi.aws.ec2.VpcIpamPool;
import com.pulumi.aws.ec2.VpcIpamPoolArgs;
import com.pulumi.aws.ec2.VpcIpamPoolCidr;
import com.pulumi.aws.ec2.VpcIpamPoolCidrArgs;
import com.pulumi.aws.ec2.Vpc;
import com.pulumi.aws.ec2.VpcArgs;
import com.pulumi.resources.CustomResourceOptions;
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) {
final var current = AwsFunctions.getRegion(GetRegionArgs.builder()
.build());
var test = new VpcIpam("test", VpcIpamArgs.builder()
.operatingRegions(VpcIpamOperatingRegionArgs.builder()
.regionName(current.region())
.build())
.build());
var testVpcIpamPool = new VpcIpamPool("testVpcIpamPool", VpcIpamPoolArgs.builder()
.addressFamily("ipv4")
.ipamScopeId(test.privateDefaultScopeId())
.locale(current.region())
.build());
var testVpcIpamPoolCidr = new VpcIpamPoolCidr("testVpcIpamPoolCidr", VpcIpamPoolCidrArgs.builder()
.ipamPoolId(testVpcIpamPool.id())
.cidr("172.20.0.0/16")
.build());
var testVpc = new Vpc("testVpc", VpcArgs.builder()
.ipv4IpamPoolId(testVpcIpamPool.id())
.ipv4NetmaskLength(28)
.build(), CustomResourceOptions.builder()
.dependsOn(testVpcIpamPoolCidr)
.build());
}
}
resources:
test:
type: aws:ec2:VpcIpam
properties:
operatingRegions:
- regionName: ${current.region}
testVpcIpamPool:
type: aws:ec2:VpcIpamPool
name: test
properties:
addressFamily: ipv4
ipamScopeId: ${test.privateDefaultScopeId}
locale: ${current.region}
testVpcIpamPoolCidr:
type: aws:ec2:VpcIpamPoolCidr
name: test
properties:
ipamPoolId: ${testVpcIpamPool.id}
cidr: 172.20.0.0/16
testVpc:
type: aws:ec2:Vpc
name: test
properties:
ipv4IpamPoolId: ${testVpcIpamPool.id}
ipv4NetmaskLength: 28
options:
dependsOn:
- ${testVpcIpamPoolCidr}
variables:
current:
fn::invoke:
function: aws:getRegion
arguments: {}
Instead of specifying cidrBlock directly, you reference an IPAM pool via ipv4IpamPoolId and request a netmask length via ipv4NetmaskLength. IPAM allocates a CIDR block from the pool automatically, ensuring no conflicts across your organization. The dependsOn ensures the pool has allocated CIDR ranges before VPC creation.
Beyond these examples
These snippets focus on specific VPC-level features: CIDR block allocation (explicit and IPAM-managed) and tagging and instance tenancy. They’re intentionally minimal rather than full network deployments.
The IPAM example references pre-existing infrastructure such as IPAM pools with allocated CIDR ranges. It focuses on configuring the VPC rather than provisioning the complete networking stack.
To keep things focused, common VPC patterns are omitted, including:
- DNS settings (enableDnsSupport, enableDnsHostnames)
- IPv6 configuration (assignGeneratedIpv6CidrBlock, ipv6IpamPoolId)
- Network address usage metrics (enableNetworkAddressUsageMetrics)
These omissions are intentional: the goal is to illustrate how each VPC feature is wired, not provide drop-in network modules. See the VPC resource reference for all available configuration options.
Let's create and Configure AWS VPCs
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
CIDR & IP Address Management
cidrBlock, ipv4IpamPoolId, and ipv4NetmaskLength properties are immutable and cannot be modified after VPC creation.ipv4IpamPoolId and ipv4NetmaskLength, then add a dependsOn for the aws.ec2.VpcIpamPoolCidr resource as shown in the IPAM example.assignGeneratedIpv6CidrBlock for an Amazon-provided /56 block, ipv6IpamPoolId with ipv6NetmaskLength for IPAM allocation, or ipv6CidrBlock for an explicit CIDR. Note that assignGeneratedIpv6CidrBlock conflicts with ipv6IpamPoolId, and ipv6NetmaskLength conflicts with ipv6CidrBlock.Instance Tenancy & Costs
instanceTenancy to dedicated incurs a $2 per hour per region fee, plus an hourly per-instance usage fee.default tenancy, instances use the tenancy attribute specified at launch. With dedicated tenancy, all instances launched in the VPC run on dedicated hardware regardless of the launch-time attribute.DNS Configuration
enableDnsSupport) defaults to true, while DNS hostnames (enableDnsHostnames) defaults to false.Advanced Configuration
ipv6CidrBlockNetworkBorderGroup to specify a Network Border Group such as a Local Zone. By default, it’s set to the VPC’s region.defaultNetworkAclId), default route table (defaultRouteTableId), and default security group (defaultSecurityGroupId) when you create a VPC.