skip to content

Coming from Terraform, which Pulumi CLI commands correspond to `terraform plan`, `terraform apply` and `terraform destroy`, and is there anything equivalent to `terraform init`?

level: juniorimportance: should knowfreq 50%

answer

  1. preview, up, destroy
  2. up asks before it applies
  3. --yes for pipelines
  4. refresh is its own command
  5. SDKs come from the package manager

basics

~20 s

pulumi preview corresponds to plan, pulumi up to apply, pulumi destroy to destroy, and pulumi refresh reconciles recorded state with the cloud. There is no provider init step: SDK packages come from the language's package manager and plugins download automatically.

solid answer

~40 s

The everyday mapping is `pulumi preview` for `terraform plan`, `pulumi up` for `terraform apply`, and `pulumi destroy` for `terraform destroy`. `pulumi refresh` is the equivalent of asking Terraform to reconcile state with reality. The differences worth knowing on day one: `pulumi up` shows a preview and prompts for confirmation interactively, so in CI you pass `--yes` or `--skip-preview`; and there is no separate init step for providers, because the SDK you import comes from npm, PyPI or a Go module and the matching plugin is fetched automatically. A Pulumi stack is also the unit you select before running any of these, the way you would pick a workspace or a root module directory in Terraform.

go deeper

for a junior

Memorise the three-line mapping — preview, up, destroy — and be able to add that up shows the changes and asks before applying, and that --yes is what makes it work in a pipeline.

for a middle

Explain why there is no provider init step: SDKs are ordinary language packages resolved by npm, pip or Go modules, with plugins fetched automatically, so version pinning lives in your normal lockfile.

for a senior

Point out where the mapping stops being cosmetic — refresh is opt-in rather than implied, and an apply recomputes its diff rather than replaying a saved plan artifact, which changes how you build the approval gate.

for a principal

Treat the CLI surface as a policy surface: decide who may run an apply, from which job identity, with which role, and make the interactive confirmation irrelevant by putting the real gate in the pipeline.

## The everyday mapping | Terraform | Pulumi | What it does | | --- | --- | --- | | `terraform plan` | `pulumi preview` | Read-only. Reports what would be created, updated, replaced or deleted. | | `terraform apply` | `pulumi up` | Makes the changes. Interactively it previews first and asks for confirmation. | | `terraform destroy` | `pulumi destroy` | Deletes everything the stack manages. | | refresh-only apply | `pulumi refresh` | Re-reads live resources and updates the recorded state to match. | | `terraform init` | *(no direct equivalent)* | See below. | That table is the whole answer for a screening question; the interesting parts are the three places where the mapping is not one-to-one. ## `pulumi up` previews before it applies Run interactively, `pulumi up` performs a preview, prints the change summary, and waits for you to confirm before it touches anything. That makes the two-command habit (`plan`, read, `apply`) less necessary at a terminal — although for anything shared you still want the preview reviewed separately. In automation this prompt would hang the job, so pipelines pass `--yes` to auto-confirm. `--skip-preview` additionally suppresses the preview phase. The usual pipeline is `pulumi preview` on the pull request for review and `pulumi up --yes` on merge. ```bash pulumi preview --diff # on the pull request pulumi up --yes # on merge, non-interactive ``` ## There is no provider init step `terraform init` downloads providers and modules into the working directory and configures the backend before anything else can run. Pulumi has nothing corresponding to the provider half of that, because a provider SDK is an ordinary library dependency: you `npm install @pulumi/aws`, `pip install pulumi-aws` or `go get` it, exactly like any other package, and your existing dependency tooling handles versions and lockfiles. The provider *plugin* binary that goes with it is fetched automatically when a program that needs it runs, so there is no explicit download command in the normal flow. What you do instead, once per project, is scaffold it — `pulumi new` creates a project from a template — and select the stack you are operating on. Every command above runs against a currently-selected stack, so the practical daily rhythm is: select the stack, preview, up. ## `pulumi refresh` is a first-class command In Terraform, reconciling recorded state with the live cloud is folded into `plan` by default and is available as a refresh-only apply. In Pulumi it is a distinct command, `pulumi refresh`, and it is **not** implied by preview or up — you opt in with `--refresh` if you want it as part of a run. Knowing that early saves confusion later, because it means a Pulumi preview can report "no changes" while a resource has been edited in the console. ## Things that look like they should map, and don't - **Saved plans.** Terraform can write a plan to a file and apply exactly that file later. `pulumi up` by default re-runs the program and recomputes the diff rather than replaying a saved artifact, so the two-stage pipeline is shaped differently. - **Workspaces.** Terraform workspaces are multiple states behind one configuration; a Pulumi *stack* is the unit of deployment and carries its own configuration file as well as its own state, so it is a fuller concept than a workspace even though the CLI ergonomics feel similar. - **`terraform fmt` / `validate`.** These belong to HCL. In Pulumi the equivalents come from the language: a formatter and a compiler or type checker you already run, plus your usual linter. ## How to answer this in a screen Give the three-line mapping first — preview, up, destroy — then add the two facts that show you have actually used it: `up` previews and prompts unless you pass `--yes`, and there is no provider init because the SDK is a normal package dependency. That is a complete junior-level answer and it lands in under thirty seconds.

  • Why does a CI job running `pulumi up` need an extra flag?
    Because run interactively `pulumi up` previews the changes and waits for a confirmation that no CI job can give, so the step would hang until it times out. Pipelines pass `--yes` to auto-confirm, and `--skip-preview` if they also want to suppress the preview phase. The reviewed diff normally comes from a separate `pulumi preview` step on the pull request.
  • If there is no init step, how are provider versions pinned?
    Through the language's own dependency tooling. The provider SDK is an ordinary package — `@pulumi/aws` in npm, `pulumi-aws` in PyPI, a Go module — so its version is pinned in package.json and the lockfile, requirements or go.mod, and upgraded through the same review process as any other dependency. The matching plugin binary is resolved automatically to go with it.

saying these in an interview costs you the question

  • Thinks pulumi up applies immediately with no preview or confirmation
  • Looks for a pulumi init command to download providers
  • Assumes pulumi preview refreshes state against the cloud first
  • Believes pulumi up applies a plan file saved by a previous preview

context