Dynamic Providers
Relevant source files
The following files were used as context for generating this wiki page:
- sdk/python/lib/pulumi/dynamic/dynamic.py
- sdk/nodejs/dynamic/index.ts
- tests/integration/dynamic/python/simple/Pulumi.yaml
- sdk/nodejs/tests/cmd/dynamic/config.spec.ts
- tests/integration/nodejs/codepaths-workspaces-tsc/packages/my-dynamic-provider/package.json
- sdk/nodejs/tests/runtime/langhost/run.spec.ts
Dynamic Providers are a feature within Pulumi that allows developers to create simple, custom resources directly within their Pulumi application using their chosen language (like Python or TypeScript). This mechanism is ideal for managing resources from providers that don't have a dedicated Pulumi provider, or for encapsulating custom logic that needs to participate in the Pulumi resource lifecycle. Unlike full-fledged providers, dynamic providers do not require creating a separate gRPC server and plugin binary.
The core of a dynamic provider is a user-defined class that implements a specific Provider interface. This interface defines methods that correspond to the standard resource lifecycle events: create, read, update, and delete. A DynamicResource class is then used to instantiate a resource managed by this custom provider logic.
Core Concepts
The Dynamic Provider system is built around two main components: the Provider logic and the Resource that consumes it.
The Provider Interface
Users must implement a Provider interface (or ResourceProvider class in Python) that contains the logic for managing the resource's lifecycle. The Pulumi engine calls these methods at different points during a deployment.
The following diagram illustrates the structure of the provider interface.
This diagram shows the methods that must be implemented by a user-defined dynamic provider class.
Sources: sdk/nodejs/dynamic/index.ts:24-106, sdk/python/lib/pulumi/dynamic/dynamic.py:20-137
The key methods of the provider interface are summarized below:
| Method | Parameters | Description |
|---|---|---|
check | olds, news | Validates the inputs for a resource before any other operations. It can be used to provide default values. |
diff | id, olds, news | Compares the old and new state of a resource to determine if an update or replacement is necessary. |
create | inputs | Creates a new resource and returns its initial state and a unique ID. |
read | id, props | Reads the current state of an existing resource from the target environment. |
update | id, olds, news | Updates an existing resource in-place. |
delete | id, props | Deletes a resource. |
Sources: sdk/nodejs/dynamic/index.ts:24-106, sdk/python/lib/pulumi/dynamic/dynamic.py:20-137
The Dynamic Resource
To use a dynamic provider, you instantiate the Resource class, passing it an instance of your provider implementation, a name, and the input properties.
// Example of instantiating a dynamic resource in TypeScript
new Resource(provider, name, props, opts)
Sources: sdk/nodejs/dynamic/index.ts:119-129
The Resource class is a subclass of pulumi.CustomResource and handles the communication with the Pulumi engine, invoking the appropriate methods on your provider instance.
Resource Lifecycle Flow
The Pulumi engine orchestrates the calls to the dynamic provider's methods during a pulumi up operation. The typical flow for creating a new resource is as follows:
This diagram shows the sequence of calls from the Pulumi engine to a dynamic provider to create a new resource.
Sources: sdk/nodejs/dynamic/index.ts:24-106, sdk/python/lib/pulumi/dynamic/dynamic.py:20-137
Language-Specific Implementations
The implementation details for dynamic providers vary slightly between languages.
Node.js / TypeScript
In Node.js, you define a class that implements the pulumi.dynamic.Provider interface.
Key Components:
ProviderInterface: Defines the contract forcheck,diff,create,read,update, anddelete.ResourceClass: ACustomResourcethat takes aProviderinstance in its constructor.
The following class diagram shows the relationship between these components in the Node.js SDK.
This diagram shows the main classes for Node.js Dynamic Providers.
Sources: sdk/nodejs/dynamic/index.ts:23,119
A dynamic provider can also access the project's configuration using the Config class. This is useful for parameterizing the provider's behavior.
// From sdk/nodejs/tests/cmd/dynamic/config.spec.ts
const config = new Config({ "my-project:a": "A", "namespace:b": "B" }, "my-project");
assert.strictEqual(config.get("a"), "A");
Sources: sdk/nodejs/tests/cmd/dynamic/config.spec.ts:16-18
A package.json for a project using a dynamic provider typically includes a dependency on @pulumi/pulumi.
{
"name": "my-dynamic-provider",
"main": "bin/index.js",
"version": "1.0.0",
"dependencies": {
"@pulumi/pulumi": "latest",
"path-exists": "4.0.0"
}
}
Sources: tests/integration/nodejs/codepaths-workspaces-tsc/packages/my-dynamic-provider/package.json:1-9
Python
In Python, you create a class that inherits from pulumi.dynamic.ResourceProvider.
Key Components:
ResourceProviderClass: A base class that your provider implementation inherits from. It defines the same lifecycle methods as the Node.js interface (check,diff,create, etc.).ResourceClass: The custom resource class used to instantiate your dynamic resource.
The following class diagram shows the structure of the Python implementation.
This diagram shows the main classes for Python Dynamic Providers.
Sources: sdk/python/lib/pulumi/dynamic/dynamic.py:19,140
A Pulumi.yaml file for a Python project using a dynamic provider specifies the Python runtime.
name: simple-python-dynamic-provider
runtime: python
description: A simple dynamic provider example in Python
Sources: tests/integration/dynamic/python/simple/Pulumi.yaml:1-3
Summary
Dynamic Providers offer a powerful, in-process way to extend Pulumi's capabilities for one-off components or for integrating with systems that lack a formal Pulumi provider. By implementing a simple Provider interface, developers can leverage the full power of the Pulumi engine's resource lifecycle management, including previews, diffs, and state tracking, for their custom logic. This feature provides a balance between the simplicity of writing application code and the robustness of a full provider, making it a valuable tool for many infrastructure-as-code scenarios.