Programming Model: Inputs and Outputs
Relevant source files
The following files were used as context for generating this wiki page:
The Input and Output programming model is a core concept in Pulumi that facilitates the handling of asynchronous values and the management of dependencies between resources. When infrastructure is provisioned, some values, like a database's connection string or a server's IP address, are not known until the resource is actually created. Pulumi represents these eventual values as Outputs. To maintain a declarative model, resources accept these eventual values as Inputs, allowing Pulumi to automatically track dependencies and provision resources in the correct order.
This model is implemented across all Pulumi language SDKs, providing a consistent way to compose resources regardless of the language used. The core idea is that an Output from one resource can be passed as an Input to another. The Pulumi engine understands this relationship and waits for the output value to be resolved before creating the dependent resource. This document details the architecture and language-specific implementations of this model.
Core Concepts
The Input/Output model revolves around two primary types: Input<T> and Output<T>.
Output<T>: Represents a value of typeTthat is not yet known. It is a "read-only" container for a value that will be available in the future.Outputs are typically properties of resources that are created during apulumi upoperation.Input<T>: Represents a value of typeTthat can be provided to a resource argument. It is a "write-only" type that can accept a concrete value (T), a promise of a value (Promise<T>), or anotherOutput<T>. This flexibility is what allows for the composition of resources.
The following diagram illustrates the fundamental data flow where the output of one resource is used as the input for another, establishing an implicit dependency.
This diagram shows how an Output from Resource A is fed into an Input for Resource B, ensuring Resource B is created only after Resource A's output value is available.
Sources: sdk/nodejs/output.ts, sdk/python/lib/pulumi/output.py, sdk/go/pulumi/types.go
Language-Specific Implementations
While the core concepts are universal, the implementation details of the Input/Output model vary slightly across the different language SDKs to align with idiomatic language practices.
Go
The Go SDK provides both a standard, concrete implementation and a newer, generic implementation for Inputs and Outputs.
Standard SDK (pulumi package)
The standard Go SDK defines a series of interfaces and structs for each primitive type. For example, for the string type, it provides StringInput, StringOutput, StringArrayInput, StringArrayOutput, StringMapInput, and StringMapOutput.
An Input interface, like StringInput, includes a method ToStringOutput() which converts the input into its corresponding Output type.
// sdk/go/pulumi/types.go:39-44
type StringInput interface {
Input
ToStringOutput() StringOutput
ToStringOutputWithContext(context.Context) StringOutput
}
The Output structs, like StringOutput, contain an embedded *pulumi.OutputState and provide methods for further transformations.
Sources: sdk/go/pulumi/types.go:39-44
Generic SDK (pulumix package)
The pulumix package introduces generic Input[T] and Output[T] types, simplifying the model. The key function for working with these generic outputs is ApplyN, which allows transforming one or more Output values.
// sdk/go/pulumix/applyn.go:31-36
// ApplyN transforms a set of Pulumi Outputs into a new Output by applying a function.
// The applied function is executed asynchronously when all outputs have resolved.
func ApplyN[O any](https://github.com/Alonso-pixel/pulumi/blob/HEAD/ctx%20*pulumi.Context%2C%20os%20%5B%5Dpulumi.Output%2C%0A%09applier%20func(args%20%5B%5Dany) (O, error)) (pulumi.Output, error) {
ApplyN is the low-level primitive. More user-friendly wrappers like Apply (for one output) and Apply2 (for two outputs) are built on top of it. These functions are crucial for accessing and manipulating the underlying values of Outputs once they become available.
Sources: sdk/go/pulumix/applyn.go:31-36
Code Generation
The Go SDK's concrete Input/Output types are generated by the pulumi/codegen package. The generator creates a suite of types for each resource property, including ...Input, ...Output, ...PtrInput, ...ArrayInput, ...MapOutput, etc. This process is driven by the resource schema.
The genInputInterface and genOutputType functions in pkg/codegen/go/gen.go are responsible for creating these definitions. For example, genInputInterface generates the ...Input interface with its To...Output() methods.
This diagram shows the Go code generation process for Input/Output types from a resource schema.
The generator also creates registration functions (init()) to register these generated types with the Pulumi runtime, which is essential for serialization and deserialization.
Sources: pkg/codegen/go/gen.go:1015-1022,1102-1119
Node.js (TypeScript)
The Node.js SDK defines Input<T> as a type alias that can accept a concrete value, a Promise, or an Output.
// sdk/nodejs/output.ts:1109
export type Input<T> = T | Promise<T> | Output<T>;
The Output<T> is a class that encapsulates an eventual value. It provides the apply method to transform the value once it's available. This is the primary mechanism for working with the underlying data of an Output.
Key factory functions include:
pulumi.output(val): Lifts a value into anOutput.pulumi.all(inputs): Combines multipleInputs into a singleOutputthat resolves when all its constituent inputs have resolved.
The following diagram illustrates the composition of the Input<T> type in TypeScript.
Sources: sdk/nodejs/output.ts:1109
Python
The Python SDK's model is very similar to the Node.js implementation. Input[T] is a type hint defined as a Union of a concrete value, an Awaitable (Python's equivalent of a Promise), and an Output.
# sdk/python/lib/pulumi/output.py:730
Input = Union[T, Awaitable[T], 'Output[T]']
The Output class provides an apply method for transformations, analogous to the Node.js SDK. It also includes an __await__ method, making Output objects directly awaitable in asynchronous Python code.
Key factory functions are also similar:
pulumi.output(val): Lifts a value into anOutput.pulumi.all(*args): Combines multipleInputs.
The following class diagram shows the key components of the Output class in Python.
Summary of Operations
The primary operations for working with Outputs are apply (for transforming a single output) and all (for combining multiple outputs).
| Language | apply (Transform one Output) | all (Combine multiple Inputs) |
|---|---|---|
| Go (Standard) | output.ApplyT(func(v T) U) | pulumi.All(inputs...).ApplyT(...) |
| Go (Generic) | pulumix.Apply(output, func(v T) U) | pulumix.ApplyN(outputs, func(args []any) U) |
| Node.js (TS) | output.apply(v => u) | pulumi.all(inputs).apply(([v1, v2]) => u) |
| Python | output.apply(lambda v: u) | pulumi.all(inputs).apply(lambda args: u) |
Sources: sdk/go/pulumix/applyn.go, sdk/nodejs/output.ts, sdk/python/lib/pulumi/output.py
Conclusion
The Input and Output programming model is a cornerstone of Pulumi's architecture. It provides a robust, type-safe, and language-idiomatic way to handle the asynchronous nature of infrastructure provisioning. By abstracting away the complexities of dependency management and eventual value resolution, it allows developers to write clear, declarative code that defines their desired infrastructure state, while the Pulumi engine handles the imperative steps of realizing that state. Understanding this model is essential for effectively using Pulumi to build and manage complex cloud applications.