2.2. Core Workflow

In this lab you will declare your first resource, then drive it through the preview → up → destroy cycle.

Step 1 — Add the pulumi_random provider

Open requirements.txt and add the random provider:

pulumi==3.243.0
pulumi_random==4.21.0

Install the updated dependencies:

source venv/bin/activate
pip install -r requirements.txt

Explanation

pulumi_random wraps the Terraform Random provider and gives you resource types like RandomPet, RandomString, and RandomPassword. These are useful for generating stable, reproducible names for cloud resources that must be globally unique.

Installing via pip into the project venv/ is the standard Pulumi Python workflow — activate the virtual environment with source venv/bin/activate before running pip commands.

Pin your provider versions

Exact pins (==) prevent a surprise pip install from pulling in a new provider release that changes resource schemas, renames arguments, or alters default behaviour. A provider upgrade should be a deliberate, reviewed change — not a side-effect of a fresh environment setup. This is especially important for pulumi-azure-native, where minor releases regularly add or deprecate API versions.


Step 2 — Declare a RandomPet resource

Replace the contents of __main__.py with the following:

import pulumi
import pulumi_random as random


def main():
    pet = random.RandomPet("my-pet")
    pulumi.export("pet_name", pet.id)


main()

Explanation

Wrapping resource declarations in def main() and calling it at the bottom is a common Pulumi Python convention. It keeps the module-level namespace clean and makes the entry point explicit.

  • random.RandomPet("my-pet") — declares a resource. The first argument is the logical name — Pulumi uses it to track the resource in state.
  • pulumi.export("pet_name", pet.id) — exposes a value as a stack output, visible after pulumi up and queryable with pulumi stack output.
  • pet.id — an Output[str], resolved at deploy time, not at Python parse time.

Step 3 — Preview the plan

Run a dry-run once to get familiar with the output format:

pulumi preview

Read the output carefully before proceeding.

Explanation

pulumi preview performs a dry run: it evaluates your program, computes a diff against the current state, and prints every planned action — without touching any real infrastructure.

Previewing update (dev):

     Type                       Name              Plan
 +   pulumi:pulumi:Stack        pulumi-basics-dev  create
 +   └─ random:index:RandomPet  my-pet             create

Resources:
    + 2 to create
pulumi preview is optional

pulumi up always shows the same diff and asks for confirmation before making any changes — so a separate pulumi preview is rarely needed. Use it when you want a read-only look at a large diff without committing to run it.


Step 4 — Apply the changes

Deploy the stack — Pulumi will show the same preview and ask for confirmation before proceeding:

pulumi up

When Pulumi prompts you to confirm, select yes.

After the apply completes, inspect the stack output:

pulumi stack output pet_name

Explanation

pulumi up re-runs pulumi preview internally and then asks for confirmation before executing. Once confirmed, Pulumi drives the providers to create, update, or delete resources and writes the resulting state to the backend.

The stack output prints the generated pet name — something like charming-toucan. This value is now stored in state and survives restarts.

This is the loop

Edit __main__.pypulumi up. You will repeat this cycle for every change throughout the workshop.


Step 5 — Create multiple resources with a loop

Replace __main__.py with the following:

import pulumi
import pulumi_random as random


def main():
    pets = [random.RandomPet(f"pet-{i}") for i in range(3)]
    for i, pet in enumerate(pets):
        pulumi.export(f"pet_{i}", pet.id)


main()

Run the update:

pulumi up

Explanation

Because Pulumi programs are ordinary Python, you can use any language construct to drive resource creation. The list comprehension produces three independent RandomPet resources — Pulumi sees each as a distinct resource with its own logical name (pet-0, pet-1, pet-2) and tracks them individually in state.

When you run pulumi up, Pulumi diffs the new program against the existing state and produces a plan like:

     Type                       Name               Plan
     pulumi:pulumi:Stack        pulumi-basics-dev
 -   ├─ random:index:RandomPet  my-pet             delete
 +   ├─ random:index:RandomPet  pet-0              create
 +   ├─ random:index:RandomPet  pet-1              create
 +   └─ random:index:RandomPet  pet-2              create

my-pet is deleted and three new resources are created — Pulumi tracks resources by logical name, so renaming a resource is a delete + create, not an in-place update.

This is the Pulumi differentiator

Terraform HCL needs count or for_each meta-arguments to do what a Python list comprehension does here. Any loop, conditional, or function from the standard library is available to you — the full language, not a DSL subset.


Step 6 — Tear down

Delete all resources in the stack and clean up state:

pulumi destroy

Confirm when prompted, then verify state is clean:

pulumi stack

Explanation

pulumi destroy deletes every resource in the stack and removes their entries from state. The stack itself (dev) still exists — only the resources are gone.

CommandWhat it does
pulumi destroyDeletes all resources; empties the state
pulumi stack rm devRemoves the stack record entirely (use with care)

Tear-down is safe to run at any point during the workshop. Re-running pulumi up afterwards will recreate everything from scratch.