1. Docs
  2. Infrastructure as Code
  3. Guides
  4. Building & Extending
  5. Components
  6. Packaging Components

Packaging Components

    Once you’ve authored a Pulumi component, you will probably want to package and distribute it so others can use it. This guide covers three different approaches to packaging components, each with different trade-offs for distribution, versioning, and discoverability.

    Choosing a packaging approach

    Pulumi offers three ways to package components:

    1. Single-language components: Share components as source code with native package managers. Single-language components are a good fit for smaller organizations where all Pulumi code is written in a single language.
    2. Cross-language components: Support multiple languages with auto-generated SDKs from a single implementation. Cross-language components are a good fit for platform teams who want to create reusable abstractions to consume internally or to provide self-service for application teams.
    3. Provider-based components: Build components as Pulumi providers with zero runtime dependencies for consumers. Provider-based components are a good fit for organizations with a high degree of Pulumi expertise who expect to distribute components to a large number of teams across many languages or organizations with specific runtime requirements.
    Most platform teams should consider cross-language components as their default option because they provide multi-language consumption with minimal overhead and also have enhanced support within Pulumi Cloud.

    Single-language components

    Single-language components are written and consumed in the same language and behave just like any other shareable class in your language of choice. You distribute single-language components by publishing packages in the language’s package manager (npm, PyPI, etc.). Because single-language components are limited to the language of authorship, they are a good fit for smaller teams or organizations that expect to only ever use Pulumi in a single language.

    Distribution and consumption

    Single-language components are distributed via the native package management tool for the language in which the component is authored: npm for TypeScript, PyPI for Python, Go modules, etc. They are consumed by adding a reference to the package (or importing from a local path on disk), just like any other module or package.

    Advantages and limitations

    When considering single-language components, bear the following tradeoffs in mind:

    • Lowest overhead: Components work like any other class in your language and do not require SDKs
    • No argument type limitations: Arguments to single-language components can use any type, including union types, as they do not need to be serializable
    • Single language only: Cannot be consumed from other Pulumi languages
    • No Pulumi IDP support: Cannot be published to Pulumi IDP Private Registry

    Cross-language components

    Cross-language components support consumption in any Pulumi language. You implement your components once in your preferred language and Pulumi automatically generates SDKs via local packages for other languages when the package is added to a downstream Pulumi program. Cross-language components are source-based Pulumi plugins.

    For detailed information on packaging and using cross-language components, see Cross-language Components.

    Cross-language components written in Go can also include custom resources and functions alongside components, since Go uses a single library — pulumi-go-provider — for authoring both component and custom resources.

    A common usage pattern for cross-language components is for a platform team to author components in a general purpose language (like TypeScript), publish those components in Pulumi Cloud IDP for discoverability and auto-generated documentation, and then allow application teams to consume those components so they can compose the infrastructure for their applications in the language of their choosing (including YAML).

    This pattern gives a high degree of self-service to application teams so that they are able to consume infrastructure patterns without needing to know the details about how to create and connect every individual resource. Applications teams are still free to add additional resources to their Pulumi code in cases where the platform team does not have a suitable component published.

    Distribution and consumption

    Because cross-language components are distributed as source, they do not need to be explicitly published. Consumers use the pulumi package add command to install the package, specifying a git URL and revision (or a local path for monorepos or local testing), and Pulumi automatically generates the appropriate language SDK as a local package.

    Cross-language components also support pre-published SDKs. Authors can generate and publish SDKs to native package managers (npm, PyPI, etc.) as part of a CI/CD process, allowing consumers to install via their language’s package manager without generating the SDK themselves.

    Pulumi Cloud customers can explicitly publish versions of components to Pulumi IDP Private Registry using the pulumi package publish command. Publishing a package to Pulumi Cloud gives platform teams the ability to provide self-service to application teams by sharing components in a browsable gallery complete with READMEs and auto-generated SDK documentation in all Pulumi languages.

    Advantages and limitations

    When considering cross-language components, bear the following tradeoffs in mind:

    • Write and consume in any language: Cross-language components can be written in and consumed by any supported Pulumi language
    • Flexible distribution: Cross-language components can be distributed as source via git references (consumed with pulumi package add) or as pre-built SDKs published to native package managers
    • Pulumi IDP support: Can be published to Pulumi Cloud for discoverability and automatic API documentation
    • SDK regeneration overhead: If SDKs are not pre-published, consumers must regenerate them when the component is updated
    • Runtime dependencies: Consumers must have the runtime installed for the language in which the component is written (Node.js, Python, etc.)
    • Argument type limitations: Because calls to cross-language components are serialized, there are some limitations on the types that can be included in the component’s resources. For more information, see Component arguments and type requirements

    Provider-based components

    Provider-based components are built as full Pulumi providers and are distributed as executable-based Pulumi plugins. They compile to native binaries with no runtime dependencies and can include both component resources and custom resources. Because provider-based components have a higher overhead for CI/CD and publishing, and in most cases are written in Go, they are a fit for very large organizations that have many teams using many different languages, or organizations where the runtime requirements for cross-language components are not suitable.

    If you want to create a component for public consumption and publish it in the Pulumi Registry, you should create a provider-based component, author it in Go, and publish SDKs in the public feeds for each language (npmjs.org, etc.).

    For more information on submitting packages to the Pulumi Registry, see Publishing Pulumi packages.

    Distribution and consumption

    Provider-based components are typically distributed as pre-built SDKs published to multiple package managers (one for each language a consumer would use). The provider binary must also be published in an accessible location. Pulumi Cloud customers can also publish provider-based components to Pulumi IDP Private Registry using pulumi package publish.

    Consumers install the published SDKs (like any other Pulumi provider package). Pulumi will automatically download (and cache) the provider binary when the consuming Pulumi program is first run (just like providers).

    For more information on authoring providers, see Build a provider.

    Advantages and limitations

    When considering provider-based components, bear the following tradeoffs in mind:

    • No runtime dependencies for consumers: Consumers don’t need Node.js, Python, or any other runtime installed because the provider plugin is distributed as a native binary
    • Full provider capabilities: In addition to components, provider packages can include custom resources and functions. Note that cross-language components written in Go can also mix in custom resources and functions, since Go has a single library for authoring both component and custom resources.
    • Go strongly recommended: Providers are usually written in Go because the Pulumi supported tooling makes maintaining the provider schema significantly easier compared to other languages (see note for details)
    • CI/CD Overhead: Provider-based components typically have pre-published SDKs, which requires a more involved CI/CD process (including publishing to a package feed after a release)
    • Argument type limitations: Because calls to provider-based components are serialized, there are some limitations on the types that can be included in the component’s resources. For more information, see Component arguments and type requirements

    Providers (and provider-based components) may technically be written in any language if the author is willing to hand-author the schema. Maintaining schemas by hand is labor-intensive and the learning curve maintaining schemas may be prohibitive for many teams. Pulumi provides tooling in the pulumi-go-provider framework to automatically infer the schema and reduce the need for manually maintained schema files. Pulumi does not provide tooling for inferring schemas in languages other than Go.

    Consuming providers written in languages other than Go requires consumers to have the runtime of the provider’s language installed, similar to Cross-language components.

    Component packaging summary

    The table below summarizes the trade-offs between the three packaging approaches:

    FeatureSingle-language componentsCross-language componentsProvider-based components
    Best forSmaller organizations using a single languagePlatform teams providing reusable abstractions and self-serviceOrganizations distributing to many teams/languages or with specific runtime requirements, or components designed for public consumption
    Cross-language consumptionNo - limited to original languageYes - consume in any Pulumi languageYes - consume in any Pulumi language
    Pulumi Cloud IDP Private Registry supportNoYesYes
    Packaging complexityMinimal - publish a native packageLow - requires PulumiPlugin.yaml and entry pointsHigh - requires schema authoring, SDK generation, and binary publishing
    DistributionSource published to package managers (npm, PyPI, etc.)Git reference (via pulumi package add) or pre-built SDKs published to native package managersPre-built SDKs published to native package managers
    Runtime dependenciesn/aLanguage runtime for authoring language requiredNone - distributed as native binary