Component Resources
Relevant source files
The following files were used as context for generating this wiki page:
- sdk/go/pulumi/resource.go
- sdk/nodejs/resource.ts
- sdk/python/lib/pulumi/resource.py
- tests/integration/construct_component/go/main.go
- pkg/codegen/dotnet/gen.go
- tests/testdata/codegen/other-owned/schema.json
- pkg/codegen/go/gen.go
- sdk/nodejs/cmd/pulumi-language-nodejs/testdata/published/tsnode/sdks/component-property-deps-1.33.7/component.ts
Component Resources are a fundamental concept in Pulumi for creating reusable and modular cloud infrastructure. They act as logical containers that group a set of related resources—which can be other Component Resources or Custom Resources—into a single, abstracted component. Unlike Custom Resources, Component Resources do not correspond to a physical resource managed by a cloud provider. Instead, they are a powerful abstraction mechanism within the Pulumi programming model.
The primary purpose of a Component Resource is to encapsulate the complexity of a set of resources, presenting a simplified interface to the consumer. This promotes code reuse, improves maintainability, and allows for the creation of higher-level abstractions that match the logical architecture of an application. By parenting other resources to a component, Pulumi understands the relationship and displays it correctly in the resource graph.
Core Concepts
Component Resources are defined by their logical role in the resource hierarchy rather than a direct mapping to a provider's API.
Logical vs. Physical Resources
- ComponentResource: A logical resource that exists only in the Pulumi object model. It is used to group other resources. It is registered with the Pulumi engine but does not have a corresponding provider for CRUD operations.
- CustomResource: Represents a physical resource in a cloud provider (e.g., an S3 bucket, a VM). It has a corresponding provider plugin that manages its lifecycle.
Class Hierarchy
Component Resources, like all other resource types in Pulumi, inherit from a base Resource class. This shared inheritance ensures they participate in the same resource graph and dependency tracking system.
The diagram below illustrates the basic inheritance structure for resource types in Pulumi.
This class hierarchy is consistent across the supported languages. For example, in Python, ComponentResource inherits from Resource, and in Go, ComponentResourceState embeds ResourceState.
Sources: sdk/python/lib/pulumi/resource.py:823-826, sdk/go/pulumi/resource.go:42-45, sdk/nodejs/resource.ts:1080-1083
Comparison: Component vs. Custom Resource
The following table summarizes the key differences between ComponentResource and CustomResource.
| Feature | ComponentResource | CustomResource |
|---|---|---|
| Nature | Logical grouping of resources | A single physical cloud resource |
Schema isComponent | true | false (or absent) |
| Provider Interaction | None directly; it orchestrates its child resources | Interacts with a resource provider for CRUD operations |
| State Management | No independent state in the Pulumi backend | Manages its own state in the Pulumi backend |
| Base Class (Node.js) | pulumi.ComponentResource | pulumi.CustomResource |
| Base Class (Python) | pulumi.ComponentResource | pulumi.CustomResource |
| Base Class (Go) | pulumi.ComponentResourceState | pulumi.CustomResourceState |
| Base Class (.NET) | Pulumi.ComponentResource | Pulumi.CustomResource |
Sources: sdk/nodejs/resource.ts:1080, 933, sdk/python/lib/pulumi/resource.py:823, 679, sdk/go/pulumi/resource.go:42, 37, pkg/codegen/dotnet/gen.go:1115-1126
Defining a Component Resource
A component resource is defined as a class that inherits from the pulumi.ComponentResource base class in the respective language SDK.
Constructor
The constructor of a component resource is where the magic happens. It is responsible for:
- Calling the base class constructor (
super()orbase()) to register the component itself with the Pulumi engine. - Instantiating its child resources, passing
this(orself) as theparentoption. This establishes the parent-child relationship in the resource graph. - Registering any outputs that should be exposed by the component.
The base constructor typically accepts four arguments:
type: A unique type token string for the component (e.g.,pkg:module:MyComponent).name: The logical name for this instance of the component.args: An object containing the input properties for the component.opts: A set of resource options, includingparent,provider,dependsOn, etc. For a component's child resources,{ parent: this }is crucial.
Sources: sdk/nodejs/resource.ts:1097-1104, sdk/python/lib/pulumi/resource.py:836-843, sdk/go/pulumi/resource.go:193-202
Registering Outputs
To expose properties from child resources as outputs of the component, the registerOutputs method is called at the end of the constructor. This method takes a dictionary of output names to their values (which are often Outputs from child resources).
Sources: sdk/go/pulumi/resource.go:205-208, sdk/python/lib/pulumi/resource.py:851-853, sdk/nodejs/resource.ts:1113-1115
Instantiation and Registration Flow
The following sequence diagram shows the process of instantiating a component resource and its children.
This flow demonstrates how a component first registers itself and then registers its children, establishing the hierarchy within the Pulumi engine.
Sources: sdk/go/pulumi/resource.go:193-208, sdk/nodejs/resource.ts:1106-1115
Schema and Codegen
Component Resources can be defined in a Pulumi schema file (schema.json), which allows Pulumi to generate strongly-typed SDKs in all supported languages.
Schema Definition
In the schema, a resource is marked as a component by setting the isComponent property to true.
Here is an example of a component resource definition within a schema.json file:
{
"resources": {
"example::OtherResource": {
"isComponent": true,
"properties": {
"foo": {
"$ref": "#/resources/example::Resource"
}
},
"inputProperties": {
"foo": {
"$ref": "#/resources/example::Resource"
}
},
"type": "object"
}
}
}
Sources: tests/testdata/codegen/other-owned/schema.json:84-101
Code Generation
The Pulumi code generation tools use the isComponent flag to determine which base class to use for the generated resource class.
- If
isComponentistrue, the generated class inherits fromComponentResource. - If
isComponentisfalseor absent, it inherits fromCustomResource.
This logic is present in the code generators for each language. For example, in the Go and .NET generators, there is a conditional check for this flag.
Sources: pkg/codegen/go/gen.go:1839-1842, pkg/codegen/dotnet/gen.go:1115-1126
Language-Specific Examples
Go
In Go, you embed the pulumi.ComponentResourceState in your struct and call pulumi.RegisterComponentResource within your constructor function.
// MyComponent is a new component resource.
type MyComponent struct {
pulumi.ComponentResourceState
ChildID pulumi.StringOutput `pulumi:"childId"`
}
// NewMyComponent creates a new component resource.
func NewMyComponent(ctx *pulumi.Context, name string, args *MyComponentArgs, opts ...pulumi.ResourceOption) (*MyComponent, error) {
var resource MyComponent
err := ctx.RegisterComponentResource("my:module:MyComponent", name, args, &resource, opts...)
if err != nil {
return nil, err
}
// Create a child resource
child, err := NewMyChildResource(ctx, name+"-child", &MyChildResourceArgs{
// ...
}, pulumi.Parent(&resource))
if err != nil {
return nil, err
}
// Register outputs
err = ctx.RegisterResourceOutputs(&resource, pulumi.Map{
"childId": child.ID(),
})
if err != nil {
return nil, err
}
return &resource, nil
}
Sources: tests/integration/construct_component/go/main.go:29-61, sdk/go/pulumi/resource.go:42-45, 193-208
Node.js
In TypeScript/JavaScript, you extend the pulumi.ComponentResource class.
import * as pulumi from "@pulumi/pulumi";
export interface MyComponentArgs {
// ... input properties
}
export class MyComponent extends pulumi.ComponentResource {
public readonly childId: pulumi.Output<string>;
constructor(name: string, args: MyComponentArgs, opts?: pulumi.ComponentResourceOptions) {
super("my:module:MyComponent", name, args, opts);
const child = new MyChildResource(`${name}-child`, {
// ...
}, { parent: this });
this.childId = child.id;
this.registerOutputs({
childId: this.childId,
});
}
}
Sources: sdk/nodejs/resource.ts:1080-1115, sdk/nodejs/cmd/pulumi-language-nodejs/testdata/published/tsnode/sdks/component-property-deps-1.33.7/component.ts:10-13, 40-58
Python
In Python, you inherit from pulumi.ComponentResource.
import pulumi
class MyComponent(pulumi.ComponentResource):
child_id: pulumi.Output[str]
def __init__(self, name: str, args: MyComponentArgs, opts: pulumi.ResourceOptions = None):
super().__init__('my:module:MyComponent', name, vars(args), opts)
child = MyChildResource(f'{name}-child',
args=...,
opts=pulumi.ResourceOptions(parent=self))
self.child_id = child.id
self.register_outputs({
'childId': self.child_id
})
Sources: sdk/python/lib/pulumi/resource.py:823-853
.NET
In .NET languages like C#, you inherit from Pulumi.ComponentResource.
using Pulumi;
public class MyComponentArgs : ResourceArgs
{
// ... input properties
}
public class MyComponent : ComponentResource
{
[Output("childId")]
public Output<string> ChildId { get; private set; }
public MyComponent(string name, MyComponentArgs args, ComponentResourceOptions? options = null)
: base("my:module:MyComponent", name, args, options, remote: true)
{
var child = new MyChildResource($"{name}-child", new MyChildResourceArgs
{
// ...
}, new CustomResourceOptions
{
Parent = this,
});
this.ChildId = child.Id;
this.RegisterOutputs(new Dictionary<string, object?>
{
{ "childId", this.ChildId }
});
}
}
Sources: pkg/codegen/dotnet/gen.go:1119-1122, pkg/codegen/testing/test/testdata/simple-plain-schema/dotnet/Component.cs:13-15, 33-36