2.7. Component Resources
Component resources let you group related Pulumi resources into a reusable Python class with the same interface as built-in providers.
Step 1 — Clean up previous stacks
Destroy any running resources and make sure dev is the active stack:
If a prod stack still exists, remove it too:
Confirm the state is empty:
Explanation
This lab replaces __main__.py entirely. Starting with an empty dev state means pulumi up creates the
new component tree from scratch, making the preview output easier to read and avoiding any confusion from
resources left over from the previous labs.
Step 2 — Extract a WebServer component
Create a new file web_server.py in the project directory:
Explanation
pulumi.ComponentResource is the base class for all component resources. The constructor signature follows
the same three-part pattern as built-in resources: logical name, inputs, and options.
The super().__init__() call registers the component in the Pulumi graph under the type
workshop:index:WebServer — a namespaced type string in the form <package>:<module>:<class>.
pulumi.ResourceOptions(parent=self) wires every child resource to the component as its logical owner.
This has two effects:
- The children appear nested under the component in
pulumi previewandpulumi upoutput - Destroying the component destroys all its children
self.register_outputs() declares the component’s public outputs. These appear on the component node
in the preview tree and can be accessed by callers.
Step 3 — Deploy two instances
Replace __main__.py with the following:
Run a preview to see the component tree, then deploy:
Verify both servers are running:
Explanation
The calling code in __main__.py is now concise — two lines declare two fully independent nginx servers.
The preview output shows the component hierarchy:
Each WebServer instance has its own isolated state. Changing port on green triggers a replace only
for green-container — blue is not touched.
The same pattern scales to Azure
In the Azure labs, component resources are the natural abstraction for grouping related cloud resources —
a KubernetesCluster component might bundle the ManagedCluster, the node pool, and the RoleAssignment
into a single reusable unit. Same pattern, different provider.
Step 4 — Add an input with a default
Extend WebServer to accept an optional labels map and pass it to the container:
Pass labels from __main__.py:
Apply:
Explanation
Optional inputs follow the standard Python pattern — None default, guard with or {} before use. The
component interface stays clean: callers that do not need labels simply omit the argument.
This is the core value proposition of component resources: a stable, typed interface with sensible defaults that hides provider-specific boilerplate. Teams can publish components as Python packages and consume them the same way as any Pulumi provider.