Automation API & dynamic providers
Pulumi as a library; custom resources.
Normally you drive Pulumi from the CLI. The Automation API turns that inside out: it exposes the whole engine as a library you call from your own program, so preview, up, and destroy become function calls with no CLI and no subprocess. That lets you build infrastructure into a product — a self-service platform where a web request provisions a customer environment, a CLI of your own, or an integration test harness that stands up and tears down real infra in code.
import { LocalWorkspace } from "@pulumi/pulumi/automation";const stack = await LocalWorkspace.createOrSelectStack({stackName: "customer-42",projectName: "tenant",program: async () => {const b = new aws.s3.BucketV2("tenant-data", {});return { bucket: b.id };},});await stack.setConfig("aws:region", { value: "us-east-1" });const res = await stack.up({ onOutput: console.log }); // deploy from codeconsole.log(res.outputs.bucket.value);
Dynamic providers
When no provider exists for something you need to manage — an internal API, a SaaS with only a REST interface — a dynamic provider lets you implement the CRUD lifecycle yourself in the program’s language. You supply create/read/update/delete functions, and Pulumi manages the resulting resource in state like any other, complete with diffs and dependencies.
const provider: pulumi.dynamic.ResourceProvider = {async create(inputs) {const key = await internalApi.createKey(inputs.name);return { id: key.id, outs: { token: key.token } };},async delete(id) { await internalApi.revokeKey(id); },};export class ApiKey extends pulumi.dynamic.Resource {constructor(name: string, args: any, opts?: any) {super(provider, name, args, opts);}}