Pulumi Component Resources
Pulumi component resources let you wrap a graph of related cloud resources behind one typed Python class with its own logical name, parenting, and registered outputs — turning a sprawling resource list into a reusable abstraction. This page is part of Pulumi Patterns & Provider Management, and it explains how ComponentResource, register_outputs, and resource parenting fit together so you can publish higher-level building blocks like a VPC or an app service.
What breaks without components
A Pulumi program that defines every VPC, subnet, route table, and gateway inline grows into hundreds of lines where the relationships are implicit. Two teams that both need "a standard VPC" copy-paste the block and slowly diverge. There is no single name for the unit, so the preview is a flat list of forty resources rather than one meaningful node, and there is no typed contract describing what the unit takes in or hands back. A ComponentResource solves all three: it gives the group a name and a parent in the resource tree, a typed constructor signature, and an explicit set of registered outputs. The same discipline that keeps stacks structured per environment applies here — push variation into typed inputs, keep the structure fixed.
Prerequisites
- Python 3.9+ and
pulumi >= 3.0(pulumi version). - A provider SDK pinned in your lockfile, e.g.
pulumi-aws >= 6.0. - A configured state backend; component resources are recorded in state like any other resource.
- Familiarity with
pulumi.Outputresolution, since component outputs areOutputvalues.
How component resources work
Subclassing ComponentResource
A component is a class that calls super().__init__() with a fully-qualified type token (package:module:Type), a name, and resource options. That registration creates the parent node every child will attach to.
# components/network.py
# CLI: imported by __main__.py, then pulumi up
from dataclasses import dataclass
import pulumi
import pulumi_aws as aws
@dataclass
class VpcArgs:
cidr_block: str
az_count: int = 2
class VpcComponent(pulumi.ComponentResource):
def __init__(self, name: str, args: VpcArgs, opts: pulumi.ResourceOptions | None = None) -> None:
# The type token namespaces the component in state and the resource tree.
super().__init__("myorg:network:Vpc", name, None, opts)
Parenting child resources
Every child created inside the component must pass parent=self so Pulumi nests it under the component in the resource graph. Parenting drives preview grouping, deletion ordering, and aliasing.
# components/network.py (continued)
self.vpc = aws.ec2.Vpc(
f"{name}-vpc",
cidr_block=args.cidr_block,
enable_dns_hostnames=True,
# State implication: parent=self nests this VPC under the component node.
opts=pulumi.ResourceOptions(parent=self),
)
Registering outputs
register_outputs signals that the component is fully constructed and publishes its typed surface. Until it is called, the engine treats the component as incomplete.
# components/network.py (continued)
self.vpc_id = self.vpc.id
# Provider note: register_outputs closes the component; outputs become readable upstream.
self.register_outputs({"vpc_id": self.vpc.id})
Building and reusing components
Two concrete tasks build on this foundation. The first is authoring a real component end to end: Building a Reusable VPC Component in Pulumi (Python) walks through typed args, subnets spread across availability zones, and parenting every child correctly. The second is distribution: Packaging Pulumi Components for Reuse covers turning a component into an installable, versioned Python package you can publish to a private index and consume from many stacks.
Verification
Confirm the component nests correctly and exposes its outputs by inspecting the preview tree and the resolved outputs:
# CLI: the preview should show child resources nested under the component node
pulumi preview --stack dev
pulumi stack output vpc_id --stack dev
In tests, instantiate the component under pulumi.runtime.set_mocks and assert its registered outputs without touching a cloud API — the same mock-based unit pattern used across Testing Python Infrastructure Code.
# tests/test_vpc_component.py
# CLI: pytest tests/test_vpc_component.py -q
import pulumi
class _Mocks(pulumi.runtime.Mocks):
def new_resource(self, args: pulumi.runtime.MockResourceArgs):
return (args.name + "-id", args.inputs)
def call(self, args: pulumi.runtime.MockCallArgs):
return {}
pulumi.runtime.set_mocks(_Mocks())
# State implication: mocks intercept creation; no real VPC is provisioned.
Troubleshooting
Child resources appear at the stack root, not under the component — Cause: a child was created without parent=self. Fix: pass opts=pulumi.ResourceOptions(parent=self) to every resource constructed inside __init__.
register_outputs warnings or missing outputs — Cause: register_outputs was never called, or was called before the children were created. Fix: call it once at the end of __init__ with a dict of the outputs you want to expose.
Renaming the component forces replacement of every child — Cause: the component name is part of every child's URN. Fix: use pulumi.ResourceOptions(aliases=[...]) to preserve identity across a rename instead of letting the engine replace resources.
Related
- Building a Reusable VPC Component in Pulumi (Python) — author a full VPC component with typed args, multi-AZ subnets, and parenting.
- Packaging Pulumi Components for Reuse — turn a component into an installable, versioned Python package.
- Pulumi Patterns & Provider Management — the parent set of provider and architecture patterns these components support.