A Pulumi package for creating and managing Amazon Web Services (AWS) cloud resources with infrastructure-as-code.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Guide to selecting and using AWS networking services with Pulumi.
Foundation of AWS networking - VPCs, subnets, routing
Distribute traffic across targets
Name resolution and routing
Global edge locations, low latency
Build and manage APIs
Hybrid and inter-region connections
Network protection
Microservices networking
Start: What are you building?
┌─ Basic web application
│ ├─ VPC with public/private subnets
│ ├─ Internet Gateway for public access
│ ├─ NAT Gateway for private subnet outbound
│ ├─ ALB for load balancing
│ └─ Security Groups for instance protection
│
┌─ Multi-tier application
│ ├─ VPC with 3-tier architecture
│ │ ├─ Public subnet → ALB
│ │ ├─ Private subnet → Application servers
│ │ └─ Private subnet → Databases
│ ├─ NAT Gateway for app servers
│ ├─ Internal ALB for app tier
│ └─ Security Groups per tier
│
┌─ Global application
│ ├─ Multi-region deployment
│ ├─ Route 53 for DNS routing
│ │ ├─ Latency-based routing
│ │ ├─ Geolocation routing
│ │ └─ Failover routing
│ ├─ CloudFront for static content
│ └─ Global Accelerator for dynamic content
│
┌─ API backend
│ ├─ REST API → API Gateway (REST) + Lambda
│ ├─ HTTP API → API Gateway v2 (HTTP) + Lambda (cheaper)
│ ├─ WebSocket → API Gateway v2 (WebSocket)
│ └─ GraphQL → AppSync
│
┌─ Hybrid connectivity
│ ├─ Consistent high bandwidth → Direct Connect
│ ├─ Encrypted connectivity → Site-to-Site VPN
│ ├─ Remote access → Client VPN
│ └─ Multiple VPCs → Transit Gateway
│
└─ Microservices
├─ Service mesh → App Mesh
├─ Service discovery → Cloud Map
├─ Modern application networking → VPC Lattice
└─ Container networking → ECS with service discoveryVPC design best practices:
Subnet strategy:
ALB features:
NLB features:
Routing policies:
Use cases:
Origin options:
Cache behaviors:
REST API:
HTTP API (v2):
WebSocket API:
When to use AppSync instead:
Direct Connect vs VPN:
Use both: Direct Connect for primary, VPN for backup
Transit Gateway benefits:
Transit Gateway vs VPC Peering:
VPC + ALB + Auto Scaling
import * as aws from "@pulumi/aws";
// VPC
const vpc = new aws.ec2.Vpc("main-vpc", {
cidrBlock: "10.0.0.0/16",
enableDnsHostnames: true,
enableDnsSupport: true,
tags: { Name: "main-vpc" },
});
// Public subnets (2 AZs)
const publicSubnet1 = new aws.ec2.Subnet("public-1", {
vpcId: vpc.id,
cidrBlock: "10.0.1.0/24",
availabilityZone: "us-east-1a",
mapPublicIpOnLaunch: true,
tags: { Name: "public-subnet-1", Tier: "public" },
});
const publicSubnet2 = new aws.ec2.Subnet("public-2", {
vpcId: vpc.id,
cidrBlock: "10.0.2.0/24",
availabilityZone: "us-east-1b",
mapPublicIpOnLaunch: true,
tags: { Name: "public-subnet-2", Tier: "public" },
});
// Private subnets for application tier
const appSubnet1 = new aws.ec2.Subnet("app-1", {
vpcId: vpc.id,
cidrBlock: "10.0.11.0/24",
availabilityZone: "us-east-1a",
tags: { Name: "app-subnet-1", Tier: "application" },
});
const appSubnet2 = new aws.ec2.Subnet("app-2", {
vpcId: vpc.id,
cidrBlock: "10.0.12.0/24",
availabilityZone: "us-east-1b",
tags: { Name: "app-subnet-2", Tier: "application" },
});
// Private subnets for database tier
const dbSubnet1 = new aws.ec2.Subnet("db-1", {
vpcId: vpc.id,
cidrBlock: "10.0.21.0/24",
availabilityZone: "us-east-1a",
tags: { Name: "db-subnet-1", Tier: "database" },
});
const dbSubnet2 = new aws.ec2.Subnet("db-2", {
vpcId: vpc.id,
cidrBlock: "10.0.22.0/24",
availabilityZone: "us-east-1b",
tags: { Name: "db-subnet-2", Tier: "database" },
});
// Internet Gateway
const igw = new aws.ec2.InternetGateway("igw", {
vpcId: vpc.id,
tags: { Name: "main-igw" },
});
// NAT Gateway (1 per AZ for HA)
const eip1 = new aws.ec2.Eip("nat-eip-1", { vpc: true });
const natGw1 = new aws.ec2.NatGateway("nat-1", {
subnetId: publicSubnet1.id,
allocationId: eip1.id,
tags: { Name: "nat-gateway-1" },
});
// Route tables
const publicRt = new aws.ec2.RouteTable("public-rt", {
vpcId: vpc.id,
routes: [{ cidrBlock: "0.0.0.0/0", gatewayId: igw.id }],
tags: { Name: "public-route-table" },
});
const privateRt = new aws.ec2.RouteTable("private-rt", {
vpcId: vpc.id,
routes: [{ cidrBlock: "0.0.0.0/0", natGatewayId: natGw1.id }],
tags: { Name: "private-route-table" },
});
// Route table associations
const publicRta1 = new aws.ec2.RouteTableAssociation("public-rta-1", {
subnetId: publicSubnet1.id,
routeTableId: publicRt.id,
});
const appRta1 = new aws.ec2.RouteTableAssociation("app-rta-1", {
subnetId: appSubnet1.id,
routeTableId: privateRt.id,
});
// Application Load Balancer
const albSg = new aws.ec2.SecurityGroup("alb-sg", {
vpcId: vpc.id,
ingress: [
{ protocol: "tcp", fromPort: 80, toPort: 80, cidrBlocks: ["0.0.0.0/0"] },
{ protocol: "tcp", fromPort: 443, toPort: 443, cidrBlocks: ["0.0.0.0/0"] },
],
egress: [{ protocol: "-1", fromPort: 0, toPort: 0, cidrBlocks: ["0.0.0.0/0"] }],
});
const alb = new aws.lb.LoadBalancer("app-alb", {
loadBalancerType: "application",
subnets: [publicSubnet1.id, publicSubnet2.id],
securityGroups: [albSg.id],
tags: { Name: "app-alb" },
});
const targetGroup = new aws.lb.TargetGroup("app-tg", {
port: 80,
protocol: "HTTP",
vpcId: vpc.id,
healthCheck: {
path: "/health",
interval: 30,
timeout: 5,
healthyThreshold: 2,
unhealthyThreshold: 2,
},
});
const listener = new aws.lb.Listener("app-listener", {
loadBalancerArn: alb.arn,
port: 80,
protocol: "HTTP",
defaultActions: [{
type: "forward",
targetGroupArn: targetGroup.arn,
}],
});
export const albDns = alb.dnsName;Use when: Traditional web applications, multi-tier architectures
API Gateway + Lambda + DynamoDB
// Lambda function
const handler = new aws.lambda.Function("api", {
runtime: "nodejs20.x",
handler: "index.handler",
role: lambdaRole.arn,
code: new pulumi.asset.FileArchive("./app"),
environment: {
variables: { TABLE_NAME: table.name },
},
});
// API Gateway HTTP API (v2)
const api = new aws.apigatewayv2.Api("http-api", {
protocolType: "HTTP",
corsConfiguration: {
allowOrigins: ["*"],
allowMethods: ["GET", "POST", "PUT", "DELETE"],
allowHeaders: ["content-type", "authorization"],
},
});
// Lambda integration
const integration = new aws.apigatewayv2.Integration("lambda-integration", {
apiId: api.id,
integrationType: "AWS_PROXY",
integrationUri: handler.arn,
payloadFormatVersion: "2.0",
});
// Routes
const getRoute = new aws.apigatewayv2.Route("get-route", {
apiId: api.id,
routeKey: "GET /users",
target: pulumi.interpolate`integrations/${integration.id}`,
});
const postRoute = new aws.apigatewayv2.Route("post-route", {
apiId: api.id,
routeKey: "POST /users",
target: pulumi.interpolate`integrations/${integration.id}`,
});
// Stage
const stage = new aws.apigatewayv2.Stage("default", {
apiId: api.id,
name: "$default",
autoDeploy: true,
});
// Lambda permission
const permission = new aws.lambda.Permission("api-permission", {
action: "lambda:InvokeFunction",
function: handler.name,
principal: "apigateway.amazonaws.com",
sourceArn: pulumi.interpolate`${api.executionArn}/*/*`,
});
export const apiUrl = api.apiEndpoint;Use when: Serverless REST APIs, microservices
CloudFront + S3 + Route 53
// S3 bucket for website
const bucket = new aws.s3.BucketV2("website", {
bucket: "my-website.com",
});
// Origin Access Identity for CloudFront
const oai = new aws.cloudfront.OriginAccessIdentity("oai", {
comment: "OAI for website bucket",
});
// Bucket policy to allow CloudFront
const bucketPolicy = new aws.s3.BucketPolicy("bucket-policy", {
bucket: bucket.id,
policy: pulumi.all([bucket.arn, oai.iamArn]).apply(([bucketArn, oaiArn]) =>
JSON.stringify({
Version: "2012-10-17",
Statement: [{
Effect: "Allow",
Principal: { AWS: oaiArn },
Action: "s3:GetObject",
Resource: `${bucketArn}/*`,
}],
})
),
});
// ACM certificate for CloudFront (must be in us-east-1)
const certificate = new aws.acm.Certificate("cert", {
domainName: "my-website.com",
validationMethod: "DNS",
}, { provider: usEast1Provider });
// CloudFront distribution
const cdn = new aws.cloudfront.Distribution("cdn", {
origins: [{
domainName: bucket.bucketRegionalDomainName,
originId: "S3Origin",
s3OriginConfig: {
originAccessIdentity: oai.cloudfrontAccessIdentityPath,
},
}],
enabled: true,
defaultRootObject: "index.html",
aliases: ["my-website.com", "www.my-website.com"],
defaultCacheBehavior: {
targetOriginId: "S3Origin",
viewerProtocolPolicy: "redirect-to-https",
allowedMethods: ["GET", "HEAD", "OPTIONS"],
cachedMethods: ["GET", "HEAD"],
forwardedValues: {
queryString: false,
cookies: { forward: "none" },
},
compress: true,
minTtl: 0,
defaultTtl: 3600,
maxTtl: 86400,
},
restrictions: {
geoRestriction: { restrictionType: "none" },
},
viewerCertificate: {
acmCertificateArn: certificate.arn,
sslSupportMethod: "sni-only",
minimumProtocolVersion: "TLSv1.2_2021",
},
});
// Route 53 records
const zone = aws.route53.getZone({ name: "my-website.com" });
const record = new aws.route53.Record("website", {
zoneId: zone.then(z => z.zoneId),
name: "my-website.com",
type: "A",
aliases: [{
name: cdn.domainName,
zoneId: cdn.hostedZoneId,
evaluateTargetHealth: false,
}],
});
export const websiteUrl = pulumi.interpolate`https://${record.name}`;Use when: Static websites, global applications, CDN requirements
Route 53 + Health Checks
// Primary region ALB (us-east-1)
const primaryAlb = new aws.lb.LoadBalancer("primary-alb", {
loadBalancerType: "application",
subnets: primarySubnetIds,
}, { provider: usEast1Provider });
// Secondary region ALB (us-west-2)
const secondaryAlb = new aws.lb.LoadBalancer("secondary-alb", {
loadBalancerType: "application",
subnets: secondarySubnetIds,
}, { provider: usWest2Provider });
// Health check for primary
const primaryHealthCheck = new aws.route53.HealthCheck("primary-health", {
type: "HTTPS",
resourcePath: "/health",
fqdn: primaryAlb.dnsName,
port: 443,
requestInterval: 30,
failureThreshold: 3,
tags: { Name: "primary-health-check" },
});
// Route 53 records with failover
const zone = aws.route53.getZone({ name: "example.com" });
const primaryRecord = new aws.route53.Record("primary", {
zoneId: zone.then(z => z.zoneId),
name: "app.example.com",
type: "A",
setIdentifier: "primary",
failoverRoutingPolicies: [{ type: "PRIMARY" }],
healthCheckId: primaryHealthCheck.id,
aliases: [{
name: primaryAlb.dnsName,
zoneId: primaryAlb.zoneId,
evaluateTargetHealth: true,
}],
});
const secondaryRecord = new aws.route53.Record("secondary", {
zoneId: zone.then(z => z.zoneId),
name: "app.example.com",
type: "A",
setIdentifier: "secondary",
failoverRoutingPolicies: [{ type: "SECONDARY" }],
aliases: [{
name: secondaryAlb.dnsName,
zoneId: secondaryAlb.zoneId,
evaluateTargetHealth: true,
}],
});
export const appUrl = "https://app.example.com";Use when: High availability, disaster recovery, multi-region
Transit Gateway + VPN
// Transit Gateway
const tgw = new aws.ec2transitgateway.TransitGateway("main-tgw", {
description: "Main transit gateway",
amazonSideAsn: 64512,
defaultRouteTableAssociation: "enable",
defaultRouteTablePropagation: "enable",
tags: { Name: "main-tgw" },
});
// VPC attachments
const vpcAttachment1 = new aws.ec2transitgateway.VpcAttachment("vpc1-attachment", {
transitGatewayId: tgw.id,
vpcId: vpc1.id,
subnetIds: vpc1PrivateSubnetIds,
tags: { Name: "vpc1-attachment" },
});
const vpcAttachment2 = new aws.ec2transitgateway.VpcAttachment("vpc2-attachment", {
transitGatewayId: tgw.id,
vpcId: vpc2.id,
subnetIds: vpc2PrivateSubnetIds,
tags: { Name: "vpc2-attachment" },
});
// Customer Gateway (on-premises)
const cgw = new aws.ec2.CustomerGateway("on-prem-cgw", {
bgpAsn: 65000,
ipAddress: "203.0.113.1", // On-premises public IP
type: "ipsec.1",
tags: { Name: "on-premises-cgw" },
});
// VPN attachment to Transit Gateway
const vpnAttachment = new aws.ec2transitgateway.VpnAttachment("vpn-attachment", {
transitGatewayId: tgw.id,
vpnConnectionId: vpnConnection.id,
tags: { Name: "vpn-attachment" },
});
// Site-to-Site VPN
const vpnConnection = new aws.ec2.VpnConnection("vpn", {
customerGatewayId: cgw.id,
transitGatewayId: tgw.id,
type: "ipsec.1",
staticRoutesOnly: false, // Use BGP
tags: { Name: "site-to-site-vpn" },
});
export const tgwId = tgw.id;Use when: Multiple VPCs, hybrid cloud, centralized routing
Install with Tessl CLI
npx tessl i tessl/npm-pulumi--aws@7.16.0