Skip to main content
One set of configuration files, run against several separate environments, each with its own state and its own settings. That’s the whole idea: the same main.tf or index.ts from the project structure tutorial, applied three times with three different answers to “how big” and “who pays.”

Terraform workspaces

A workspace is an isolated slot of state within the same backend. Every workspace shares the same configuration files.
Reference the current workspace inside your configuration with terraform.workspace. The data blocks and the biznetgio_neolite_keypair resource are exactly what the quickstart already declared in full - what’s different here is that product_id is no longer products[0]. It’s picked deliberately per workspace, using the real package names from the NEO Lite catalog:
Confirm "XS 1.1" and "MS 4.2" against your own account’s catalog output before relying on them - see Step 1 in the catalog overview. select_os still uses oss[0] here since OS lists are usually short and dominated by one obvious choice; apply the same name-filter pattern to it if that’s not true for your account.
Always check which workspace is selected before applying: terraform workspace show. Applying to the wrong workspace is the single most common way people accidentally touch production. Consider wrapping apply in a script or CI job that requires the workspace name as an explicit argument instead of trusting whatever is currently selected.
If dev, staging, and production ever need genuinely different resources (not just different values), workspaces stop being the right tool, since all workspaces share one configuration. At that point, move to a directory per environment (envs/dev, envs/staging, envs/production), each with its own backend configuration and tfvars, all calling the same shared module.

Pulumi stacks

A stack is Pulumi’s native equivalent, and it’s the primitive the quickstart already used without naming it.
Each stack gets its own Pulumi.<stack>.yaml config file and independent state. Set config per stack explicitly with --stack:
Read the current stack’s name inside your program with pulumi.getStack(). keypair and config are exactly what the Pulumi quickstart already declared in full - what’s different here is that productId is no longer products[0]. It’s picked deliberately per stack, using the real package names from the NEO Lite catalog:
Confirm "XS 1.1" and "MS 4.2" against your own account’s catalog output before relying on them - see Step 1 in the catalog overview. selectOs still uses oss[0] here since OS lists are usually short and dominated by one obvious choice; apply the same name-filter pattern to it if that’s not true for your account.
Same rule as Terraform: check pulumi stack ls (the current one is marked) before pulumi up. Pass --stack <name> explicitly in CI rather than relying on whichever stack was last selected on that runner.

A practical dev/staging/production split

A pattern that works well for these providers specifically: Read the billing guide for exactly what pay_with_credit_card = false does; it’s the cheapest way to rehearse a whole apply/up cycle before committing real money.

Next steps