๐Ÿ—๏ธ

Pulumi

Pulumi IaC: CLI, stacks, state, secrets, config, providers, and automation API

CLI Basics

Create, preview, deploy, and destroy infrastructure

bashยทNew project
# Create a new project (interactive)
pulumi new

# From a template
pulumi new aws-typescript
pulumi new azure-python
pulumi new gcp-go
pulumi new kubernetes-typescript

# List available templates
pulumi new --list-templates

# Create in an existing directory
pulumi new aws-typescript --dir ./infra --name my-app --description "My infra"
bashยทPreview & deploy
# Preview changes (dry run)
pulumi preview

# Preview with detailed diff
pulumi preview --diff

# Deploy (up)
pulumi up

# Deploy without confirmation prompt
pulumi up --yes

# Deploy only specific resources
pulumi up --target "urn:pulumi:dev::my-app::aws:s3/bucket:Bucket::my-bucket"

# Skip resources that haven't changed
pulumi up --target-dependents

# Show expected JSON plan
pulumi preview --json
bashยทDestroy & refresh
# Destroy all resources in the stack
pulumi destroy

# Destroy without confirmation
pulumi destroy --yes

# Destroy specific resources
pulumi destroy --target "urn:pulumi:dev::my-app::aws:s3/bucket:Bucket::my-bucket"

# Refresh state from real cloud (reconcile drift)
pulumi refresh

# Refresh without confirmation
pulumi refresh --yes

Stacks

Create, select, list, and manage stacks

bashยทManage stacks
# List all stacks in the project
pulumi stack ls

# Create a new stack
pulumi stack init dev
pulumi stack init prod

# Select (switch to) a stack
pulumi stack select dev

# Show current stack info and outputs
pulumi stack

# Show stack outputs only
pulumi stack output

# Show a specific output value
pulumi stack output bucketName

# Output as JSON
pulumi stack output --json
bashยทCopy & remove stacks
# Copy config from one stack to another
pulumi config cp --dest prod

# Rename a stack
pulumi stack rename staging

# Remove a stack (must be empty โ€” destroy first)
pulumi stack rm dev

# Remove stack and all resources
pulumi destroy --yes && pulumi stack rm dev --yes
bashยทStack tags
# Set a tag on the current stack
pulumi stack tag set environment production
pulumi stack tag set team backend

# Get a tag
pulumi stack tag get environment

# List all tags
pulumi stack tag ls

# Remove a tag
pulumi stack tag rm environment

Config & Secrets

Store plaintext config and encrypted secrets per stack

bashยทConfig values
# Set a config value
pulumi config set aws:region us-east-1
pulumi config set myApp:instanceType t3.medium

# Set for a specific stack
pulumi config set --stack prod aws:region eu-west-1

# Get a value
pulumi config get aws:region

# List all config for current stack
pulumi config

# Remove a config key
pulumi config rm myApp:instanceType
bashยทSecrets
# Set an encrypted secret
pulumi config set --secret dbPassword s3cr3t!
pulumi config set --secret apiKey abc123

# Read a secret (decrypted)
pulumi config get dbPassword

# List config (secrets shown as [secret])
pulumi config

# Use in TypeScript
import * as pulumi from "@pulumi/pulumi";
const cfg = new pulumi.Config();
const dbPassword = cfg.requireSecret("dbPassword");
bashยทSecret providers
# Default: Pulumi Cloud managed encryption
pulumi stack init dev

# AWS KMS
pulumi stack init dev --secrets-provider="awskms://alias/my-key?region=us-east-1"

# Azure Key Vault
pulumi stack init dev --secrets-provider="azurekeyvault://my-vault.vault.azure.net/keys/my-key"

# GCP KMS
pulumi stack init dev --secrets-provider="gcpkms://projects/my-proj/locations/global/keyRings/my-ring/cryptoKeys/my-key"

# Passphrase (local, no cloud dependency)
pulumi stack init dev --secrets-provider=passphrase

# Change provider on existing stack
pulumi stack change-secrets-provider "awskms://alias/my-key?region=us-east-1"

State Management

Inspect, import, move, and repair Pulumi state

bashยทView & export state
# Export full stack state to JSON
pulumi stack export

# Export to a file
pulumi stack export --file state.json

# Import state from a file (replace current state)
pulumi stack import --file state.json

# Show all resources in state
pulumi stack --show-urns

# Get URN of a specific resource
pulumi stack --show-urns | grep BucketName
bashยทImport existing resources
# Import an existing cloud resource into state
# (resource must already be defined in code)
pulumi import aws:s3/bucket:Bucket my-bucket my-existing-bucket-name
pulumi import aws:ec2/instance:Instance web-server i-0abcd1234efgh5678
pulumi import azure:core/resourceGroup:ResourceGroup rg myResourceGroup

# Import in bulk from JSON file
pulumi import --file resources.json

# Protect a resource from accidental deletion
# In code (TypeScript):
# const bucket = new aws.s3.Bucket("b", {}, { protect: true });
bashยทMove & remove from state
# Remove a resource from state without destroying it
pulumi state delete "urn:pulumi:dev::my-app::aws:s3/bucket:Bucket::my-bucket"

# Unprotect a resource (allow deletion)
pulumi state unprotect "urn:pulumi:dev::my-app::aws:s3/bucket:Bucket::my-bucket"

# Rename / move a resource within state
pulumi state rename \
  "urn:pulumi:dev::my-app::aws:s3/bucket:Bucket::old-name" \
  new-name

# Move resources between stacks
pulumi state move \
  --source dev --dest prod \
  "urn:pulumi:dev::my-app::aws:s3/bucket:Bucket::my-bucket"

Backends

Switch between Pulumi Cloud, S3, Azure Blob, and local backends

bashยทLogin & backend selection
# Pulumi Cloud (default)
pulumi login

# Local filesystem backend
pulumi login --local

# Amazon S3
pulumi login s3://my-state-bucket
pulumi login s3://my-state-bucket/pulumi-state   # with prefix

# Azure Blob Storage
pulumi login azblob://my-container

# GCP Storage
pulumi login gs://my-state-bucket

# Check current backend
pulumi whoami -v

# Logout
pulumi logout

Providers & Plugins

Install, list, and manage provider plugins

bashยทPlugin management
# Install all plugins required by the current project
pulumi plugin install

# Install a specific provider plugin
pulumi plugin install resource aws 6.0.0
pulumi plugin install resource kubernetes 4.0.0

# List installed plugins
pulumi plugin ls

# Remove a plugin
pulumi plugin rm resource aws 5.0.0

# Remove all unused plugins
pulumi plugin prune
bashยทProvider config (TypeScript)
import * as aws from "@pulumi/aws";
import * as pulumi from "@pulumi/pulumi";

// Explicit provider (overrides stack config)
const usEast = new aws.Provider("us-east", { region: "us-east-1" });
const euWest = new aws.Provider("eu-west", { region: "eu-west-1" });

// Use a specific provider for a resource
const bucket = new aws.s3.Bucket("bucket", {}, { provider: usEast });

// Assume role
const assumedRole = new aws.Provider("assumed", {
  region: "us-east-1",
  assumeRole: { roleArn: "arn:aws:iam::123456789:role/DeployRole" },
});

Resource Options

dependsOn, protect, ignoreChanges, aliases, transformations

bashยทCommon resource options (TypeScript)
import * as aws from "@pulumi/aws";

const bucket = new aws.s3.Bucket("bucket", {
  bucket: "my-bucket",
}, {
  // Prevent accidental deletion
  protect: true,

  // Ignore drift on specific properties
  ignoreChanges: ["tags", "lifecycleRules"],

  // Explicit ordering
  dependsOn: [otherResource],

  // Delete old resource before creating new one
  deleteBeforeReplace: true,

  // Custom timeout
  customTimeouts: {
    create: "10m",
    update: "10m",
    delete: "5m",
  },
});
bashยทaliases, parent & import
import * as aws from "@pulumi/aws";

// Alias: rename a resource without destroying + recreating
const bucket = new aws.s3.Bucket("new-name", {}, {
  aliases: [{ name: "old-name" }],
});

// Parent: model logical hierarchy
const bucket = new aws.s3.Bucket("bucket", {});
const policy = new aws.s3.BucketPolicy("policy", {
  bucket: bucket.id,
  policy: "...",
}, { parent: bucket });

// Import existing resource (run once, then remove)
const existing = new aws.s3.Bucket("imported", {
  bucket: "already-exists",
}, { import: "already-exists" });

Stack References

Share outputs between stacks

bashยทStackReference (TypeScript)
import * as pulumi from "@pulumi/pulumi";

// Reference another stack's outputs
const networkStack = new pulumi.StackReference("acme/network/prod");

// Get a typed output value
const vpcId = networkStack.getOutput("vpcId");
const subnetIds = networkStack.requireOutput("subnetIds");

// Use the output in a resource
const cluster = new aws.ecs.Cluster("cluster", {
  // outputs are pulumi.Output<T> โ€” use .apply() to transform
  tags: { vpcId: vpcId.apply(id => String(id)) },
});

// Self-referencing current project/stack
const currentStack = pulumi.getStack();       // "prod"
const currentProject = pulumi.getProject();   // "my-app"
const currentOrg = pulumi.getOrganization();  // "acme"

Component Resources

Build reusable abstractions over multiple resources

bashยทComponentResource (TypeScript)
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

interface StaticSiteArgs {
  indexDocument?: string;
}

class StaticSite extends pulumi.ComponentResource {
  public readonly bucketName: pulumi.Output<string>;
  public readonly websiteUrl: pulumi.Output<string>;

  constructor(name: string, args: StaticSiteArgs, opts?: pulumi.ComponentResourceOptions) {
    super("my:web:StaticSite", name, {}, opts);

    const bucket = new aws.s3.Bucket(`${name}-bucket`, {
      website: { indexDocument: args.indexDocument ?? "index.html" },
    }, { parent: this });

    this.bucketName = bucket.id;
    this.websiteUrl = bucket.websiteEndpoint;

    // Required: register all child outputs
    this.registerOutputs({
      bucketName: this.bucketName,
      websiteUrl: this.websiteUrl,
    });
  }
}

// Use it
const site = new StaticSite("my-site", { indexDocument: "index.html" });
export const url = site.websiteUrl;

Automation API

Drive Pulumi programmatically from TypeScript/Python

typescriptยทInline program (TypeScript)
import * as auto from "@pulumi/pulumi/automation";
import * as aws from "@pulumi/aws";

const program = async () => {
  const bucket = new aws.s3.Bucket("auto-bucket", {
    acl: "private",
  });
  return { bucketName: bucket.id };
};

async function main() {
  const stack = await auto.LocalWorkspace.createOrSelectStack({
    stackName: "dev",
    projectName: "automation-example",
    program,
  });

  // Set config
  await stack.setConfig("aws:region", { value: "us-east-1" });

  // Preview
  await stack.preview({ onOutput: console.log });

  // Deploy
  const upResult = await stack.up({ onOutput: console.log });
  console.log("Bucket:", upResult.outputs.bucketName.value);

  // Destroy
  await stack.destroy({ onOutput: console.log });
}

main().catch(console.error);
pythonยทInline program (Python)
import pulumi
import pulumi_aws as aws
from pulumi import automation as auto

def pulumi_program():
    bucket = aws.s3.Bucket("auto-bucket")
    pulumi.export("bucket_name", bucket.id)

stack = auto.create_or_select_stack(
    stack_name="dev",
    project_name="automation-example",
    program=pulumi_program,
)

stack.set_config("aws:region", auto.ConfigValue("us-east-1"))

print("Previewing...")
stack.preview(on_output=print)

print("Deploying...")
up_res = stack.up(on_output=print)
print("Bucket:", up_res.outputs["bucket_name"].value)

Useful Commands & Tips

Logs, watch mode, policy packs, and CI integration

bashยทLogs & watch
# Stream resource logs
pulumi logs

# Follow logs in real time
pulumi logs --follow

# Filter by resource name
pulumi logs --resource myFunction

# Watch mode: auto-deploy on code changes
pulumi watch
bashยทPolicy as code (CrossGuard)
# Create a policy pack
pulumi policy new aws-typescript

# Run policy against a stack (local โ€” no publish)
pulumi preview --policy-pack ./policy

# Publish a policy pack to Pulumi Cloud
pulumi policy publish

# Enable a published policy on a stack
pulumi policy enable acme/aws-policies latest --stack dev

# Disable a policy
pulumi policy disable acme/aws-policies --stack dev
bashยทCI / non-interactive usage
# Authenticate with access token (set in CI env)
export PULUMI_ACCESS_TOKEN=pul-abc123...

# Non-interactive deploy
pulumi up --yes --skip-preview

# Non-interactive destroy
pulumi destroy --yes

# Exit codes: 0=success, 1=error, 2=no changes (preview)
pulumi preview; echo "Exit: $?"

# Suppress colors for CI logs
pulumi up --yes --color never

# Set concurrency for resource operations
pulumi up --parallel 20