Pulumi Infrastructure State Management and Planned Execution

The mechanism of predicting, verifying, and executing infrastructure changes is the cornerstone of modern Infrastructure as Code (IaC). In the Pulumi ecosystem, this process is governed by the interaction between the project code, the current state of the cloud environment, and the execution plan. Unlike traditional scripting, which executes commands imperatively, Pulumi utilizes a declarative model. This means the engineer describes the desired final state of the infrastructure, and the Pulumi engine determines the most efficient path to reach that state. This path is manifested as a plan—a calculated diff that identifies exactly which resources must be created, updated, or destroyed to align the real-world environment with the codebase.

At its core, the planning phase is a safety mechanism designed to prevent catastrophic configuration drift and accidental resource deletion. By calculating a resource graph, Pulumi can understand the dependencies between different components—for instance, ensuring a virtual network exists before attempting to deploy a virtual machine into it. The transition from a conceptual code change to a physical cloud resource is mediated by the pulumi preview and pulumi up commands, which together form the lifecycle of an update plan. This process allows teams to move from a local development environment to staging and production with a high degree of confidence, knowing that the changes have been audited and verified against the current state.

The Architectural Foundation of Pulumi Projects and Stacks

Before a plan can be generated, Pulumi requires a structured environment consisting of projects and stacks. A project serves as the logical grouping of infrastructure code. Specifically, any directory that contains a Pulumi.yaml file is recognized as a project. This file is a simple YAML document that declares the project's name, the runtime environment, and a brief description.

For example, a project file might look like this:

yaml name: awsfundamentals runtime: nodejs description: A simple Pulumi project

The choice of runtime is a critical decision for the engineering team, as Pulumi supports several industry-standard languages. These include nodejs (which uses TypeScript by default), python, dotnet, go, java, and yaml. By supporting general-purpose programming languages, Pulumi allows developers to use loops, conditionals, and object-oriented patterns to define their infrastructure, rather than relying on a domain-specific language.

While the project defines the "what," the stack defines the "where." A stack is an isolated instance of a Pulumi project, representing a specific deployment target. Common examples include dev, staging, and prod. Stacks allow engineers to use the same code to deploy identical environments with different configurations.

The creation and management of these environments are handled via the command line:

  • To initialize a new stack for a specific environment: pulumi stack init staging
  • To switch between active stacks: pulumi stack select staging

The state of a stack is what allows Pulumi to perform planning. The state is a record of everything Pulumi has deployed. This state can be stored locally in a directory or remotely. For any professional or collaborative project, a remote stack is a requirement. Remote state prevents "state locking" conflicts when multiple engineers are working on the same infrastructure and provides a centralized source of truth. Pulumi Cloud provides this by default, offering access controls, history, and a web UI, but teams can also self-manage state using S3, Azure Blob Storage, or Google Cloud Storage (GCS).

The Mechanics of the Update Plan and Resource Graph

The process of generating a plan begins when the user invokes a command to change the infrastructure. Pulumi does not simply run the code; it executes the program to build a resource graph. This graph is a mathematical representation of every resource declared in the code and the dependencies between them.

Once the graph is built, Pulumi compares this desired state against the last known state stored in the stack. This comparison results in a "diff," which is the fundamental component of the update plan. The diff categorizes every required action into one of three categories:

  • Create: The resource exists in the code but not in the state/cloud.
  • Update: The resource exists in both, but a property in the code differs from the property in the state.
  • Delete: The resource exists in the state/cloud but has been removed from the code.

To visualize this diff without making any actual changes to the cloud provider, users employ the pulumi preview command. This command is essential for production environments as it allows for a "dry run" of the deployment. In automated pipelines, such as those using the pulumi/actions GitHub Action, pulumi preview is run by default on pull requests to ensure that reviewers can see exactly what the code change will do before it is merged.

When the user is ready to apply the changes, they run pulumi up. The execution flow for pulumi up is as follows:

  1. The program is executed.
  2. A resource graph is constructed.
  3. A diff is computed against the current state.
  4. The planned changes are printed to the console.
  5. The user is prompted to confirm the update.

If the user wishes to skip the confirmation prompt for faster execution in a CI/CD pipeline, they can use the --yes flag: pulumi up --yes.

Advanced Plan Constraints and JSON Export

In highly regulated industries or large organizations with strict approval workflows, simply seeing a preview in a console is insufficient. There is a need to "freeze" a plan, get it signed off by a security or operations lead, and then execute that exact plan without the risk of the environment changing in the interim. Pulumi solves this through the use of update plans saved to files.

To generate a persisted plan, the following command is used:

pulumi preview --save-plan=plan.json

This command writes the calculated diff into a plan.json file. This file serves as a snapshot of the intended changes at a specific point in time. Once the plan.json is approved, the update can be constrained to only the operations described in that file:

pulumi up --plan=plan.json

Using a saved plan ensures that the pulumi up operation only performs the changes that were reviewed during the preview phase. No other changes—even if the code was modified after the plan was saved—will be applied.

However, the execution of a saved plan is not an "all-or-nothing" atomic transaction. Instead, Pulumi operates in batches. As the program executes and resource input values resolve, Pulumi processes the update. If at any point the engine detects a discrepancy between the actual state of the cloud and the operations described in the plan.json, the operation will immediately fail. This failure mechanism is a safeguard that prevents unexpected updates that might occur if a resource was manually changed in the cloud console between the time the plan was saved and the time it was executed.

It is important to note that update plans have a specific limitation: they can only record information that is available at preview time. If a resource's value is only determined after it is created (such as a dynamically assigned IP address), that specific detail cannot be locked into the plan file, though the intent to create the resource remains.

Configuration and Secret Management per Stack

A plan is only as accurate as the configuration provided to it. Because different stacks (dev, staging, prod) require different settings—such as different region codes or database passwords—Pulumi uses a stack-specific configuration system.

Setting a standard configuration value is done via:

pulumi config set aws:region us-west-public-1

For sensitive data, such as API keys or passwords, Pulumi provides a built-in encryption mechanism. By using the --secret flag, the value is encrypted before it is stored in the state file:

pulumi config set --secret dbPassword hunter2

The use of per-stack encryption keys ensures that secrets are only decrypted during the actual execution of the program on an authorized machine. This prevents sensitive data from being leaked in plain text within the Pulumi.yaml or the state files stored in remote backends.

Practical Implementation: A Minimalist Deployment Example

To understand how these planning concepts manifest in real code, consider a simple AWS S3 bucket deployment using TypeScript. The process begins with the creation of the project and the definition of the resource.

The code in index.ts would look like this:

typescript import * as aws from '@pulumi/aws'; // Create an AWS resource (S3 Bucket) const bucket = new aws.s3.BucketV2('my-bucket'); // Export the name of the bucket export const bucketName = bucket.id;

The lifecycle of this deployment involves several distinct planning and execution steps:

  1. Initial Deployment: The user runs pulumi up. Pulumi translates the TypeScript code into a plan, detects the request for a new S3 bucket, and prompts for confirmation.
  2. Verification: After the update completes, the user can verify the resource existence using the AWS CLI: aws s3 ls | grep my-bucket.
  3. State Update: Pulumi records the newly created bucket's ID in the stack state.
  4. Resource Destruction: To clean up the environment, the user runs pulumi destroy. Pulumi generates a plan to delete the bucket, updates the state to reflect the deletion, and removes the resource from AWS.

Pulumi Pricing and Tiered Service Models

The scale at which a team uses Pulumi's planning and state management features often dictates their pricing tier. Pulumi provides a range of options from free individual use to enterprise-grade support.

Feature Free Team Enterprise Business-critical
Price $0 $150/mo $150/mo $150/mo
Storage Limited Unlimited Unlimited 100 GB
Team Members 1-5 Unlimited Unlimited Up to 50
Integrations Basic All + Custom All + Custom Advanced
Support Community Dedicated 24/7 Dedicated Priority
Analytics Basic Enterprise Custom Advanced
Custom Branding
API Access Limited Full Full + Custom Full
SSO / SAML

It is noted that while some paid plans start at a base of $50/mo for specific access levels, the primary professional tiers are listed at $150/mo. Teams can optimize these costs by utilizing annual billing, which typically yields a discount of 15-20%.

Additionally, Pulumi provides accessibility programs to lower the barrier to entry:
- Student & Education Discounts: Available for teachers, students, and academic institutions.
- Startup Programs: Reduced rates for early-stage companies through specific partner programs.
- Free Tier: A generous entry point for individuals and small-scale testing.

Resource Providers and the Ecosystem

The ability of Pulumi to create a plan depends entirely on its providers. Providers are essentially plugins that act as the translation layer between Pulumi's high-level resource declarations (written in languages like Python or Go) and the actual API calls required by the cloud vendor.

The provider ecosystem is expansive, covering over 70 different providers, including:
- Major Clouds: AWS, Azure, GCP.
- Container Orchestration: Kubernetes.
- Edge and Networking: Cloudflare.
- Observability: Datadog.

When a user declares a resource, such as aws.s3.BucketV2, the Pulumi engine communicates with the AWS provider to determine the current state of that bucket in the real world. The provider then reports back whether the bucket exists or if its configuration has drifted. This bidirectional communication is what makes the pulumi preview and pulumi up cycle accurate.

Detailed Analysis of State Management and Recovery

The integrity of an update plan is tethered to the integrity of the state file. In Pulumi, state is the record of truth. If the state file becomes corrupted or out of sync with the actual cloud resources (often called "drift"), the plans generated by pulumi preview will be inaccurate.

Unlike some other tools where state is a simple JSON document handled manually by the user, Pulumi Cloud enhances this by wrapping the state in a management layer. This layer provides:

  • Locking: Prevents two engineers from running pulumi up simultaneously, which would otherwise result in a race condition and corrupted state.
  • History: Allows teams to see a versioned history of every state change, making it possible to audit who changed what resource and when.
  • Resource Graphs: Provides a visual representation of the infrastructure, making it easier to understand complex dependencies before planning a deletion.

For those who avoid the managed cloud and use S3 or GCS backends, these patterns mirror the remote backend configurations found in other tools like Terraform. The primary difference is the level of integration provided by the Pulumi Cloud frontend.

When a resource is removed from the code, Pulumi marks it for deletion in the next plan. However, if a resource must be deleted from the state without being deleted from the actual cloud (for example, if a resource was manually moved to another account), engineers can manipulate the state to "forget" the resource, thereby removing it from future update plans.

Conclusion

The Pulumi planning lifecycle represents a sophisticated evolution of infrastructure management. By combining the power of general-purpose programming languages with a rigorous state-tracking mechanism, Pulumi allows for a high-fidelity prediction of cloud changes. The progression from pulumi stack init to pulumi preview and finally to pulumi up creates a controlled pipeline where human intent is translated into machine-executable plans. The introduction of saved plans via plan.json further bridges the gap between agile development and strict corporate governance, ensuring that no change reaches production without explicit verification. As the cloud ecosystem grows in complexity, the ability to compute a precise diff and constrain updates to approved plans remains the most effective defense against downtime and configuration errors.

Sources

  1. Pulumi Pricing
  2. What is Pulumi and how to use it with env0
  3. Pulumi Getting Started
  4. Pulumi Update Plans

Related Posts