Automation API & dynamic providers

Pulumi as a library; custom resources.

Advanced14 min · lesson 9 of 12

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.

provision.ts
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 code
console.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.

apikey.ts
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);
}
}
Automation API runs real infrastructure — guard it
Because the Automation API deploys from inside your application, a bug or an untrusted input can create or destroy real cloud resources at runtime. Treat it as privileged: validate inputs, run it with least-privilege cloud credentials, isolate per-tenant state, and put the same approval/limits around it you would around a CI apply — it is not just a library call, it is production changes.