docs/architecture/deployment-execution/steps.md
Deployments in Pulumi happen as a series of Steps. These are generated by the
step generator,
and then executed by the deployment executor.
When a Step is
applied using the Apply() function, it interacts with backing providers to produce the
function that will ultimately register the resource against Pulumi's desired state graph.
Each step also has an old and a new state, the old state representing the state
we read from the snapshot, or the result of a previous step, and the new state
representing the state that the step should have after the Apply().
Pulumi's deployment system uses different step types to represent the various operations that can be performed on resources. These steps fall into several categories:
Externally Mutating Steps directly modify resources via provider operations:
Non-Mutating Steps represent logical operations:
Special Steps handle specific scenarios:
sequenceDiagram
participant SG as Step Generator
participant SE as Step Executor
participant E as Events
participant P as Provider
SG->>SE: Submit Step (in chain)
SE->>E: OnResourceStepPre
E-->>SE: payload
SE->>SE: step.Apply()
alt Custom Resource
SE->>P: Create/Update/Delete/Read
P-->>SE: Response + New State
else Component/Preview
SE->>SE: State manipulation only
end
SE->>E: OnResourceStepPost
SE->>SE: stepComplete()
SE-->>SG: Step finished
Each step follows this lifecycle:
OnResourceStepPre callback fires before executionApply() method executes, potentially calling
provider methodsOnResourceStepPost callback fires after executionReplacements come in two flavors:
The new resource is created first, then the old is deleted. Both resources exist
in state temporarily, with the old marked Delete = true after creation. The
ReplaceStep is created as a marker after the CreateStep is generated.
The old resource is deleted first in the provider, but kept in the snapshot, marked
as PendingReplacement. Then the new resource is created, removing the resource
PendingReplacement from the state.
All steps implement the Step interface and have:
SameSteps are generated whenever a resource does not change. This can happen
for example if we have an existing resource and the provider Diff call returns
no changes, or if there's a targeted up, and the resource is not being
targeted. We also use SameSteps for "skipped creates". Those are also emitted
if a resource is not targeted, but is being created by the program.
When this type of step is applied, we copy the old inputs to the new inputs, and complete the step. The completion function will mark the resulting state as the "new" state. If the step is skipped, we do no such copying.
CreateSteps are emitted whenever we want to create a new resource. Note that
there are two variations of create steps, namely a regular create, and a
create-replacement. The create step simply creates a new resource. A create-replacement step is generated when we do a replace of a resource. It is always returned combined with a ReplaceStep` (see below).
During the application of the create step, we first run the BeforeCreate
hooks. Then if we have a custom resource, we call Create on the provider to
actually create the resource. For both Custom and Component resources we then
update the "New" state, and finally call the AfterCreate hook once the
resource creation is done.
If we have a create-replacement step, and the old resource is not yet deleted
(i.e. it's not marked deleteBeforeReplace), we also mark the old resource as
Delete. This allows us to keep both versions of the resource in the snapshot,
and keep track of the resource that has not yet been deleted from the cloud
provider. Note that this is one of the places where we edit the deployment state
directly instead of through the snapshot manager.
DeleteSteps are generated when a resource needs to be removed from the
deployment. This includes resources removed from the program, replacements
requiring deletion, or external resource discards.
The delete step runs BeforeDelete hooks, checks for protection (failing if
protected and not a replacement), and then:
Delete methodFor delete-before-replace operations, the PendingReplacement flag is set after
successful deletion. AfterDelete hooks run immediately for both custom and
component resources (unlike Create/Update which defer component hooks to
RegisterResourceOutputs).
UpdateSteps modify existing resources when the provider indicates changes are
needed that don't require replacement.
The update step propagates the resource ID and timestamps, runs BeforeUpdate
hooks, then calls the provider's Update method with old and new
inputs/outputs. The provider returns updated outputs, and the modified timestamp
is updated. For custom resources, AfterUpdate hooks run at completion; for
component resources, they run during RegisterResourceOutputs.
ReplaceSteps are logical markers indicating a resource replacement. The actual
work happens in the associated CreateStep and DeleteStep. This step exists
for visualization and tooling purposes.
The Apply method is essentially a no-op, verifying that pending delete
resources are properly marked for deletion.
ReadSteps import existing resources as external references (marked with the
External flag, meaning Pulumi doesn't own their lifecycle).
The provider's Read method fetches the current cloud state. For
ReadReplacement steps, the old managed resource is marked for deletion. Unlike
most steps, Read steps execute during previews to fetch actual state.
RefreshSteps update resource state by reading current provider state. Note that
these steps can be issued either by the deployment executor or the step generator.
Component resources, providers, and pending replacements aren't refreshed. For
custom resources, the provider's Read fetches current state. If the provider
returns a blank ID, the resource is considered deleted. A diff is computed
showing changes between provider state and program's desired state (inverted
from normal diff direction). The ResultOp method returns whether the resource
is same, updated, or deleted.
ImportSteps bring existing cloud resources under Pulumi management (unlike
ReadSteps which create external references).
For custom resources, the provider's Read method fetches state using the
ImportID. An "old" state is synthesized for diff display. For planned imports
(from pulumi import command), the resource must not exist in state, and
provider Check validates inputs. For import replacements, the original
resource is marked for deletion.
RemovePendingReplaceSteps clean up the PendingReplacement flag set during
delete-before-replace operations.
The Apply method is a no-op. This step exists for tracking and display
purposes.
DiffSteps leverage the step executor's parallel worker pool for computing
resource diffs during step generation.
Calls the provider's Diff method and communicates results via a promise
completion source rather than returning errors. Never participates in normal
step error handling.
ViewSteps are virtual steps for view resources. They communicate
what happened to views for display and analysis, but don't perform provider operations.
The Apply method is mostly a no-op. For OpCreateReplacement view steps, it
marks the old resource for deletion. Returns the recorded status and error.