Pulumi Infrastructure as Code Setup
Pulumi is a Terraform alternative where infrastructure is described in regular programming languages: TypeScript, Python, Go, C#. Allows using loops, functions, conditions, and reusing code without learning HCL.
Installation
# CLI
brew install pulumi/tap/pulumi # macOS
choco install pulumi # Windows
# Create project (TypeScript)
pulumi new aws-typescript
# Create project (Python + AWS)
pulumi new aws-python
TypeScript stack for web application
// index.ts
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";
import * as pulumi from "@pulumi/pulumi";
const config = new pulumi.Config();
const dbPassword = config.requireSecret("dbPassword");
// VPC
const vpc = new awsx.ec2.Vpc("myapp-vpc", {
numberOfAvailabilityZones: 2,
enableDnsHostnames: true,
});
// ECS Cluster
const cluster = new aws.ecs.Cluster("myapp-cluster", {
settings: [{ name: "containerInsights", value: "enabled" }],
});
// RDS PostgreSQL
const db = new aws.rds.Instance("myapp-db", {
engine: "postgres",
engineVersion: "16.1",
instanceClass: aws.rds.InstanceType.T3_Medium,
allocatedStorage: 100,
dbName: "myapp",
username: "myapp",
password: dbPassword,
skipFinalSnapshot: !pulumi.getStack().startsWith("prod"),
vpcSecurityGroupIds: [dbSg.id],
dbSubnetGroupName: dbSubnetGroup.name,
storageEncrypted: true,
});
// ECS Fargate Service
const service = new awsx.ecs.FargateService("myapp-web", {
cluster: cluster.arn,
taskDefinitionArgs: {
container: {
name: "web",
image: "registry.example.com/myapp:latest",
cpu: 256,
memory: 512,
essential: true,
portMappings: [{ containerPort: 8080 }],
environment: [
{ name: "APP_ENV", value: "production" },
{ name: "DB_HOST", value: db.endpoint },
],
secrets: [
{ name: "DB_PASSWORD", valueFrom: dbPasswordSecret.arn },
],
},
},
desiredCount: 3,
loadBalancers: [{
targetGroupArn: targetGroup.arn,
containerName: "web",
containerPort: 8080,
}],
});
// Outputs
export const dbEndpoint = db.endpoint;
export const serviceUrl = pulumi.interpolate`https://${loadBalancer.dnsName}`;
Python stack (Google Cloud)
# __main__.py
import pulumi
import pulumi_gcp as gcp
config = pulumi.Config()
# Cloud Run Service
service = gcp.cloudrun.Service("myapp",
location="europe-west3",
template=gcp.cloudrun.ServiceTemplateArgs(
spec=gcp.cloudrun.ServiceTemplateSpecArgs(
containers=[gcp.cloudrun.ServiceTemplateSpecContainerArgs(
image="gcr.io/myproject/myapp:latest",
resources=gcp.cloudrun.ServiceTemplateSpecContainerResourcesArgs(
limits={"cpu": "1000m", "memory": "512Mi"},
),
envs=[
gcp.cloudrun.ServiceTemplateSpecContainerEnvArgs(
name="DB_HOST",
value=config.require("db_host"),
),
],
)],
container_concurrency=80,
),
metadata=gcp.cloudrun.ServiceTemplateMetadataArgs(
annotations={"autoscaling.knative.dev/maxScale": "10"},
),
),
)
# Allow unauthenticated access
iam = gcp.cloudrun.IamMember("public",
service=service.name,
location=service.location,
role="roles/run.invoker",
member="allUsers",
)
pulumi.export("service_url", service.statuses[0].url)
Stack management
# Create production stack
pulumi stack init production
# Configure
pulumi config set aws:region eu-west-1
pulumi config set dbPassword "secret" --secret
# Deploy
pulumi up
# Preview changes without applying
pulumi preview
# Destroy
pulumi destroy
# Switch between stacks
pulumi stack select staging
pulumi stack select production
CI/CD Automation
- name: Pulumi Deploy
uses: pulumi/actions@v4
with:
command: up
stack-name: production
cloud-url: https://api.pulumi.com
env:
PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
Pulumi vs Terraform
Pulumi is preferable when: complex logic is needed (generating resources in a loop from external data), the team knows TypeScript/Python better than HCL, components need to be reused as npm/pip packages.
Timeline
TypeScript stack for typical AWS application: 5–7 days.







