my-assets-dev can’t become my-assets-prod without editing the source file — and editing source files per environment is exactly the kind of fragile, error-prone pattern that infrastructure-as-code exists to eliminate. input blocks solve this by turning any value into a configurable parameter with a type, an optional default, and a name you can pass at apply time. On the other side of the same problem, once Pulumi provisions a resource, you need to get its runtime attributes — the endpoint, the ARN, the URL — out of the stack and into the hands of operators, CI pipelines, and other stacks that depend on them. That’s what output blocks are for. Together, inputs and outputs are what turn a collection of resource declarations into a genuinely reusable, composable stack.
What you’ll learn
- How
inputblocks declare typed, configurable variables - How
outputblocks export stack values - Why inputs are
Resolved<T>— available at compile time, not pending - How to organise your project into separate
.iacfiles by block type
Why this matters
A stack with well-defined inputs and outputs becomes a reusable building block. The same
.iac files can deploy to dev, staging, and prod by passing different values at apply time — no branching, no duplication, no environment-specific source files.The source code
Splitting your stack into separate files by block type keeps each file focused and easy to navigate.inputs.iac holds your variables, main.iac holds your resources, outputs.iac holds your exports, and ubx.iac holds your project config.
How it works
Inputs are resolved before the stack runs
Inputs with defaults are
Resolved<T> — their value is known at compile time. Inputs without defaults are also Resolved<T>, their value passed before the stack runs via --var or Pulumi config. Neither type is Pending<T> — that’s reserved for resource outputs that only exist after Pulumi creates a resource.String interpolation is evaluated at compile time
"assets-${input.env}-${input.bucket_suffix}" resolves into a plain string expression. No .apply() chain is generated — both inputs are resolved values, so the compiler can inline them directly.What ubx generates
Common mistakes
Run it
What you learned
input blocks declare typed, configurable variables with optional defaultsInputs are
Resolved<T> — available at compile time, no pending chain requiredoutput blocks export stack values; sensitive = true encrypts them in stateSplitting blocks into
main.iac, inputs.iac, and outputs.iac keeps large stacks navigableNext steps
Locals
Compute reusable values from inputs and other expressions
Pending refs: RDS to Helm
The Pending<T> type system in depth
Full runnable example: github.com/ubiquex/ubx-examples/01-inputs-and-outputs

