Component resources
A component is a logical grouping of Pulumi resources that is exposed as a single Pulumi resource. Components encapsulate related resources and their configuration, letting consumers create complex infrastructure through a simple, well-defined interface—without needing to know the implementation details.
A component can live anywhere code lives: defined inline in a Pulumi program, shared through a language-ecosystem library, or distributed as part of a Pulumi package whose plugin lets it be consumed from any Pulumi language. Pulumi packages are the most common distribution format for components intended for broad reuse, and a single package can contain multiple components alongside custom resources and functions — but a component is not required to be part of a package.
For example, the AWSx package contains many components, including:
awsx.ec2.Vpc— creates a complete VPC with subnets, route tables, and gateways preconfigured to AWS best practices.awsx.ecs.FargateService— creates an ECS service with a load balancer and all required networking.awsx.ecr.Repository— creates an ECR repository with image scanning and lifecycle policies.
Your organization might publish a package that contains components like:
- An
AcmeCorpVirtualMachinecomponent that enforces your company’s tagging requirements on every VM it creates. - A
SecureS3Bucketcomponent that bakes in encryption, versioning, and access logging so consumers get a compliant bucket by default.
Platform teams can use components to codify infrastructure best practices, security policies, and compliance requirements as reusable building blocks. When published to the Pulumi IDP Private Registry, packages containing components become discoverable across the organization and can be consumed by any team without needing to understand the underlying implementation.
Consuming components
How you consume a component depends on how it has been distributed.
Local components
Components that live in the same repository as your Pulumi program are consumed by importing or referencing the class using your language’s standard import mechanism—no additional installation steps are needed.
Native language packages
Some components are distributed as native language packages—standard packages published to a language registry (npm, PyPI, NuGet, Maven, etc.) without a Pulumi plugin. Because they have no cross-language support, they can only be consumed in the language in which they were authored. Add them to your project via your native package manager:
npm install @my-org/my-component
pip install my-org-my-component
go get github.com/my-org/my-component
dotnet add package MyOrg.MyComponent
<dependency>
<groupId>com.my-org</groupId>
<artifactId>my-component</artifactId>
<version>1.0.0</version>
</dependency>
Pulumi packages without pre-published SDKs
Components distributed as Pulumi packages can be consumed in any language using the pulumi package add command. Pulumi generates an SDK for your language on-the-fly and wires it into your project:
pulumi package add github.com/my-org/my-component@v1.0.0
This pattern is common for components your organization publishes for internal consumption via a Git repository or the Pulumi IDP Private Registry. It is also how components from a source-based plugin package are consumed across languages — the SDK is generated in your program’s language regardless of the language the component was authored in.
Under the hood, Pulumi fetches the package source (e.g. from GitHub), generates a local package SDK from the component’s schema, and makes the generated SDK available for import in your program.
You can also point pulumi package add at a local directory instead of a Git URL — useful for monorepos, rapid iteration, or components that don’t need to be published:
pulumi package add /path/to/local/secure-s3-component
Pulumi identifies the folder as a component package, generates a local SDK, and makes it available in your program — even if your program is written in a different language from the component.
Pulumi packages with pre-published SDKs
Some Pulumi packages have pre-generated SDKs published for each language, allowing consumers to install them directly via their native package manager without running pulumi package add. Because the SDK is already compiled and published, no additional runtime is required beyond your own language toolchain. Pulumi-published packages such as AWSx are distributed this way. Your organization may also opt to publish SDKs for each language.
Add the package to your project using your native package manager:
npm install @pulumi/awsx
pip install pulumi-awsx
go get github.com/pulumi/pulumi-awsx/sdk/v3/go/awsx
dotnet add package Pulumi.Awsx
<dependency>
<groupId>com.pulumi</groupId>
<artifactId>awsx</artifactId>
<version>3.0.0</version>
</dependency>
Example: consuming a component
The awsx.ec2.Vpc component from the AWSx package is a good example of a component resource. It creates a complete VPC—including subnets, route tables, and internet gateways—behind a simple interface.
After adding the AWSx package (see above), instantiate the component like any other Pulumi resource, passing arguments and resource options:
import * as awsx from "@pulumi/awsx";
const vpc = new awsx.ec2.Vpc("vpc", {
subnetSpecs: [
{ type: awsx.ec2.SubnetType.Public, cidrMask: 22 },
{ type: awsx.ec2.SubnetType.Private, cidrMask: 20 },
],
}, { protect: true });
export const vpcId = vpc.vpcId;
export const privateSubnetIds = vpc.privateSubnetIds;
export const publicSubnetIds = vpc.publicSubnetIds;
import pulumi
import pulumi_awsx as awsx
vpc = awsx.ec2.Vpc("vpc",
awsx.ec2.VpcArgs(
subnet_specs=[
awsx.ec2.SubnetSpecArgs(type=awsx.ec2.SubnetType.PUBLIC, cidr_mask=22),
awsx.ec2.SubnetSpecArgs(type=awsx.ec2.SubnetType.PRIVATE, cidr_mask=20),
],
),
opts=pulumi.ResourceOptions(protect=True),
)
pulumi.export("vpcId", vpc.vpc_id)
pulumi.export("privateSubnetIds", vpc.private_subnet_ids)
pulumi.export("publicSubnetIds", vpc.public_subnet_ids)
package main
import (
"github.com/pulumi/pulumi-awsx/sdk/v3/go/awsx/ec2"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
vpc, err := ec2.NewVpc(ctx, "vpc", &ec2.VpcArgs{
SubnetSpecs: []ec2.SubnetSpecArgs{
{Type: ec2.SubnetTypePublic, CidrMask: pulumi.IntRef(22)},
{Type: ec2.SubnetTypePrivate, CidrMask: pulumi.IntRef(20)},
},
}, pulumi.Protect(true))
if err != nil {
return err
}
ctx.Export("vpcId", vpc.VpcId)
ctx.Export("privateSubnetIds", vpc.PrivateSubnetIds)
ctx.Export("publicSubnetIds", vpc.PublicSubnetIds)
return nil
})
}
using Pulumi;
using System.Collections.Generic;
using Pulumi.Awsx.Ec2.Inputs;
using Ec2 = Pulumi.Awsx.Ec2;
return await Deployment.RunAsync(() =>
{
var vpc = new Ec2.Vpc("vpc", new()
{
SubnetSpecs =
{
new SubnetSpecArgs { Type = Ec2.SubnetType.Public, CidrMask = 22 },
new SubnetSpecArgs { Type = Ec2.SubnetType.Private, CidrMask = 20 },
},
}, new ComponentResourceOptions { Protect = true });
return new Dictionary<string, object?>
{
["vpcId"] = vpc.VpcId,
["privateSubnetIds"] = vpc.PrivateSubnetIds,
["publicSubnetIds"] = vpc.PublicSubnetIds,
};
});
package myproject;
import java.util.Arrays;
import com.pulumi.Pulumi;
import com.pulumi.awsx.ec2.Vpc;
import com.pulumi.awsx.ec2.VpcArgs;
import com.pulumi.awsx.ec2.enums.SubnetType;
import com.pulumi.awsx.ec2.inputs.SubnetSpecArgs;
import com.pulumi.resources.ComponentResourceOptions;
public class App {
public static void main(String[] args) {
Pulumi.run(ctx -> {
var vpc = new Vpc("vpc",
VpcArgs.builder()
.subnetSpecs(Arrays.asList(
SubnetSpecArgs.builder().type(SubnetType.Public).cidrMask(22).build(),
SubnetSpecArgs.builder().type(SubnetType.Private).cidrMask(20).build()
))
.build(),
ComponentResourceOptions.builder().protect(true).build());
ctx.export("vpcId", vpc.vpcId());
ctx.export("privateSubnetIds", vpc.privateSubnetIds());
ctx.export("publicSubnetIds", vpc.publicSubnetIds());
});
}
}
Notice that components are instantiated exactly like any other Pulumi resource: with a name, arguments defined by the component author, and resource options like protect.
Components in pulumi up output
When you run pulumi up, components appear as a resource tree in the CLI output. Child resources are displayed nested under their parent component, giving you visibility into everything the component created on your behalf:
Updating (dev):
Type Name Status
+ pulumi:pulumi:Stack my-stack created
+ └─ awsx:ec2:Vpc vpc created
+ └─ aws:ec2:Vpc vpc created
+ ├─ aws:ec2:Subnet vpc-private-1 created
+ │ └─ aws:ec2:RouteTable vpc-private-1 created
+ │ ├─ aws:ec2:Route vpc-private-1 created
+ │ └─ aws:ec2:RouteTableAssociation vpc-private-1 created
+ └─ aws:ec2:Subnet vpc-public-1 created
+ └─ aws:ec2:RouteTable vpc-public-1 created
+ ├─ aws:ec2:Route vpc-public-1 created
+ └─ aws:ec2:RouteTableAssociation vpc-public-1 created
This tree makes it clear that a single awsx:ec2:Vpc component encapsulates multiple AWS resources—subnets, route tables, routes, and associations—that would otherwise need to be defined and managed individually.
Resource options and component resources
Resource options passed to a component resource do not always behave the same as they do for custom resources. For example, the provider option has no effect on a component—use providers instead to pass explicit provider configuration to a component’s child resources. For a complete list of which options apply to component resources, see Resource options and component resources.
Authoring components
You author a component by extending the ComponentResource class. The guides below walk through building, packaging, and testing components:
- Build a Component — define the class, structure arguments, create child resources, register outputs, and configure provider inheritance.
- Packaging Components — compare the three distribution options and package a component for sharing.
- Testing Components — write tests for component resources.
- Pulumi IDP Private Registry — publish and discover components within your organization.
Thank you for your feedback!
If you have a question about how to use Pulumi, reach out in Community Slack.
Open an issue on GitHub to report a problem or suggest an improvement.