2.8. State Management

Pulumi’s state file is the source of truth for every deployed resource — its properties, outputs, and dependencies. This lab covers how to inspect it, detect drift, and perform surgical repairs.

Step 1 — Inspect the state file

Export the full state and browse it with jq:

pulumi stack export | jq . | less

Find all container resources and list their URNs:

pulumi stack export | jq -r '.deployment.resources[] | select(.type == "docker:index/container:Container") | .urn'

You should see two URNs — one for blue-container and one for green-container. Note the blue-container URN — you will need it in Step 3.

Explanation

pulumi stack export writes the full stack state to stdout as a JSON document. The top-level structure is:

{
  "version": 3,
  "deployment": {
    "resources": [ ... ]
  }
}

Each entry in resources represents one managed resource and records everything Pulumi knows about it: the provider, the logical name, the physical name, all input and output properties, and the list of resource URNs it depends on.

The state is what makes pulumi preview possible — Pulumi diffs your program against this file rather than querying the live infrastructure on every run.

State backends

The state file lives in a backend — the storage location Pulumi uses for a given project:

BackendWhen to use
Local (--local)Solo dev and this lab — file on your laptop
Pulumi CloudManaged backend with UI, history, and RBAC
Azure Blob / S3Self-hosted team backend — the pattern for customer projects

In the Azure lab you will move to an Azure Blob backend so the state is shared across the team and survives beyond your laptop.


Step 2 — Detect and repair drift

Pulumi’s desired state lives in code; drift happens when someone changes infrastructure outside Pulumi — a container stopped via the CLI, a resource deleted in the Azure portal, a tag edited by hand.

Force drift by removing the blue container directly from Docker:

docker rm -f $(docker ps -qf "name=blue")

Confirm only green remains:

docker ps --format '{{.Names}}'

Now run pulumi refresh to compare the state file against live infrastructure:

pulumi refresh

Observe the output — Pulumi detects blue-container is missing and marks it for replacement. Bring the stack back to the desired state:

pulumi up

Explanation

pulumi refresh queries the actual infrastructure (Docker, Azure, …) for every resource in the state file and reconciles any differences it finds:

What refresh doesResult
Resource still exists, matchesNo change
Resource exists but driftedState updated to reflect actual properties
Resource is goneState marks the resource as needing recreation

After pulumi refresh, the state accurately reflects reality. pulumi up then applies only the changes needed to reach the desired state — in this case, recreating blue-container.

Refresh before up in production

In long-running environments it is good practice to run pulumi refresh before pulumi up so you are diffing against reality rather than a potentially stale state file.


Step 3 — Surgical state repairs

Sometimes a resource is deleted out-of-band and you do not want Pulumi to recreate it — you want Pulumi to simply forget it. pulumi state delete removes a resource from the state file without touching the actual infrastructure.

Look up the URN for blue-container (you noted it in Step 1):

pulumi stack export | jq -r '.deployment.resources[] | select(.type == "docker:index/container:Container") | .urn'

Remove blue-container from state using the URN:

pulumi state delete 'urn:pulumi:dev::pulumi-basics::workshop:index:WebServer$docker:index/container:Container::blue-container'

Confirm only one container remains in state, while both are still running in Docker:

pulumi stack export | jq '[.deployment.resources[] | select(.type == "docker:index/container:Container")] | length'
docker ps --format '{{.Names}}'

Run pulumi up to bring state and reality back in sync — Pulumi recreates blue-container since it is declared in code but absent from state:

pulumi up

Explanation

pulumi state delete is a break-glass tool for situations pulumi refresh cannot handle — for example, a resource that was deleted in the Azure portal in a way that left its provider in an error state, or a resource you want to hand off to another stack.

CommandWhat it does
pulumi refreshSyncs state to reality — updates or marks missing resources
pulumi state delete <urn>Removes the resource from state; leaves real infrastructure alone
pulumi state unprotect <urn>Clears the protect flag in state without changing code

pulumi state unprotect is the counterpart to protect=True in ResourceOptions — useful when a protected resource must be deleted urgently and re-deploying to flip the flag is not an option.

URNs encode the full parent chain

A URN encodes the stack, project, parent chain, resource type, and logical name. For a top-level resource:

urn:pulumi:<stack>::<project>::<type>::<name>

For a resource nested inside a component, the parent type is prepended with $:

urn:pulumi:<stack>::<project>::<parent-type>$<child-type>::<name>

This is why blue-container’s URN contains workshop:index:WebServer$docker:index/container:Container — it lives inside the blue WebServer component.

The URN is stable as long as you do not rename the resource or move it to a different parent. Either change causes a delete-and-recreate.