From 7801194d1d88635acddd8e3110cc727f8eb32fd0 Mon Sep 17 00:00:00 2001 From: Justin Van Patten Date: Mon, 15 Jul 2024 09:59:32 -0700 Subject: [PATCH] Edits and examples --- .../index.md | 23 --- content/blog/resource-transforms/index.md | 164 ++++++++++++++++++ content/blog/resource-transforms/meta.png | Bin 0 -> 24665 bytes 3 files changed, 164 insertions(+), 23 deletions(-) delete mode 100644 content/blog/announcing-new-transform-callbacks/index.md create mode 100644 content/blog/resource-transforms/index.md create mode 100644 content/blog/resource-transforms/meta.png diff --git a/content/blog/announcing-new-transform-callbacks/index.md b/content/blog/announcing-new-transform-callbacks/index.md deleted file mode 100644 index 113cb68a01659..0000000000000 --- a/content/blog/announcing-new-transform-callbacks/index.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: "Announcing new transform callbacks" -date: 2024-05-15 -meta_desc: Transform callbacks are a rebuild of the transformations system to work for Multi-Language Components -meta_image: meta.png -authors: - - fraser-waters -tags: - - features ---- - -Pulumi has supported a [transformations](/docs/concepts/resources#transformations) system for a number of years now. This has proved to be a powerful and flexible escape hatch for editing resource properties across your entire program. - -However it was written before the advent of Multi-Language Components (MLCs) and functioned entirely on in-memory data structures, a sensible choice at the time but impossible to extend to work with the cross process nature of MLCs. - -We've now written a new system based on a new "callback" system which allows the Pulumi engine to call back into your user program to answer requests. - - - -This new system is intended to fully replace the old transformations system. We've written up some guides on the differences between the two systems in the new [docs](/docs/concepts/options/transforms/#transforms-vs-transformations). - -We'll continue supporting the current transformations system going forward, but given the expected growth in MLCs and the soon to be released Programs as Components feature users will want to prefer using the new system. Only the new system will work correctly with resources created in MLCs and Programs as Components. - diff --git a/content/blog/resource-transforms/index.md b/content/blog/resource-transforms/index.md new file mode 100644 index 0000000000000..3a0a0993805b4 --- /dev/null +++ b/content/blog/resource-transforms/index.md @@ -0,0 +1,164 @@ +--- +title: "Resource Transforms" +date: 2024-07-16 +meta_desc: Announcing a new Transform system with support for transforming child resources of packaged components. +meta_image: meta.png +authors: + - fraser-waters + - justin-vanpatten +tags: + - features +--- + +Pulumi has supported a [Transformations](/docs/concepts/options/transformations) system for a number of years now. This has proved to be a powerful and flexible escape hatch for modifying resource properties and options across your entire program. For example, you could use Transformations to [automatically apply tags](/blog/automatically-enforcing-aws-resource-tagging-policies/#automatically-applying-tags) to all taggable resources in your program, including the children of component resources. + +However, there is one major limitation with the existing Transformations system: it isn't able to transform the children of _packaged_ component resources, such as those in [awsx](/registry/packages/awsx) and [eks](/registry/packages/eks). This limitation is due to the fact that _packaged_ component resources are created in a separate provider process and Transformations only work with resources created in your program's process. + +To address this limitation we're introducing a new system called [Transforms](/docs/concepts/options/transforms), which works with all resources, including packaged component resources and their children. The new Transforms system is intended to fully replace the old Transformations system (we plan to deprecate the old system in the future). + + + +Here's an example that demonstrates adding tags to all `Subnet` child resources created by the `Vpc` packaged component: + +{{< chooser language "typescript,python,go,csharp" >}} + +{{% choosable language typescript %}} + +```typescript +import * as pulumi from "@pulumi/pulumi"; +import * as awsx from "@pulumi/awsx"; + +const myTags = { test: "TEST" }; + +pulumi.runtime.registerResourceTransform(args => { + if (args.type === "aws:ec2/subnet:Subnet") { + const tags = pulumi.output(args.props.tags || {}); + args.props.tags = tags.apply(t => ({ ...t, ...myTags })); + return { props: args.props, opts: args.opts }; + } + return undefined; +}); + +const vpc = new awsx.ec2.Vpc("test"); +``` + +{{% /choosable %}} +{{% choosable language python %}} + +```python +import pulumi +from pulumi_awsx import ec2 + +my_tags = { "test": "TEST" } + +def tag_transform(args): + if args.type_ == "aws:ec2/subnet:Subnet": + tags = pulumi.Output.from_input(args.props.get("tags", {})) + args.props["tags"] = tags.apply(lambda v: {**v, **my_tags}) + return pulumi.ResourceTransformResult(args.props, args.opts) + +pulumi.runtime.register_resource_transform(tag_transform) + +my_vpc = ec2.Vpc("my-vpc") +``` + +{{% /choosable %}} +{{% choosable language go %}} + +```go +package main + +import ( + "context" + + "github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/ec2" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +func main() { + pulumi.Run(func(ctx *pulumi.Context) error { + myTags := map[string]string{"test": "TEST"} + + err := ctx.RegisterResourceTransform(func(_ context.Context, args *pulumi.ResourceTransformArgs) *pulumi.ResourceTransformResult { + if args.Type == "aws:ec2/subnet:Subnet" { + tags, ok := args.Props["tags"].(pulumi.StringMapInput) + if !ok { + tags = pulumi.StringMap{} + } + args.Props["tags"] = tags.ToStringMapOutput().ApplyT(func(m map[string]string) map[string]string { + result := make(map[string]string) + for k, v := range m { + result[k] = v + } + for k, v := range myTags { + result[k] = v + } + return result + }) + return &pulumi.ResourceTransformResult{ + Props: args.Props, + Opts: args.Opts, + } + } + return nil + }) + if err != nil { + return err + } + + _, err = ec2.NewVpc(ctx, "test", nil) + if err != nil { + return err + } + + return nil + }) +} +``` + +{{% /choosable %}} +{{% choosable language csharp %}} + +```csharp +using Pulumi; +using Pulumi.Awsx.Ec2; +using System.Collections.Immutable; + +await Deployment.RunAsync(); + +class MyStack : Stack +{ + public MyStack() : base(new StackOptions + { + ResourceTransforms = + { + async (args, _) => { + var myTags = ImmutableDictionary.Create() + .Add("test", "Test"); + + if (args.Type == "aws:ec2/subnet:Subnet") + { + var tags = args.Args.TryGetValue("tags", out var tagsValue) && + tagsValue is ImmutableDictionary tagsDictionary + ? tagsDictionary + : ImmutableDictionary.Empty; + tags = tags.AddRange(myTags); + return new(args.Args.SetItem("tags", tags), args.Options); + } + return null; + }, + } + }) + { + var vpc = new Vpc("test"); + } +} +``` + +{{% /choosable %}} + +{{< /chooser >}} + +Using the new Transforms system is similar to the old system, but there are some differences between the two systems. A guide on [Migrating from Transformations to Transforms](/docs/concepts/options/transformations/#migrating-from-transformations-to-transforms) is available to help transition to the new system. + +We're excited to make it possible to transform all resources in your program, including those from packaged components. If you have any questions or feedback, please don't hesitate to reach out to us on [Slack](https://slack.pulumi.com/). diff --git a/content/blog/resource-transforms/meta.png b/content/blog/resource-transforms/meta.png new file mode 100644 index 0000000000000000000000000000000000000000..44a346913c3498ce31b90f943b1e2830cd0ca816 GIT binary patch literal 24665 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