Pulumi
How to draw a Pulumi stack as a Plotwork diagram that is published again whenever the stack's outputs change. Pulumi runs the Plotwork Terraform provider through its Any Terraform Provider package, so a TypeScript or Python program gets the same resources as Terraform. It is for platform engineers who keep their infrastructure in Pulumi.
TL;DR
| Question | Answer |
|---|---|
| How do I add it? | pulumi package add terraform-provider registry.terraform.io/plotwork-official/plotwork 0.1.0-beta.2, in the project folder. |
| Which Pulumi? | 3.147.0 or later. Tested with 3.272.0, TypeScript on Node.js 22 and Python 3.14. |
| What do I get? | plotwork.Diagram publishes, getRender turns source into an SVG, getImport turns files into source, and the organisation's brand, icons and patterns as resources. |
| Where can the SVG go? | Anywhere a Pulumi resource can write, such as aws.s3.BucketObjectv2. |
| How does it sign in? | pulumi config set --secret plotwork:apiKey pw_..., or PLOTWORK_API_KEY. |
| Which plan? | Publishing works on every plan. Rendering and importing need Pro or above. |
Does pulumi preview need a key? | Not for a diagram built from outputs that do not exist yet. A render or import whose input is already known is called during the preview, so it needs a key. |
| Is there an example? | examples/pulumi/typescript-stack-diagram (opens in a new tab) in the examples repository. |
1. Add the provider
Run this in the folder that holds Pulumi.yaml:
pulumi package add terraform-provider registry.terraform.io/plotwork-official/plotwork 0.1.0-beta.2It generates an SDK in sdks/plotwork, links it into the project as the dependency @pulumi/plotwork for Node.js, or as sdks/plotwork in requirements.txt for Python, and records the package in Pulumi.yaml:
packages: plotwork: source: terraform-provider version: 1.5.0 parameters: - registry.terraform.io/plotwork-official/plotwork - 0.1.0-beta.2| Rule | Why |
|---|---|
Give the full address, registry.terraform.io/plotwork-official/plotwork | The short form plotwork-official/plotwork looks in the OpenTofu registry, which does not carry the provider, and fails with "does not have a provider named". |
| Give the exact version | 0.1.0-beta.2 is a pre-release. It talks to https://api-preview.plotwork.dev by default, and needs a key from the preview portal. |
Commit Pulumi.yaml, not sdks/ | pulumi install generates the SDK again from the packages entry on a fresh checkout or in CI. |
2. Sign in
Keep the key in the stack's configuration as a secret, so pulumi up and pulumi destroy both have it:
pulumi config set --secret plotwork:apiKey pw_...pulumi config set plotwork:organization acme # only to publish to an organisation's library| Config | Variable | What |
|---|---|---|
plotwork:apiKey | PLOTWORK_API_KEY | A personal key from API keys, or an organisation key from Keys and CI |
plotwork:organization | PLOTWORK_ORG | The organisation a diagram publishes to when it names none. Needed with an organisation key |
plotwork:apiUrl | PLOTWORK_API_URL | Another API address |
Set the key before the first pulumi up. Pulumi keeps the provider's settings from the last update, and pulumi destroy uses those.
3. Draw the stack's outputs in TypeScript
Build the source from your resources' outputs. Pulumi publishes the diagram after them, and again when one of them changes. With a bucket set, it also renders the SVG and writes it to S3:
import * as aws from "@pulumi/aws";import * as plotwork from "@pulumi/plotwork";import * as pulumi from "@pulumi/pulumi";
const config = new pulumi.Config();const organization = config.get("organization");const bucket = config.get("bucket");
// Your resources' outputs: vpc.cidrBlock, lb.dnsName, db.address.const deployment = pulumi.output({ vpcCidr: vpc.cidrBlock, lbDnsName: lb.dnsName, dbEndpoint: db.address });
const source = deployment.apply((d) => [ 'flow "Production network"', 'node users person "Users"', `node lb gateway "Load balancer" sub="${d.lbDnsName}"`, `group vpc "VPC ${d.vpcCidr}" type=network {`, ' node app service "App"', ` node db database "PostgreSQL" sub="${d.dbEndpoint}"`, "}", 'users -> lb : "HTTPS"', 'lb -> app : "routes"', 'app -> db : "queries" role=data', "",].join("\n"));
const diagram = new plotwork.Diagram("network", { source, name: "Production network", folder: "Platform/Network", scheme: "auto", ...(organization === undefined ? { alias: "prod-network" } : { organization, classification: "internal" }),});
export const link = organization === undefined ? diagram.stableUrl : diagram.viewUrl;
if (bucket !== undefined) { const picture = plotwork.getRenderOutput({ source, scheme: "auto" }); new aws.s3.BucketObjectv2("network-svg", { bucket, key: "diagrams/network.svg", content: picture.svg, contentType: "image/svg+xml", });}The writing diagrams guide describes the language. The example in the examples repository runs this program with a stand-in for the stack, so it needs no cloud account.
4. The same in Python
pulumi package add writes sdks/plotwork into requirements.txt and installs it as pulumi_plotwork:
import pulumiimport pulumi_aws as awsimport pulumi_plotwork as plotwork
config = pulumi.Config()organization = config.get("organization")bucket = config.get("bucket")
# Your resources' outputs.deployment = pulumi.Output.all(vpc_cidr=vpc.cidr_block, lb_dns_name=lb.dns_name)
def to_source(d): return "\n".join([ 'flow "Production network"', 'node users person "Users"', f'node lb gateway "Load balancer" sub="{d["lb_dns_name"]}"', f'group vpc "VPC {d["vpc_cidr"]}" type=network {{', ' node app service "App"', "}", 'users -> lb : "HTTPS"', 'lb -> app : "routes"', ]) + "\n"
source = deployment.apply(to_source)
diagram = plotwork.Diagram( "network", source=source, name="Production network", folder="Platform/Network", scheme="auto", organization=organization, classification="internal" if organization else None,)pulumi.export("link", diagram.view_url if organization else diagram.url)
if bucket: picture = plotwork.get_render_output(source=source, scheme="auto") aws.s3.BucketObjectv2( "network-svg", bucket=bucket, key="diagrams/network.svg", content=picture.svg, content_type="image/svg+xml", )5. Publish to an organisation's library
Set organization on the diagram, or plotwork:organization for the whole stack. The diagram goes to that organisation's private library, and viewUrl (view_url in Python) is the portal page its members open. An organisation's diagram has no public url and takes no alias; a stable link is for a personal library. classification sets public, internal or confidential.
Limits
| Limit | What to do |
|---|---|
The provider is a pre-release, 0.1.0-beta.2, and talks to Plotwork's preview API | Use a key from the preview portal, or set plotwork:apiUrl |
Each change to the source publishes a new diagram and deletes the old one, but Pulumi keeps the resource's first id | Read links from url, stableUrl and viewUrl, never from id |
getRender and getImport need Pro or above, and run during pulumi preview when their input is known | Keep a key in the stack's configuration for previews in CI |
| The generated SDK is local to the project | Run pulumi install after a fresh checkout |
| What a render, an import or a publish sends leaves your network | Send source and configuration, never state or secrets |
See also
| Page | Why |
|---|---|
| Terraform provider | The same provider in Terraform and OpenTofu, and every argument |
| Hosted diagrams | Links, stable links and how long diagrams are kept |
| Organisations | Private libraries, roles and organisation keys |
| Writing diagrams | The language for the source your program builds |