The aws:ec2/instance:Instance resource, part of the Pulumi AWS provider, defines an EC2 virtual machine instance: its AMI, instance type, networking, and compute options. This guide focuses on four capabilities: dynamic AMI selection, spot instance requests, network interface attachment, and CPU topology configuration.
Instances require an AMI and run in a VPC with subnets and security groups. Most examples use the default VPC; some create VPC infrastructure inline. The examples are intentionally small. Combine them with your own VPC, security groups, SSH keys, and user data scripts.
Launch an instance with dynamic AMI lookup
Most deployments begin by selecting an AMI that matches OS and architecture requirements. Dynamic AMI lookup ensures you always use the latest version without hardcoding IDs.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const ubuntu = aws.ec2.getAmi({
mostRecent: true,
filters: [
{
name: "name",
values: ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"],
},
{
name: "virtualization-type",
values: ["hvm"],
},
],
owners: ["099720109477"],
});
const example = new aws.ec2.Instance("example", {
ami: ubuntu.then(ubuntu => ubuntu.id),
instanceType: aws.ec2.InstanceType.T3_Micro,
tags: {
Name: "HelloWorld",
},
});
import pulumi
import pulumi_aws as aws
ubuntu = aws.ec2.get_ami(most_recent=True,
filters=[
{
"name": "name",
"values": ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"],
},
{
"name": "virtualization-type",
"values": ["hvm"],
},
],
owners=["099720109477"])
example = aws.ec2.Instance("example",
ami=ubuntu.id,
instance_type=aws.ec2.InstanceType.T3_MICRO,
tags={
"Name": "HelloWorld",
})
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 {
ubuntu, err := ec2.LookupAmi(ctx, &ec2.LookupAmiArgs{
MostRecent: pulumi.BoolRef(true),
Filters: []ec2.GetAmiFilter{
{
Name: "name",
Values: []string{
"ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*",
},
},
{
Name: "virtualization-type",
Values: []string{
"hvm",
},
},
},
Owners: []string{
"099720109477",
},
}, nil)
if err != nil {
return err
}
_, err = ec2.NewInstance(ctx, "example", &ec2.InstanceArgs{
Ami: pulumi.String(ubuntu.Id),
InstanceType: pulumi.String(ec2.InstanceType_T3_Micro),
Tags: pulumi.StringMap{
"Name": pulumi.String("HelloWorld"),
},
})
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 ubuntu = Aws.Ec2.GetAmi.Invoke(new()
{
MostRecent = true,
Filters = new[]
{
new Aws.Ec2.Inputs.GetAmiFilterInputArgs
{
Name = "name",
Values = new[]
{
"ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*",
},
},
new Aws.Ec2.Inputs.GetAmiFilterInputArgs
{
Name = "virtualization-type",
Values = new[]
{
"hvm",
},
},
},
Owners = new[]
{
"099720109477",
},
});
var example = new Aws.Ec2.Instance("example", new()
{
Ami = ubuntu.Apply(getAmiResult => getAmiResult.Id),
InstanceType = Aws.Ec2.InstanceType.T3_Micro,
Tags =
{
{ "Name", "HelloWorld" },
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ec2.Ec2Functions;
import com.pulumi.aws.ec2.inputs.GetAmiArgs;
import com.pulumi.aws.ec2.Instance;
import com.pulumi.aws.ec2.InstanceArgs;
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 ubuntu = Ec2Functions.getAmi(GetAmiArgs.builder()
.mostRecent(true)
.filters(
GetAmiFilterArgs.builder()
.name("name")
.values("ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*")
.build(),
GetAmiFilterArgs.builder()
.name("virtualization-type")
.values("hvm")
.build())
.owners("099720109477")
.build());
var example = new Instance("example", InstanceArgs.builder()
.ami(ubuntu.id())
.instanceType("t3.micro")
.tags(Map.of("Name", "HelloWorld"))
.build());
}
}
resources:
example:
type: aws:ec2:Instance
properties:
ami: ${ubuntu.id}
instanceType: t3.micro
tags:
Name: HelloWorld
variables:
ubuntu:
fn::invoke:
function: aws:ec2:getAmi
arguments:
mostRecent: true
filters:
- name: name
values:
- ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*
- name: virtualization-type
values:
- hvm
owners:
- '099720109477'
The getAmi data source queries AWS for AMIs matching your filters. The filters property narrows results by name pattern and virtualization type, while owners restricts results to trusted publishers (099720109477 is Canonical’s AWS account). The ami property references the data source result, ensuring the instance always launches with the latest matching AMI.
Reference AMIs from Systems Manager Parameter Store
AWS publishes AMI IDs to Systems Manager Parameter Store, providing stable paths to official AMI catalogs that update automatically.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.ec2.Instance("example", {
ami: "resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64",
instanceType: aws.ec2.InstanceType.T3_Micro,
tags: {
Name: "HelloWorld",
},
});
import pulumi
import pulumi_aws as aws
example = aws.ec2.Instance("example",
ami="resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64",
instance_type=aws.ec2.InstanceType.T3_MICRO,
tags={
"Name": "HelloWorld",
})
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.NewInstance(ctx, "example", &ec2.InstanceArgs{
Ami: pulumi.String("resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64"),
InstanceType: pulumi.String(ec2.InstanceType_T3_Micro),
Tags: pulumi.StringMap{
"Name": pulumi.String("HelloWorld"),
},
})
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 example = new Aws.Ec2.Instance("example", new()
{
Ami = "resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64",
InstanceType = Aws.Ec2.InstanceType.T3_Micro,
Tags =
{
{ "Name", "HelloWorld" },
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ec2.Instance;
import com.pulumi.aws.ec2.InstanceArgs;
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 example = new Instance("example", InstanceArgs.builder()
.ami("resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64")
.instanceType("t3.micro")
.tags(Map.of("Name", "HelloWorld"))
.build());
}
}
resources:
example:
type: aws:ec2:Instance
properties:
ami: resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64
instanceType: t3.micro
tags:
Name: HelloWorld
The ami property uses the resolve:ssm: prefix to fetch AMI IDs from Parameter Store at deployment time. This approach provides stable parameter paths that AWS maintains, eliminating the need for custom AMI lookup logic. Your IAM credentials must have permissions to read SSM parameters.
Request spot instances with price limits
Workloads that can tolerate interruption often use spot instances to reduce compute costs by bidding on spare capacity.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = aws.ec2.getAmi({
mostRecent: true,
owners: ["amazon"],
filters: [
{
name: "architecture",
values: ["arm64"],
},
{
name: "name",
values: ["al2023-ami-2023*"],
},
],
});
const exampleInstance = new aws.ec2.Instance("example", {
ami: example.then(example => example.id),
instanceMarketOptions: {
marketType: "spot",
spotOptions: {
maxPrice: "0.0031",
},
},
instanceType: aws.ec2.InstanceType.T4g_Nano,
tags: {
Name: "test-spot",
},
});
import pulumi
import pulumi_aws as aws
example = aws.ec2.get_ami(most_recent=True,
owners=["amazon"],
filters=[
{
"name": "architecture",
"values": ["arm64"],
},
{
"name": "name",
"values": ["al2023-ami-2023*"],
},
])
example_instance = aws.ec2.Instance("example",
ami=example.id,
instance_market_options={
"market_type": "spot",
"spot_options": {
"max_price": "0.0031",
},
},
instance_type=aws.ec2.InstanceType.T4G_NANO,
tags={
"Name": "test-spot",
})
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 {
example, err := ec2.LookupAmi(ctx, &ec2.LookupAmiArgs{
MostRecent: pulumi.BoolRef(true),
Owners: []string{
"amazon",
},
Filters: []ec2.GetAmiFilter{
{
Name: "architecture",
Values: []string{
"arm64",
},
},
{
Name: "name",
Values: []string{
"al2023-ami-2023*",
},
},
},
}, nil)
if err != nil {
return err
}
_, err = ec2.NewInstance(ctx, "example", &ec2.InstanceArgs{
Ami: pulumi.String(example.Id),
InstanceMarketOptions: &ec2.InstanceInstanceMarketOptionsArgs{
MarketType: pulumi.String("spot"),
SpotOptions: &ec2.InstanceInstanceMarketOptionsSpotOptionsArgs{
MaxPrice: pulumi.String("0.0031"),
},
},
InstanceType: pulumi.String(ec2.InstanceType_T4g_Nano),
Tags: pulumi.StringMap{
"Name": pulumi.String("test-spot"),
},
})
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 example = Aws.Ec2.GetAmi.Invoke(new()
{
MostRecent = true,
Owners = new[]
{
"amazon",
},
Filters = new[]
{
new Aws.Ec2.Inputs.GetAmiFilterInputArgs
{
Name = "architecture",
Values = new[]
{
"arm64",
},
},
new Aws.Ec2.Inputs.GetAmiFilterInputArgs
{
Name = "name",
Values = new[]
{
"al2023-ami-2023*",
},
},
},
});
var exampleInstance = new Aws.Ec2.Instance("example", new()
{
Ami = example.Apply(getAmiResult => getAmiResult.Id),
InstanceMarketOptions = new Aws.Ec2.Inputs.InstanceInstanceMarketOptionsArgs
{
MarketType = "spot",
SpotOptions = new Aws.Ec2.Inputs.InstanceInstanceMarketOptionsSpotOptionsArgs
{
MaxPrice = "0.0031",
},
},
InstanceType = Aws.Ec2.InstanceType.T4g_Nano,
Tags =
{
{ "Name", "test-spot" },
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ec2.Ec2Functions;
import com.pulumi.aws.ec2.inputs.GetAmiArgs;
import com.pulumi.aws.ec2.Instance;
import com.pulumi.aws.ec2.InstanceArgs;
import com.pulumi.aws.ec2.inputs.InstanceInstanceMarketOptionsArgs;
import com.pulumi.aws.ec2.inputs.InstanceInstanceMarketOptionsSpotOptionsArgs;
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 example = Ec2Functions.getAmi(GetAmiArgs.builder()
.mostRecent(true)
.owners("amazon")
.filters(
GetAmiFilterArgs.builder()
.name("architecture")
.values("arm64")
.build(),
GetAmiFilterArgs.builder()
.name("name")
.values("al2023-ami-2023*")
.build())
.build());
var exampleInstance = new Instance("exampleInstance", InstanceArgs.builder()
.ami(example.id())
.instanceMarketOptions(InstanceInstanceMarketOptionsArgs.builder()
.marketType("spot")
.spotOptions(InstanceInstanceMarketOptionsSpotOptionsArgs.builder()
.maxPrice("0.0031")
.build())
.build())
.instanceType("t4g.nano")
.tags(Map.of("Name", "test-spot"))
.build());
}
}
resources:
exampleInstance:
type: aws:ec2:Instance
name: example
properties:
ami: ${example.id}
instanceMarketOptions:
marketType: spot
spotOptions:
maxPrice: 0.0031
instanceType: t4g.nano
tags:
Name: test-spot
variables:
example:
fn::invoke:
function: aws:ec2:getAmi
arguments:
mostRecent: true
owners:
- amazon
filters:
- name: architecture
values:
- arm64
- name: name
values:
- al2023-ami-2023*
The instanceMarketOptions property configures spot instance behavior. Setting marketType to “spot” requests spare capacity, while maxPrice caps your bid. When spot prices exceed your limit or capacity becomes unavailable, AWS terminates the instance. The example doesn’t configure spotInstanceInterruptionBehavior, so AWS defaults to terminating the instance on interruption.
Attach custom network interfaces at launch
Some applications require specific IP addresses or network configuration that must be defined at launch time rather than using default VPC networking.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const myVpc = new aws.ec2.Vpc("my_vpc", {
cidrBlock: "172.16.0.0/16",
tags: {
Name: "tf-example",
},
});
const mySubnet = new aws.ec2.Subnet("my_subnet", {
vpcId: myVpc.id,
cidrBlock: "172.16.10.0/24",
availabilityZone: "us-west-2a",
tags: {
Name: "tf-example",
},
});
const example = new aws.ec2.NetworkInterface("example", {
subnetId: mySubnet.id,
privateIps: ["172.16.10.100"],
tags: {
Name: "primary_network_interface",
},
});
const exampleInstance = new aws.ec2.Instance("example", {
ami: "ami-005e54dee72cc1d00",
instanceType: aws.ec2.InstanceType.T2_Micro,
primaryNetworkInterface: {
networkInterfaceId: example.id,
},
creditSpecification: {
cpuCredits: "unlimited",
},
});
import pulumi
import pulumi_aws as aws
my_vpc = aws.ec2.Vpc("my_vpc",
cidr_block="172.16.0.0/16",
tags={
"Name": "tf-example",
})
my_subnet = aws.ec2.Subnet("my_subnet",
vpc_id=my_vpc.id,
cidr_block="172.16.10.0/24",
availability_zone="us-west-2a",
tags={
"Name": "tf-example",
})
example = aws.ec2.NetworkInterface("example",
subnet_id=my_subnet.id,
private_ips=["172.16.10.100"],
tags={
"Name": "primary_network_interface",
})
example_instance = aws.ec2.Instance("example",
ami="ami-005e54dee72cc1d00",
instance_type=aws.ec2.InstanceType.T2_MICRO,
primary_network_interface={
"network_interface_id": example.id,
},
credit_specification={
"cpu_credits": "unlimited",
})
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 {
myVpc, err := ec2.NewVpc(ctx, "my_vpc", &ec2.VpcArgs{
CidrBlock: pulumi.String("172.16.0.0/16"),
Tags: pulumi.StringMap{
"Name": pulumi.String("tf-example"),
},
})
if err != nil {
return err
}
mySubnet, err := ec2.NewSubnet(ctx, "my_subnet", &ec2.SubnetArgs{
VpcId: myVpc.ID(),
CidrBlock: pulumi.String("172.16.10.0/24"),
AvailabilityZone: pulumi.String("us-west-2a"),
Tags: pulumi.StringMap{
"Name": pulumi.String("tf-example"),
},
})
if err != nil {
return err
}
example, err := ec2.NewNetworkInterface(ctx, "example", &ec2.NetworkInterfaceArgs{
SubnetId: mySubnet.ID(),
PrivateIps: pulumi.StringArray{
pulumi.String("172.16.10.100"),
},
Tags: pulumi.StringMap{
"Name": pulumi.String("primary_network_interface"),
},
})
if err != nil {
return err
}
_, err = ec2.NewInstance(ctx, "example", &ec2.InstanceArgs{
Ami: pulumi.String("ami-005e54dee72cc1d00"),
InstanceType: pulumi.String(ec2.InstanceType_T2_Micro),
PrimaryNetworkInterface: &ec2.InstancePrimaryNetworkInterfaceArgs{
NetworkInterfaceId: example.ID(),
},
CreditSpecification: &ec2.InstanceCreditSpecificationArgs{
CpuCredits: pulumi.String("unlimited"),
},
})
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 myVpc = new Aws.Ec2.Vpc("my_vpc", new()
{
CidrBlock = "172.16.0.0/16",
Tags =
{
{ "Name", "tf-example" },
},
});
var mySubnet = new Aws.Ec2.Subnet("my_subnet", new()
{
VpcId = myVpc.Id,
CidrBlock = "172.16.10.0/24",
AvailabilityZone = "us-west-2a",
Tags =
{
{ "Name", "tf-example" },
},
});
var example = new Aws.Ec2.NetworkInterface("example", new()
{
SubnetId = mySubnet.Id,
PrivateIps = new[]
{
"172.16.10.100",
},
Tags =
{
{ "Name", "primary_network_interface" },
},
});
var exampleInstance = new Aws.Ec2.Instance("example", new()
{
Ami = "ami-005e54dee72cc1d00",
InstanceType = Aws.Ec2.InstanceType.T2_Micro,
PrimaryNetworkInterface = new Aws.Ec2.Inputs.InstancePrimaryNetworkInterfaceArgs
{
NetworkInterfaceId = example.Id,
},
CreditSpecification = new Aws.Ec2.Inputs.InstanceCreditSpecificationArgs
{
CpuCredits = "unlimited",
},
});
});
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.Subnet;
import com.pulumi.aws.ec2.SubnetArgs;
import com.pulumi.aws.ec2.NetworkInterface;
import com.pulumi.aws.ec2.NetworkInterfaceArgs;
import com.pulumi.aws.ec2.Instance;
import com.pulumi.aws.ec2.InstanceArgs;
import com.pulumi.aws.ec2.inputs.InstancePrimaryNetworkInterfaceArgs;
import com.pulumi.aws.ec2.inputs.InstanceCreditSpecificationArgs;
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 myVpc = new Vpc("myVpc", VpcArgs.builder()
.cidrBlock("172.16.0.0/16")
.tags(Map.of("Name", "tf-example"))
.build());
var mySubnet = new Subnet("mySubnet", SubnetArgs.builder()
.vpcId(myVpc.id())
.cidrBlock("172.16.10.0/24")
.availabilityZone("us-west-2a")
.tags(Map.of("Name", "tf-example"))
.build());
var example = new NetworkInterface("example", NetworkInterfaceArgs.builder()
.subnetId(mySubnet.id())
.privateIps("172.16.10.100")
.tags(Map.of("Name", "primary_network_interface"))
.build());
var exampleInstance = new Instance("exampleInstance", InstanceArgs.builder()
.ami("ami-005e54dee72cc1d00")
.instanceType("t2.micro")
.primaryNetworkInterface(InstancePrimaryNetworkInterfaceArgs.builder()
.networkInterfaceId(example.id())
.build())
.creditSpecification(InstanceCreditSpecificationArgs.builder()
.cpuCredits("unlimited")
.build())
.build());
}
}
resources:
myVpc:
type: aws:ec2:Vpc
name: my_vpc
properties:
cidrBlock: 172.16.0.0/16
tags:
Name: tf-example
mySubnet:
type: aws:ec2:Subnet
name: my_subnet
properties:
vpcId: ${myVpc.id}
cidrBlock: 172.16.10.0/24
availabilityZone: us-west-2a
tags:
Name: tf-example
example:
type: aws:ec2:NetworkInterface
properties:
subnetId: ${mySubnet.id}
privateIps:
- 172.16.10.100
tags:
Name: primary_network_interface
exampleInstance:
type: aws:ec2:Instance
name: example
properties:
ami: ami-005e54dee72cc1d00
instanceType: t2.micro
primaryNetworkInterface:
networkInterfaceId: ${example.id}
creditSpecification:
cpuCredits: unlimited
The example creates a VPC, subnet, and network interface, then attaches the interface to the instance via primaryNetworkInterface. The networkInterfaceId property references the pre-created interface, giving you control over private IP addresses and network placement. This approach is useful when you need consistent IP addresses across instance replacements.
Configure CPU cores and threads for specialized workloads
Certain workloads benefit from custom CPU topology to optimize for performance or meet software licensing requirements that charge per core.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.ec2.Vpc("example", {
cidrBlock: "172.16.0.0/16",
tags: {
Name: "tf-example",
},
});
const exampleSubnet = new aws.ec2.Subnet("example", {
vpcId: example.id,
cidrBlock: "172.16.10.0/24",
availabilityZone: "us-east-2a",
tags: {
Name: "tf-example",
},
});
const amzn_linux_2023_ami = aws.ec2.getAmi({
mostRecent: true,
owners: ["amazon"],
filters: [{
name: "name",
values: ["al2023-ami-2023.*-x86_64"],
}],
});
const exampleInstance = new aws.ec2.Instance("example", {
ami: amzn_linux_2023_ami.then(amzn_linux_2023_ami => amzn_linux_2023_ami.id),
instanceType: aws.ec2.InstanceType.C6a_2XLarge,
subnetId: exampleSubnet.id,
cpuOptions: {
coreCount: 2,
threadsPerCore: 2,
},
tags: {
Name: "tf-example",
},
});
import pulumi
import pulumi_aws as aws
example = aws.ec2.Vpc("example",
cidr_block="172.16.0.0/16",
tags={
"Name": "tf-example",
})
example_subnet = aws.ec2.Subnet("example",
vpc_id=example.id,
cidr_block="172.16.10.0/24",
availability_zone="us-east-2a",
tags={
"Name": "tf-example",
})
amzn_linux_2023_ami = aws.ec2.get_ami(most_recent=True,
owners=["amazon"],
filters=[{
"name": "name",
"values": ["al2023-ami-2023.*-x86_64"],
}])
example_instance = aws.ec2.Instance("example",
ami=amzn_linux_2023_ami.id,
instance_type=aws.ec2.InstanceType.C6A_2_X_LARGE,
subnet_id=example_subnet.id,
cpu_options={
"core_count": 2,
"threads_per_core": 2,
},
tags={
"Name": "tf-example",
})
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 {
example, err := ec2.NewVpc(ctx, "example", &ec2.VpcArgs{
CidrBlock: pulumi.String("172.16.0.0/16"),
Tags: pulumi.StringMap{
"Name": pulumi.String("tf-example"),
},
})
if err != nil {
return err
}
exampleSubnet, err := ec2.NewSubnet(ctx, "example", &ec2.SubnetArgs{
VpcId: example.ID(),
CidrBlock: pulumi.String("172.16.10.0/24"),
AvailabilityZone: pulumi.String("us-east-2a"),
Tags: pulumi.StringMap{
"Name": pulumi.String("tf-example"),
},
})
if err != nil {
return err
}
amzn_linux_2023_ami, err := ec2.LookupAmi(ctx, &ec2.LookupAmiArgs{
MostRecent: pulumi.BoolRef(true),
Owners: []string{
"amazon",
},
Filters: []ec2.GetAmiFilter{
{
Name: "name",
Values: []string{
"al2023-ami-2023.*-x86_64",
},
},
},
}, nil)
if err != nil {
return err
}
_, err = ec2.NewInstance(ctx, "example", &ec2.InstanceArgs{
Ami: pulumi.String(amzn_linux_2023_ami.Id),
InstanceType: pulumi.String(ec2.InstanceType_C6a_2XLarge),
SubnetId: exampleSubnet.ID(),
CpuOptions: &ec2.InstanceCpuOptionsArgs{
CoreCount: pulumi.Int(2),
ThreadsPerCore: pulumi.Int(2),
},
Tags: pulumi.StringMap{
"Name": pulumi.String("tf-example"),
},
})
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 example = new Aws.Ec2.Vpc("example", new()
{
CidrBlock = "172.16.0.0/16",
Tags =
{
{ "Name", "tf-example" },
},
});
var exampleSubnet = new Aws.Ec2.Subnet("example", new()
{
VpcId = example.Id,
CidrBlock = "172.16.10.0/24",
AvailabilityZone = "us-east-2a",
Tags =
{
{ "Name", "tf-example" },
},
});
var amzn_linux_2023_ami = Aws.Ec2.GetAmi.Invoke(new()
{
MostRecent = true,
Owners = new[]
{
"amazon",
},
Filters = new[]
{
new Aws.Ec2.Inputs.GetAmiFilterInputArgs
{
Name = "name",
Values = new[]
{
"al2023-ami-2023.*-x86_64",
},
},
},
});
var exampleInstance = new Aws.Ec2.Instance("example", new()
{
Ami = amzn_linux_2023_ami.Apply(amzn_linux_2023_ami => amzn_linux_2023_ami.Apply(getAmiResult => getAmiResult.Id)),
InstanceType = Aws.Ec2.InstanceType.C6a_2XLarge,
SubnetId = exampleSubnet.Id,
CpuOptions = new Aws.Ec2.Inputs.InstanceCpuOptionsArgs
{
CoreCount = 2,
ThreadsPerCore = 2,
},
Tags =
{
{ "Name", "tf-example" },
},
});
});
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.Subnet;
import com.pulumi.aws.ec2.SubnetArgs;
import com.pulumi.aws.ec2.Ec2Functions;
import com.pulumi.aws.ec2.inputs.GetAmiArgs;
import com.pulumi.aws.ec2.Instance;
import com.pulumi.aws.ec2.InstanceArgs;
import com.pulumi.aws.ec2.inputs.InstanceCpuOptionsArgs;
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 example = new Vpc("example", VpcArgs.builder()
.cidrBlock("172.16.0.0/16")
.tags(Map.of("Name", "tf-example"))
.build());
var exampleSubnet = new Subnet("exampleSubnet", SubnetArgs.builder()
.vpcId(example.id())
.cidrBlock("172.16.10.0/24")
.availabilityZone("us-east-2a")
.tags(Map.of("Name", "tf-example"))
.build());
final var amzn-linux-2023-ami = Ec2Functions.getAmi(GetAmiArgs.builder()
.mostRecent(true)
.owners("amazon")
.filters(GetAmiFilterArgs.builder()
.name("name")
.values("al2023-ami-2023.*-x86_64")
.build())
.build());
var exampleInstance = new Instance("exampleInstance", InstanceArgs.builder()
.ami(amzn_linux_2023_ami.id())
.instanceType("c6a.2xlarge")
.subnetId(exampleSubnet.id())
.cpuOptions(InstanceCpuOptionsArgs.builder()
.coreCount(2)
.threadsPerCore(2)
.build())
.tags(Map.of("Name", "tf-example"))
.build());
}
}
resources:
example:
type: aws:ec2:Vpc
properties:
cidrBlock: 172.16.0.0/16
tags:
Name: tf-example
exampleSubnet:
type: aws:ec2:Subnet
name: example
properties:
vpcId: ${example.id}
cidrBlock: 172.16.10.0/24
availabilityZone: us-east-2a
tags:
Name: tf-example
exampleInstance:
type: aws:ec2:Instance
name: example
properties:
ami: ${["amzn-linux-2023-ami"].id}
instanceType: c6a.2xlarge
subnetId: ${exampleSubnet.id}
cpuOptions:
coreCount: 2
threadsPerCore: 2
tags:
Name: tf-example
variables:
amzn-linux-2023-ami:
fn::invoke:
function: aws:ec2:getAmi
arguments:
mostRecent: true
owners:
- amazon
filters:
- name: name
values:
- al2023-ami-2023.*-x86_64
The cpuOptions property controls CPU topology. The coreCount property sets the number of physical cores, while threadsPerCore controls hyperthreading (1 disables it, 2 enables it). This example configures a c6a.2xlarge instance with 2 cores and 2 threads per core, totaling 4 vCPUs. Some software licenses charge per core rather than per vCPU, making this configuration valuable for cost optimization.
Beyond these examples
These snippets focus on specific instance-level features: AMI selection (dynamic filters and SSM Parameter Store), spot instances and pricing, and network interfaces and CPU topology. They’re intentionally minimal rather than full VM deployments.
The examples may reference pre-existing infrastructure such as default VPC and subnets (for examples 1-3), and IAM permissions for SSM Parameter Store. They focus on configuring the instance rather than provisioning everything around it.
To keep things focused, common instance patterns are omitted, including:
- SSH key pairs and security groups
- IAM instance profiles (iamInstanceProfile)
- User data bootstrapping (userData, userDataBase64)
- EBS volume configuration (rootBlockDevice, ebsBlockDevices)
- Placement groups and tenancy options
- Metadata options and monitoring configuration
These omissions are intentional: the goal is to illustrate how each instance feature is wired, not provide drop-in VM modules. See the EC2 Instance resource reference for all available configuration options.
Let's launch an EC2 Instance
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Instance Lifecycle & Updates
instanceType, userData, or userDataBase64 trigger a stop/start by default. If userDataReplaceOnChange is set to true, user data changes will instead trigger a destroy and recreate.forceDestroy to true requires a successful pulumi up run before it takes effect. It won’t work if set in the same operation that replaces or destroys the instance, or immediately after import. Run pulumi up successfully first, then perform the destroy.ami, availabilityZone, subnetId, keyName, tenancy, privateIp, ebsOptimized, hibernation, placementGroup, and instanceMarketOptions. Changes to these properties force instance replacement.Networking & Security
vpcSecurityGroupIds for VPC instances. The securityGroups property is deprecated because it doesn’t allow updates and forces instance replacement if changed. vpcSecurityGroupIds allows updates without replacement.publicIp attribute changes after attaching an aws.ec2.Eip. Refer to the EIP’s address directly instead of using the instance’s publicIp attribute to avoid broken references.enablePrimaryIpv6 is enabled, the first IPv6 GUA becomes the primary address and cannot be disabled. Attempting to disable it forces instance recreation. The primary IPv6 address remains until the instance is terminated or the ENI is detached.primaryNetworkInterface with the network interface ID. The networkInterfaces property is deprecated; use aws.ec2.NetworkInterfaceAttachment for additional interfaces.User Data & Configuration
userDataBase64 instead of userData for gzip-compressed or binary data. Passing gzip-compressed data via userData causes corruption because it’s not valid UTF-8.userData or userDataBase64 updates trigger a stop/start. Set userDataReplaceOnChange to true to trigger a destroy and recreate instead.Block Devices & Tagging
volumeTags while managing block device tags outside aws.ec2.Instance (e.g., via aws.ebs.Volume tags attached with aws.ec2.VolumeAttachment) results in resource cycling and inconsistent behavior. Don’t use volumeTags if you plan to manage volume tags externally.tags (instance only), (2) provider defaultTags (instance + volumes), (3) volumeTags (volumes at creation), (4) rootBlockDevice.tags (root volume only), and (5) ebsBlockDevice.tags (specific EBS volume). The last two conflict with volumeTags.AMI & Launch Configuration
ami property (required unless launchTemplate is specified). You can use a data source lookup, or reference AWS Systems Manager Parameter Store with the syntax resolve:ssm:/aws/service/ami-amazon-linux-latest/....instanceMarketOptions with marketType set to "spot" and optionally set spotOptions.maxPrice to specify your maximum price.hostResourceGroupArn to the ARN of your host resource group and either omit tenancy or set it to "host". Valid tenancy values are "default", "dedicated", and "host".