Pulumi Reference: IaC with TypeScript/Python — Stacks, AWS/K8s Resources, Deploy & CI
Pulumi defines cloud infrastructure using real programming languages — TypeScript, Python, Go, C#, Java, or YAML. Instead of learning HCL (Terraform) or CDK constructs, you write actual code with loops, conditions, functions, and types. The state model is similar to Terraform but the programming model is fundamentally different.
1. Pulumi vs Terraform
Key differences, when to choose which
| Pulumi | Terraform | |
|---|---|---|
| Language | TypeScript, Python, Go, C#, Java, YAML | HCL (HashiCorp Config Language) |
| State | Pulumi Cloud (free) or local/S3/GCS | Local or Terraform Cloud/S3 backend |
| Loops | Real for loops in your language | count + for_each meta-arguments |
| Logic | if/else, functions, classes, modules | Conditionals via ternary, locals |
| Testing | Unit tests with real test frameworks (Jest, pytest) | Terratest (Go) or manual |
| Providers | All Terraform providers available (via bridge) | All Terraform providers |
| Community | Smaller, growing fast | Largest IaC community |
# Install Pulumi CLI: brew install pulumi/tap/pulumi # macOS # Or: curl -fsSL https://get.pulumi.com | sh # Login (Pulumi Cloud — free, stores state): pulumi login # Local state (no Pulumi Cloud account): pulumi login --local # state in ~/.pulumi/stacks/ # Or: pulumi login s3://my-bucket # S3 backend
2. New Project & Stacks
pulumi new, stacks for environments, and config management
# Create new Pulumi project (TypeScript + AWS):
mkdir my-infra && cd my-infra
pulumi new aws-typescript
# Choose language + cloud:
pulumi new azure-python # Python + Azure
pulumi new gcp-go # Go + GCP
pulumi new kubernetes-typescript # TypeScript + Kubernetes
# Project structure:
# Pulumi.yaml — project metadata
# Pulumi.dev.yaml — dev stack config
# index.ts (or __main__.py, main.go) — your infrastructure code
# package.json / pyproject.toml
# Stacks = environments (dev, staging, production):
pulumi stack init staging # create staging stack
pulumi stack ls # list stacks
pulumi stack select production # switch to production stack
# Stack config (per-environment values):
pulumi config set aws:region us-east-1
pulumi config set instanceType t3.micro
pulumi config set --secret dbPassword "super-secret" # encrypted in state
# Read config in code (TypeScript):
# const config = new pulumi.Config();
# const region = config.require("aws:region");
# const dbPass = config.requireSecret("dbPassword"); // Output
3. Resources & Outputs (TypeScript)
Define AWS resources, use outputs, and create dependencies
// index.ts — TypeScript Pulumi program
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx"; // AWSX = higher-level components
// S3 bucket:
const bucket = new aws.s3.Bucket("my-bucket", {
bucket: "my-company-data-bucket",
versioning: { enabled: true },
tags: { Environment: "production", Team: "platform" }
});
// Export value (accessible via 'pulumi stack output'):
export const bucketName = bucket.bucket;
export const bucketArn = bucket.arn;
// ECR repository:
const repo = new awsx.ecr.Repository("app-repo", {
forceDelete: true
});
// ECS Fargate cluster + service (awsx makes this concise):
const cluster = new aws.ecs.Cluster("app-cluster");
const service = new awsx.ecs.FargateService("app-service", {
cluster: cluster.arn,
taskDefinitionArgs: {
container: {
name: "app",
image: repo.url.apply(url => `${url}:latest`), // .apply() for Output
cpu: 256,
memory: 512,
portMappings: [{ containerPort: 3000, protocol: "tcp" }],
environment: [
{ name: "DATABASE_URL", value: dbUrl }
]
}
}
});
// Dependency is implicit — Pulumi builds the DAG from resource references
// bucket.arn is an Output — resolved at deploy time, not program runtime
4. Kubernetes Resources
Deploy K8s resources from Pulumi TypeScript
import * as k8s from "@pulumi/kubernetes";
// Namespace:
const ns = new k8s.core.v1.Namespace("app-ns", {
metadata: { name: "production" }
});
// Deployment:
const deployment = new k8s.apps.v1.Deployment("app-deployment", {
metadata: { namespace: ns.metadata.name },
spec: {
replicas: 3,
selector: { matchLabels: { app: "my-app" } },
template: {
metadata: { labels: { app: "my-app" } },
spec: {
containers: [{
name: "app",
image: "gcr.io/my-project/my-app:v1.2.3",
ports: [{ containerPort: 3000 }],
resources: {
requests: { cpu: "100m", memory: "128Mi" },
limits: { cpu: "500m", memory: "512Mi" }
}
}]
}
}
}
}, { dependsOn: ns });
// Helm chart via Pulumi:
const nginx = new k8s.helm.v3.Chart("nginx-ingress", {
repo: "ingress-nginx",
chart: "ingress-nginx",
namespace: "ingress-nginx",
values: {
controller: { replicaCount: 2 }
}
});
// Export a K8s resource attribute:
export const serviceIp = nginx
.getResourceProperty("v1/Service", "ingress-nginx/ingress-nginx-controller", "status")
.apply(status => status.loadBalancer.ingress[0].ip);
5. Deploy, Preview & Destroy
pulumi up, diff, refresh, import, and CI/CD patterns
# Preview changes (like terraform plan):
pulumi preview # shows what would change
pulumi preview --diff # shows full diff of each resource
# Deploy (like terraform apply):
pulumi up # interactive confirm
pulumi up --yes # non-interactive (CI/CD)
pulumi up --target urn:aws:s3::bucket # deploy only specific resource
# Check current stack state vs real cloud:
pulumi refresh # update state to match real resources
# Import existing resource (bring unmanaged resource under Pulumi):
pulumi import aws:s3/bucket:Bucket my-bucket my-existing-bucket-name
# Stack outputs:
pulumi stack output bucketName # get specific output
pulumi stack output --json # all outputs as JSON
# Destroy (be careful!):
pulumi destroy # removes ALL resources in stack
pulumi destroy --target urn:... # remove specific resource only
# GitHub Actions CI:
# - name: Deploy
# run: pulumi up --yes --stack production
# 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 }}
# Secrets in state are encrypted with Pulumi Cloud key by default
# For local/S3 backend: pulumi config set --secret --secrets-provider=awskms://...
Track Pulumi, Terraform, and IaC tool releases.
ReleaseRun monitors Kubernetes, Docker, and 13+ DevOps technologies.
Related: Terraform Reference | Kubernetes YAML Reference | GitHub Actions Reference
🔍 Free tool: Terraform Security Scanner — if you also use Terraform alongside Pulumi, scan your .tf configs for security misconfigurations instantly.
Founded
2023 in London, UK
Contact
hello@releaserun.com