Pulumi is an Infrastructure as Code (IaC) platform that allows engineering teams to define, deploy, and manage cloud infrastructure using general-purpose languages like TypeScript, Python, and Go.
When to Use
- Modern Programming Languages for Infrastructure as Code (IaC): TypeScript, Python, Go, and C# instead of domain-specific languages (DSL).
- Modular Component Resources: Encapsulating complex cloud topologies into reusable, typed classes and libraries.
- Real-Time Integration & Unit Testing: Writing standard unit tests for infrastructure using Vitest, Jest, or PyTest.
- Kubernetes & Multi-Cloud Native: Provisioning AWS, Azure, GCP, and Kubernetes workloads in a single unified language.
Quick Start
import * as pulum from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const bucket = new aws.s3.Bucket("my-bucket", { acl: "private", });
export const bucketName = bucket.id;
## Core Concepts
### Modular Infrastructure Component with TypeScript
Creating an encapsulated, reusable microservice infrastructure component:
```typescript
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
interface MicroserviceArgs {
vpcId: pulumi.Input<string>;
subnetIds: pulumi.Input<string[]>;
imageTag: string;
}
export class ContainerMicroservice extends pulumi.ComponentResource {
public readonly url: pulumi.Output<string>;
constructor(
name: string,
args: MicroserviceArgs,
opts?: pulumi.ComponentResourceOptions,
) {
super("custom:cloud:ContainerMicroservice", name, {}, opts);
// ECR Repository
const repo = new aws.ecr.Repository(
`${name}-repo`,
{
imageScanningConfiguration: { scanOnPush: true },
},
{ parent: this },
);
// Application Load Balancer
const alb = new aws.lb.LoadBalancer(
`${name}-alb`,
{
internal: false,
subnets: args.subnetIds,
},
{ parent: this },
);
this.url = alb.dnsName;
this.registerOutputs({ url: this.url });
}
}
// In index.ts:
const service = new ContainerMicroservice("billing", {
vpcId: "vpc-0123456789",
subnetIds: ["subnet-a", "subnet-b"],
imageTag: "v2.1.0",
});
export const endpoint = service.url;
Secret Management & Stack Configuration
Encrypting sensitive credentials automatically:
# Configure encrypted secret in stack configuration
pulumi config set --secret dbPassword "SuperSecurePassword2026!"
# Read secret in Pulumi program
const config = new pulumi.Config();
const dbPassword = config.requireSecret("dbPassword"); // Returned as Output<string> (masked)
Pulumi CLI Operations
Previewing and applying infrastructure changes:
# Preview proposed changes with detailed diff
pulumi preview
# Deploy infrastructure to cloud stack
pulumi up --yes
# Destroy stack resources cleanly
pulumi destroy
Common Patterns
Typed ComponentResource for Reusable Infrastructure Modules
Problem: Copy-pasting raw cloud resource definitions across staging and production stacks.
Solution: Encapsulate resources into reusable Pulumi ComponentResources:
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
interface MicroserviceArgs {
vpcId: pulumi.Input<string>;
imageUri: pulumi.Input<string>;
port: number;
}
export class Microservice extends pulumi.ComponentResource {
public readonly url: pulumi.Output<string>;
constructor(
name: string,
args: MicroserviceArgs,
opts?: pulumi.ComponentResourceOptions,
) {
super("custom:app:Microservice", name, {}, opts);
const sg = new aws.ec2.SecurityGroup(
`${name}-sg`,
{
vpcId: args.vpcId,
ingress: [
{
protocol: "tcp",
fromPort: args.port,
toPort: args.port,
cidrBlocks: ["0.0.0.0/0"],
},
],
},
{ parent: this },
);
// Output URL
this.url = pulumi.interpolate`http://service.${name}.internal:${args.port}`;
this.registerOutputs({ url: this.url });
}
}
Best Practices
Do:
- Encapsulate related infrastructure into custom
ComponentResourceclasses for reusability. - Treat secret values strictly as
pulumi.Output<string>to ensure automated masking in console logs. - Run unit tests against infrastructure mocks before executing deployments in CI/CD pipelines.
- Configure Pulumi Cloud or an S3/GCS backend for reliable stack state locking and auditing.
Don't:
- Call
.get()on Pulumi Outputs during resource declaration; use.apply()to chain asynchronous outputs. - Commit plaintext secrets to
Pulumi.<stack>.yamlfiles; use--secretflag. - Mix side-effects (e.g. database schema migrations) directly inside Pulumi IaC definitions.
Troubleshooting
| Error | Cause | Solution |
|---|---|---|
Diagnostics: Resource '...' already exists |
Attempting to create resource that exists outside of Pulumi state. | Import resource into state: pulumi import <type> <name> <id>. |
Stack locked by another operation |
Previous update interrupted or crashed before releasing state lock. | Cancel lock after verifying no operation is running: pulumi cancel. |
Error: Output<T> cannot be passed to string |
Accessing .apply() outputs as plain synchronous strings. |
Wrap dependent values with pulumi.interpolate or use .apply(). |