The concept of a dry run within the Pulumi ecosystem represents a critical mechanism for infrastructure-as-code safety, providing a non-destructive window into the intended state of a system before any physical modifications are committed. At its architectural core, Pulumi operates through a reconciliation loop, where the system compares the current state of the actual world—the deployed infrastructure—against the desired state defined in the code. This reconciliation process is the primary driver of the dry run functionality, allowing operators to preview changes, verify resource dependencies, and ensure that the projected outcome aligns with the intended design. In an enterprise environment, the dry run is not merely a convenience but a safeguard against catastrophic misconfigurations, acting as a validation gate that can be integrated into continuous integration and continuous deployment (CI/CD) pipelines to prevent unintended resource deletion or modification.
The operational flow of Pulumi mirrors that of other industry-standard tools like Terraform, utilizing a state backend to track the identity and properties of deployed resources. When a dry run is initiated, Pulumi does not modify the target environment; instead, it calculates the delta between the state file and the current reality. This process involves querying the cloud provider's API to refresh the state, ensuring that the preview is based on the most recent configuration. The resulting plan provides a detailed account of which resources will be created, updated, or deleted, thereby allowing the user to make an informed decision before proceeding with the actual deployment.
The Architecture of Dry Run Execution
The technical implementation of a dry run in Pulumi relies on a combination of CLI commands and runtime logic that allows the program to diverge its behavior based on whether it is in a preview phase or an update phase. This capability is essential for developers who need to inject specific logic that should only execute during a final application, such as sending notification alerts or triggering post-deployment hooks.
The execution of a dry run is primarily handled through the pulumi preview command. To ensure maximum accuracy and visibility, specifically within managed environments like Spacelift, this command is often augmented with several critical flags.
The following table details the specific CLI arguments used during the planning phase of a Pulumi run:
| Flag | Function | Operational Impact |
|---|---|---|
--refresh |
State Synchronization | Forces Pulumi to check the actual state of resources in the cloud before calculating the diff. |
--diff |
Detailed Change Analysis | Provides a granular view of the specific property changes within a resource. |
--show-replacement-steps |
Lifecycle Visibility | Explicitly shows the steps involved when a resource must be replaced rather than updated. |
When these flags are combined, the command pulumi preview --refresh --diff --show-replacement-steps provides a comprehensive audit trail of the projected changes. The impact of this approach is that it eliminates "state drift" where the local state file differs from the actual cloud environment, ensuring the dry run is an honest representation of what will happen.
Runtime Logic and Dry Run Detection
One of the most powerful aspects of Pulumi is the ability for the code itself to be aware of whether it is being executed as a dry run. This is achieved through the runtime environment, allowing developers to implement conditional logic within their infrastructure programs.
In the Python runtime, for example, the pulumi.runtime.is_dry_run() function serves as the primary mechanism for this detection. This function returns a boolean value that the program can use to branch its execution paths.
Consider the following implementation logic:
```python
import pulumi
import pulumi.runtime
if pulumi.runtime.isdryrun():
print("doing a preview")
else:
print("not doing a preview")
```
When this code is executed via p up or pulumi preview, the runtime identifies the dry run state. If the is_dry_run() method returns true, the program can execute "preview-only" logic—such as printing status messages—without performing the actual operational tasks. When the user confirms the update and the program moves to the "Updating" phase, the is_dry_run() check returns false, triggering the actual deployment logic.
The internal mechanics of this runtime configuration are managed through the pulumi.runtime.Settings class. The system attempts to configure the environment using a set of parameters, including a dry_run boolean. If the Settings class encounters unsupported parameters, it utilizes a fallback mechanism involving the inspect.signature of the __init__ method to filter out invalid arguments, ensuring that the runtime configuration is applied successfully regardless of the specific version of the SDK in use.
Integration with Spacelift Workflows
In a managed orchestration environment such as Spacelift, the Pulumi dry run is integrated into the lifecycle of a run. This ensures that every change is vetted through a structured process of initialization, planning, and applying.
The initialization phase prepares the environment for the dry run by executing the following commands:
pulumi login: Authenticates the session using the configured login URL.pulumi stack select --create --select: Selects the specific Pulumi stack name defined in the vendor-specific settings.
Once initialized, the planning phase commences. Spacelift utilizes the pulumi preview --refresh --diff --show-replacement-steps command to generate the plan. The output of this command is then processed and presented to the user.
For advanced users, Spacelift allows the injection of additional CLI arguments through environment variables to further customize the dry run:
SPACELIFT_PULUMI_CLI_ARGS_preview: Adds arguments specifically to the preview/dry run command.SPACELIFT_PULUMI_CLI_ARGS_up: Adds arguments to the apply command.SPACELIFT_PULUMI_CLI_ARGS_destroy: Adds arguments to the destroy command.
The impact of this integration is that it allows for highly customized deployment pipelines where dry runs can be strictly controlled and augmented with specific organizational requirements.
Policy Enforcement and Plan Analysis
The dry run's output is not just for human consumption; it is also the primary input for policy-as-code engines. In Spacelift, plan policies are used to evaluate the results of a Pulumi dry run to ensure that the projected changes adhere to security and compliance standards.
When a Pulumi dry run is completed, the resulting plan is provided to the policy engine in a pulumi field. This field contains the raw Pulumi plan, which is distinct from the terraform field used in other workflows.
A critical security feature of the Pulumi dry run is the handling of secrets. Pulumi automatically detects sensitive values and encodes them as [secret] within the plan output. Consequently, no additional string sanitization is performed on Pulumi plans because the engine ensures that actual secret values are never exposed in the preview logs.
The data structure of a Pulumi plan usually includes:
- Type: The resource type being modified.
- Name: The logical name of the resource.
- Plan: The action to be taken (create, update, delete, or no change).
- Info: Additional diagnostics or messages.
The Role of Outputs in Dry Run Semantics
The behavior of dry runs is deeply connected to the concept of "Outputs" in Pulumi. Outputs are designed as short pipelines that transform values from one resource into arguments for another. This monadic structure is essential for supporting the preview/dry-run feature.
Outputs incorporate the semantics of an Option type. This is necessary because, during a dry run, certain computed values—those provided by the cloud engine only after a resource is actually created—are missing. Because these values are absent during the preview phase, the Outputs can "short-circuit."
The operational consequences of this short-circuiting are as follows:
- If a program relies on a
flatMapormapoperation on an Output value that is only obtained from the actual environment, the subsequent steps in the chain will be skipped during a dry run. - This results in a "broken view" of the changes if the program is structured too heavily on these chains for logic that must occur during the preview.
- To mitigate this, it is recommended to write programs in a direct style and use for-comprehensions primarily for transforming properties passed from configuration or declared resources.
By understanding this short-circuiting logic, developers can ensure that their dry runs provide an accurate and complete preview of the infrastructure changes without missing critical steps due to missing runtime values.
Comparative Analysis of Runtimes for Dry Runs
While Pulumi supports multiple languages, the experience of implementing and testing dry runs varies across runtimes. For those beginning their journey with Pulumi, the choice of language impacts the developer experience during the preview phase.
JavaScript is highlighted as providing one of the most pleasant experiences with Pulumi, offering a smooth transition into the ecosystem. Users can then migrate to TypeScript, which is highly recommended due to its type safety, providing a more robust developer experience when defining the resources that will be processed during a dry run. Other options include ClojureScript, which also compiles to JavaScript.
The following table summarizes the runtime recommendations for optimal dry run development:
| Recommended Language | Primary Benefit | Relationship to Dry Run |
|---|---|---|
| TypeScript | Type Safety | Reduces errors in resource definition before the preview is run. |
| JavaScript | Ease of Entry | Provides a fast onboarding path for basic dry run testing. |
| ClojureScript | Functional Paradigm | Compiles to JS, leveraging the same underlying runtime logic. |
Limitations of the Pulumi Dry Run Ecosystem
Despite the robustness of the dry run functionality, there are specific architectural limitations that users must be aware of, particularly when using Pulumi in conjunction with orchestration platforms like Spacelift.
One significant limitation is that Spacelift module CI/CD is not currently available for Pulumi. This means that the traditional module-based delivery pipeline used in Terraform cannot be replicated exactly for Pulumi projects.
Additionally, the "Import" functionality is not supported as a first-class integrated action in the same way it might be in other tools. To achieve the effect of an import, users must manually run a task to import resources into their state. Once imported, these resources then become part of the state backend and will be correctly identified and analyzed during subsequent dry run executions.
Comprehensive Analysis of Dry Run Lifecycle
The Pulumi dry run is not a static check but a dynamic component of the infrastructure lifecycle. When evaluated as a whole, the process moves from the theoretical (code) to the projected (preview) and finally to the actual (update).
The effectiveness of the dry run is predicated on the accuracy of the state backend. If the state is corrupted or out of sync, the dry run may provide a misleading preview. This is why the use of --refresh is non-negotiable in production environments; it forces a reconciliation between the state file and the cloud provider's current truth.
Furthermore, the ability to programmatically detect a dry run via is_dry_run() allows for the creation of "safe" infrastructure code. For instance, a developer can ensure that a resource that triggers a costly operation or an irreversible change is only executed when the runtime confirms it is no longer in a preview state.
In conclusion, the Pulumi dry run represents a sophisticated blend of state management, runtime introspection, and policy integration. By leveraging the reconciliation loop and the specific semantics of Outputs, Pulumi provides a mechanism that allows developers to move from high-level intent to low-level implementation with a significant degree of confidence. The integration of these dry runs into automated pipelines via tools like Spacelift further enhances this by adding a layer of policy-based validation, ensuring that no infrastructure change is committed without passing through a rigorous, transparent, and secure preview process.