Resource option: transforms
The transforms
resource option provides a list of transforms to apply to a resource and all of its children. This option is used to override or modify the inputs to the child resources of a component resource. One example is to use the option to add other resource options (such as ignoreChanges
or protect
). Another example is to modify an input property (such as adding to tags or changing a property that is not directly configurable).
Each transform is a callback that gets invoked by the Pulumi engine. It receives the resource type, name, input properties, and resource options. The callback returns a new set of resource input properties and resource options that will be used to construct the resource instead of the original values.
VPC example
This example looks for all VPC and Subnet resources inside of a component’s child hierarchy and adds an option to ignore any changes for tags properties (perhaps because we manage all VPC and Subnet tags outside of Pulumi):
const vpc = new MyVpcComponent("vpc", {}, {
transforms: [args => {
if (args.type === "aws:ec2/vpc:Vpc" || args.type === "aws:ec2/subnet:Subnet") {
return {
props: args.props,
opts: pulumi.mergeOptions(args.opts, { ignoreChanges: ["tags"] })
}
}
return undefined;
}],
});
const vpc = new MyVpcComponent("vpc", {}, {
transforms: [args => {
if (args.type === "aws:ec2/vpc:Vpc" || args.type === "aws:ec2/subnet:Subnet") {
return {
props: args.props,
opts: pulumi.mergeOptions(args.opts, { ignoreChanges: ["tags"] })
}
}
return undefined;
}],
});
def transform(args: ResourceTransformArgs):
if args.type_ == "aws:ec2/vpc:Vpc" or args.type_ == "aws:ec2/subnet:Subnet":
return ResourceTransformResult(
props=args.props,
opts=ResourceOptions.merge(args.opts, ResourceOptions(
ignore_changes=["tags"],
)))
vpc = MyVpcComponent("vpc", opts=ResourceOptions(transforms=[transform]))
transform := func(_ context.Context, args *pulumi.ResourceTransformArgs) *pulumi.ResourceTransformResult {
if args.Type == "aws:ec2/vpc:Vpc" || args.Type == "aws:ec2/subnet:Subnet" {
args.Opts.IgnoreChanges = append(args.Opts.IgnoreChanges, "tags")
return &pulumi.ResourceTransformResult{
Props: args.Props,
Opts: args.Opts,
}
}
return nil
}
vpc := MyVpcComponent("vpc", pulumi.Transforms([]pulumi.ResourceTransform{transform}))
var vpc = new MyVpcComponent("vpc", new ComponentResourceOptions
{
ResourceTransforms =
{
async (args, _) =>
{
if (args.Type == "aws:ec2/vpc:Vpc" ||
args.Type == "aws:ec2/subnet:Subnet")
{
var options = CustomResourceOptions.Merge(
(CustomResourceOptions) args.Options,
new CustomResourceOptions { IgnoreChanges = {"tags"} });
return new ResourcetransformResult(args.Args, options);
}
return null;
}
}
});
// Pulumi Java support for transforms is coming soon
# Pulumi YAML does not support transforms
Stack Transforms
Transforms can also be applied in bulk to many or all resources in a stack by using Stack transforms, which are applied to the root stack resource and as a result inherited by all other resources in the stack.
pulumi.runtime.registerResourceTransform(args => {
if (isTaggable(args.type)) {
args.props["tags"] = Object.assign(args.props["tags"], autoTags);
return { props: args.props, opts: args.opts };
}
};
pulumi.runtime.registerResourceTransform(args => {
if (isTaggable(args.type)) {
args.props["tags"] = Object.assign(args.props["tags"], autoTags);
return { props: args.props, opts: args.opts };
}
};
def transform(args: ResourceTransformArgs):
if args.type_ == "aws:ec2/vpc:Vpc" or args.type_ == "aws:ec2/subnet:Subnet":
return ResourceTransformResult(
props=args.props,
opts=ResourceOptions.merge(args.opts, ResourceOptions(
ignore_changes=["tags"],
)))
pulumi.runtime.register_resource_transform(transform)
ctx.RegisterResourceTransform(
func(_ context.Context, args *pulumi.ResourceTransformArgs) *pulumi.ResourceTransformResult {
if args.Type == "aws:ec2/vpc:Vpc" || args.Type == "aws:ec2/subnet:Subnet" {
args.Opts.IgnoreChanges = append(args.Opts.IgnoreChanges, "tags")
return &pulumi.ResourceTransformResult{
Props: args.Props,
Opts: args.Opts,
}
}
return nil
},
)
public class MyStack : Stack
{
public MyStack() : base(new StackOptions { ResourceTransforms = { MyTransform } })
{
...
}
private static async Task<ResourceTransformResult?> MyTransform(ResourceTransformArgs args, CancellationToken ct)
{
if (args.Type == "aws:ec2/vpc:Vpc" ||
args.Type == "aws:ec2/subnet:Subnet")
{
var options = CustomResourceOptions.Merge(
(CustomResourceOptions) args.Options,
new CustomResourceOptions { IgnoreChanges = {"tags"} });
return new ResourcetransformResult(args.Args, options);
}
return null;
}
}
// Pulumi Java support for transforms is coming soon
# Pulumi YAML does not support transforms
Transforms vs. Transformations
Transforms are a replacement for Transformations. Transformations will be deprecated in the future in favor of Transforms.
Transforms offer support for the following capabilities that are not supported by Transformations:
Support for transforming child resources of packaged components, such as components in awsx and eks.
Support for async transform functions. In Node.js and Python, transform functions can optionally be
async
and return aPromise
/Awaitable
so you can useawait
for async calls in the transform. In .NET, transform functions take aCancellationToken
as an argument and return aTask
so you can useawait
for async calls in the transform. In Go, transform functions take acontext.Context
as an argument, allowing access to the async context for tracing/logging/cancellation.
While the Transforms APIs are similar to Transformations, there are some differences in both API signatures and runtime behavior. These are discussed in detail in Migrating from Transformations to Transforms.
Transforms for Packaged Component Resources
Note that a transform will be called twice for packaged component resources (such as those in awsx and eks). The transform will be called before the component resource is constructed, providing an opportunity to modify inputs and resource options before being passed to the provider that implements the component, and then again when the component resource is actually created in the provider, providing an opportunity to modify any resource options that were configured inside the implemenation of the component.
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.