--- title: Function Patch and Transform weight: 70 description: "A function that use patches and transforms to change inputs from composite resources before creating managed resources" --- Function Patch and Transform allows you to write a Composition that specifies managed resource (MR) templates, and uses "patch and transform" operations to fill them out. Crossplane fills the templates out with values copied from a composite resource (XR). A [patch](#create-a-patch) copies a value from one resource and _patches_ it onto another resource. A [transform](#transform-a-patch) modifies the values before applying the patch. {{}} All Compositions used Patch and Transform before Crossplane added support for composition functions. Function Patch and Transform works like legacy `mode: Resources` Compositions, which Crossplane deprecated in v1.17. The difference is that it uses a `mode: Pipeline` Composition and a function instead of a `mode: Resources` Composition. {{< /hint >}} Here's an example Composition that uses Function Patch and Transform. When you create an `AcmeBucket` XR that uses this Composition, Crossplane uses the template to create the Amazon S3 `Bucket` MR. Crossplane copies the value from the `AcmeBucket` XR's `spec.desiredRegion` field and patch it onto the `Bucket` managed resource's `spec.forProvider.region` field. ```yaml {label="intro"} apiVersion: apiextensions.crossplane.io/v1 kind: Composition metadata: name: example spec: compositeTypeRef: apiVersion: custom-api.example.org/v1alpha1 kind: AcmeBucket mode: Pipeline pipeline: - step: patch-and-transform functionRef: name: function-patch-and-transform input: apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: storage-bucket base: apiVersion: s3.aws.m.upbound.io/v1beta1 kind: Bucket spec: forProvider: region: "us-east-2" patches: - type: FromCompositeFieldPath fromFieldPath: spec.desiredRegion toFieldPath: spec.forProvider.region ``` {{}} Patch and transform is best for simpler compositions. It intentionally doesn't support features like loops and conditionals. {{}} ## Install the function You must install Function Patch and Transform before you can use it in a Composition. Apply this manifest to install Function Patch and Transform: ```yaml apiVersion: pkg.crossplane.io/v1 kind: Function metadata: name: function-patch-and-transform spec: package: xpkg.crossplane.io/crossplane-contrib/function-patch-and-transform:v0.8.2 ``` {{}} Read the [Composition page]({{}}) to learn more about Compositions and composition functions. {{< /hint >}} ## Resource templates The `resources` field the function's input defines the set of things that a composite resource creates when it uses this function. For example, the input can define a template to create a virtual machine and an associated storage bucket at the same time. {{}} Crossplane calls the resources a composite resource creates _composed resources_. {{< /hint >}} The `resources` field lists the individual resources with a `name`. This name identifies the resource inside the Composition. It isn't related to the external name used with the Provider. The contents of the `base` are identical to creating a standalone [managed resource]({{}}). This example uses [provider-upjet-aws](https://github.com/crossplane-contrib/provider-upjet-aws) to define a S3 storage `Bucket` and EC2 compute `Instance`. After defining the `apiVersion` and `kind`, define the `spec.forProvider` fields defining the settings of the resource. ```yaml {copy-lines="none",label="resources"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: storage-bucket base: apiVersion: s3.aws.m.upbound.io/v1beta1 kind: Bucket spec: forProvider: region: "us-east-2" - name: vm base: apiVersion: ec2.aws.m.upbound.io/v1beta1 kind: Instance spec: forProvider: ami: ami-0d9858aa3c6322f73 instanceType: t2.micro region: "us-east-2" ``` When a [composite resource]({{}}) uses this function, the composite resource creates two new managed resources with all the provided `spec.forProvider` settings. The `spec` supports any settings used in a managed resource, including applying `annotations` and `labels` or using a specific `providerConfigRef`. {{}} Use the `crossplane.io/external-name` annotation on the resource to set the resource's name in the external system (like AWS). {{< /hint >}} {{}} You can use Function Patch and Transform to template any kind of Kubernetes resource. {{< /hint >}} ## Create a patch Each entry in the `resources` list can include a list of patches. The `patches` field takes a list of patches to apply to the individual resource. Each patch has a `type`, which defines what kind of patch action Crossplane applies. Patches reference fields inside different resources depending on the patch type, but all patches reference a `fromFieldPath` and `toFieldPath`. The `fromFieldPath` is the path to the patch's input values. The `toFieldPath` is the path the patch applies to. Here's an example of a patch that copies a value from the composite resource's `spec.field1` field to the composed Bucket's labels. ```yaml {label="createComp",copy-lines="none"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: storage-bucket base: apiVersion: s3.aws.m.upbound.io/v1beta1 kind: Bucket spec: forProvider: region: "us-east-2" patches: - type: FromCompositeFieldPath fromFieldPath: spec.field1 toFieldPath: metadata.labels["patchLabel"] ``` ### Selecting fields Crossplane selects fields in a composite resource or managed resource with a subset of [JSONPath selectors](https://kubernetes.io/docs/reference/kubectl/jsonpath/), called "field paths." Field paths can select any field in a composite resource or managed resource object, including the `metadata`, `spec` or `status` fields. Field paths can be a string matching a field name or an array index, in brackets. Field names may use a `.` character to select child elements. #### Example field paths Here are some example selectors from a composite resource object. {{}} | Selector | Selected element | | --- | --- | | `kind` | `kind` | | `spec.desiredRegion` | `eu-north-1` | | `spec.resourceRefs[0].name` | `my-example-978mh-r6z64` | {{
}} ```yaml {label="select",copy-lines="none"} $ kubectl get composite -o yaml apiVersion: example.org/v1alpha1 kind: Example metadata: # Removed for brevity labels: crossplane.io/composite: my-example-978mh spec: desiredRegion: eu-north-1 field1: field1-text crossplane: resourceRefs: - apiVersion: s3.aws.m.upbound.io/v1beta1 kind: Bucket name: my-example-978mh-r6z64 - apiVersion: s3.aws.m.upbound.io/v1beta1 kind: Bucket name: my-example-978mh-cnlhj - apiVersion: s3.aws.m.upbound.io/v1beta1 kind: Bucket name: my-example-978mh-rv5nm ``` ## Reuse a patch You can reuse a patch object on multiple resources by using a PatchSet. To create a PatchSet, define a `patchSets` object in the function's input. Each patch inside a PatchSet has a `name` and a list of `patches`. Apply the PatchSet to a resource with a patch `type: PatchSet`. Set the `patchSetName` to the `name` of the PatchSet. ```yaml {label="patchset"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources patchSets: - name: my-patchset patches: - type: FromCompositeFieldPath fromFieldPath: spec.desiredRegion toFieldPath: spec.forProvider.region resources: - name: bucket1 base: # Removed for brevity patches: - type: PatchSet patchSetName: my-patchset - name: bucket2 base: # Removed for brevity patches: - type: PatchSet patchSetName: my-patchset ``` {{}} A PatchSet can't contain other PatchSets. Crossplane ignores any [transforms](#transform-a-patch) or [policies](#patch-policies) in a PatchSet. {{< /hint >}} ## Patching between resources Function Patch and Transform can't directly patch between two composed resources. For example, generating a network resource and patching the resource name to a compute resource. A resource can patch to a user-defined `status` field in the composite resource. Another resource can then read from that `Status` field to patch a field. First, define a custom `status` in the composite resource Definition and a custom field, for example `secondResource` ```yaml {label="xrdPatch",copy-lines="13-17"} kind: CompositeResourceDefinition # Removed for brevity. spec: # Removed for brevity. versions: - name: v1alpha1 schema: openAPIV3Schema: type: object properties: spec: # Removed for brevity. status: type: object properties: secondResource: type: string ``` Inside the function input the resource with the source data uses a `ToCompositeFieldPath` patch to write data to the `status.secondResource` field in the composite resource. The destination resource uses a `FromCompositeFieldPath` patch to read data from the composite resource `status.secondResource` field in the composite resource and write it to a label named `secondResource` in the managed resource. ```yaml {label="patchBetween",copy-lines="9-11"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: bucket1 base: apiVersion: s3.aws.m.upbound.io/v1beta1 kind: Bucket # Removed for brevity patches: - type: ToCompositeFieldPath fromFieldPath: metadata.name toFieldPath: status.secondResource - name: bucket2 base: apiVersion: s3.aws.m.upbound.io/v1beta1 kind: Bucket # Removed for brevity patches: - type: FromCompositeFieldPath fromFieldPath: status.secondResource toFieldPath: metadata.labels['secondResource'] ``` Describe the composite resource to view the `resources` and the `status.secondResource` value. ```yaml {label="descCompPatch",copy-lines="none"} $ kubectl describe composite Name: my-example-jp7rx Spec: # Removed for brevity Resource Refs: Name: my-example-jp7rx-gfg4m # Removed for brevity Name: my-example-jp7rx-fttpj Status: # Removed for brevity Second Resource: my-example-jp7rx-gfg4m ``` Describe the destination managed resource to see the label `secondResource`. ```yaml {label="bucketlabel",copy-lines="none"} $ kubectl describe bucket kubectl describe bucket my-example-jp7rx-fttpj Name: my-example-jp7rx-fttpj Labels: crossplane.io/composite=my-example-jp7rx secondResource=my-example-jp7rx-gfg4m ``` ## Patch with EnvironmentConfigs Crossplane uses EnvironmentConfigs to create in-memory data stores. Compositions can read and write from this data store as part of the patch process. EnvironmentConfigs can predefine data that Compositions can use or a composite resource can write data to their in-memory environment for other resources to read. {{< hint "note" >}} Read the [EnvironmentConfigs]({{}}) page for more information on using EnvironmentConfigs. {{< /hint >}} To apply a patch using EnvironmentConfigs, first define which EnvironmentConfigs to use with `environment.environmentConfigs`. Use either a [reference]({{}}) or a [selector]({{}}) to identify the EnvironmentConfigs to use. ```yaml {label="envselect",copy-lines="none"} apiVersion: apiextensions.crossplane.io/v1 kind: Composition # Removed for Brevity spec: environment: environmentConfigs: - ref: name: example-environment # Removed for Brevity ``` ### Patch a composite resource To patch between the composite resource and the in-memory environment use `patches` inside of the `environment`. Use the `ToCompositeFieldPath` to copy data from the in-memory environment to the composite resource. Use the `FromCompositeFieldPath` to copy data from the composite resource to the in-memory environment. ```yaml {label="xrpatch",copy-lines="none"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources environment: patches: - type: ToCompositeFieldPath fromFieldPath: tags toFieldPath: metadata.labels[envTag] - type: FromCompositeFieldPath fromFieldPath: metadata.name toFieldPath: newEnvironmentKey ``` Individual resources can use any data written to their in-memory environment. ### Patch an individual resource To patch an individual resource, inside the `patches` of the resource, use `ToEnvironmentFieldPath` to copy data from the resource to the in-memory environment. Use `FromEnvironmentFieldPath` to copy data to the resource from the in-memory environment. ```yaml {label="envpatch",copy-lines="none"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: vpc base: apiVersion: ec2.aws.m.upbound.io/v1beta1 kind: VPC spec: forProvider: cidrBlock: 172.16.0.0/16 patches: - type: ToEnvironmentFieldPath fromFieldPath: status.atProvider.id toFieldPath: vpcId - type: FromEnvironmentFieldPath fromFieldPath: tags toFieldPath: spec.forProvider.tags ``` The [EnvironmentConfigs]({{}}) page has more information on EnvironmentConfigs options and usage. ## Types of patches Function Patch and Transform supports multiple patch types, each using a different source for data and applying the patch to a different location. Summary of Crossplane patches {{< table "table table-hover" >}} | Patch Type | Data Source | Data Destination | | --- | --- | --- | | [FromCompositeFieldPath](#fromcompositefieldpath) | A field in the composite resource. | A field in the composed resource. | | [ToCompositeFieldPath](#tocompositefieldpath) | A field in the composed resource. | A field in the composite resource. | | [CombineFromComposite](#combinefromcomposite) | Multiple fields in the composite resource. | A field in the composed resource. | | [CombineToComposite](#combinetocomposite) | Multiple fields in the composed resource. | A field in the composite resource. | | [FromEnvironmentFieldPath](#fromenvironmentfieldpath) | Data in the in-memory environment | A field in the composed resource. | | [ToEnvironmentFieldPath](#toenvironmentfieldpath) | A field in the composed resource. | The in-memory environment. | | [CombineFromEnvironment](#combinefromenvironment) | Multiple fields in the in-memory environment. | A field in the composed resource. | | [CombineToEnvironment](#combinetoenvironment) | Multiple fields in the composed resource. | A field in the in-memory environment. | {{< /table >}} {{}} All the following examples use the same set of Compositions, CompositeResourceDefinitions and EnvironmentConfigs. Only the applied patches change between examples. All examples rely on [provider-aws-s3](https://github.com/crossplane-contrib/provider-upjet-aws) to create resources. {{< expand "Reference Composition" >}} ```yaml {copy-lines="all"} apiVersion: apiextensions.crossplane.io/v1 kind: Composition metadata: name: example-composition spec: compositeTypeRef: apiVersion: example.org/v1alpha1 kind: Example environment: environmentConfigs: - ref: name: example-environment mode: Pipeline pipeline: - step: patch-and-transform functionRef: name: function-patch-and-transform input: apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: bucket1 base: apiVersion: s3.aws.m.upbound.io/v1beta1 kind: Bucket spec: forProvider: region: us-east-2 - name: bucket2 base: apiVersion: s3.aws.m.upbound.io/v1beta1 kind: Bucket spec: forProvider: region: us-east-2 ``` {{< /expand >}} {{}} ```yaml {copy-lines="all"} apiVersion: apiextensions.crossplane.io/v1 kind: CompositeResourceDefinition metadata: name: examples.example.org spec: group: example.org names: kind: Example plural: examples versions: - name: v1alpha1 served: true referenceable: true schema: openAPIV3Schema: type: object properties: spec: type: object properties: field1: type: string field2: type: string field3: type: string desiredRegion: type: string boolField: type: boolean numberField: type: integer status: type: object properties: url: type: string ``` {{< /expand >}} {{< expand "Reference XR" >}} ```yaml {copy-lines="all"} apiVersion: example.org/v1alpha1 kind: Example metadata: namespace: default name: my-example spec: field1: "field1-text" field2: "field2-text" desiredRegion: "eu-north-1" boolField: false numberField: 10 ``` {{< /expand >}} {{< expand "Reference EnvironmentConfig" >}} ```yaml {copy-lines="all"} apiVersion: apiextensions.crossplane.io/v1beta1 kind: EnvironmentConfig metadata: name: example-environment data: locations: us: us-east-2 eu: eu-north-1 key1: value1 key2: value2 ``` {{< /expand >}} {{< /hint >}} ### FromCompositeFieldPath The `FromCompositeFieldPath` patch takes a value in a composite resource and applies it to a field in the composed resource. {{< hint "tip" >}} Use the `FromCompositeFieldPath` patch to apply options from users in their XRs to settings in managed resource `forProvider` settings. {{< /hint >}} For example, to use the value `desiredRegion` provided by a user in a composite resource to a managed resource's `region`. The `fromFieldPath` value is a field in the composite resource. The `toFieldPath` value is the field in the composed resource to change. ```yaml {label="fromComposite",copy-lines="9-11"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: bucket1 base: apiVersion: s3.aws.m.upbound.io/v1beta1 kind: Bucket spec: forProvider: region: us-east-2 patches: - type: FromCompositeFieldPath fromFieldPath: spec.desiredRegion toFieldPath: spec.forProvider.region ``` View the managed resource to see the updated `region` ```yaml {label="fromCompMR",copy-lines="1"} $ kubectl describe bucket Name: my-example-qlr68-29nqf # Removed for brevity Spec: For Provider: Region: eu-north-1 ``` ### ToCompositeFieldPath The `ToCompositeFieldPath` writes data from an individual composed resource to the composite resource that created it. {{< hint "tip" >}} Use `ToCompositeFieldPath` patches to take data from one composed resource in a Composition and use it in a second composed resource in the same Composition. {{< /hint >}} For example, after Crossplane creates a new managed resource, take the value `hostedZoneID` and apply it as a `label` in the composite resource. ```yaml {label="toComposite",copy-lines="9-11"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: bucket1 base: apiVersion: s3.aws.m.upbound.io/v1beta1 kind: Bucket spec: forProvider: region: us-east-2 patches: - type: ToCompositeFieldPath fromFieldPath: status.atProvider.hostedZoneId toFieldPath: metadata.labels['ZoneID'] ``` View the created managed resource to see the `Hosted Zone Id` field. ```yaml {label="toCompMR",copy-lines="none"} $ kubectl describe bucket Name: my-example-p5pxf-5vnp8 # Removed for brevity Status: At Provider: Hosted Zone Id: Z2O1EMRO9K5GLX # Removed for brevity ``` Next view the composite resource and confirm the patch applied the `label` ```yaml {label="toCompositeXR",copy-lines="none"} $ kubectl describe composite Name: my-example-p5pxf Labels: ZoneID=Z2O1EMRO9K5GLX ``` ### CombineFromComposite The `CombineFromComposite` patch takes values from the composite resource, combines them and applies them to the composed resource. {{< hint "tip" >}} Use the `CombineFromComposite` patch to create complex strings, like security policies and apply them to a composed resource. {{< /hint >}} For example, use the XR value `desiredRegion` and `field2` to generate the managed resource's `name` The `CombineFromComposite` patch only supports the `combine` option. The `variables` are the list of `fromFieldPath` values from the composite resource to combine. The only supported `strategy` is `strategy: string`. Optionally you can apply a `string.fmt`, based on [Go string formatting](https://pkg.go.dev/fmt) to specify how to combine the strings. The `toFieldPath` is the field in the composed resource to apply the new string to. ```yaml {label="combineFromComp",copy-lines="11-20"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: bucket1 base: apiVersion: s3.aws.m.upbound.io/v1beta1 kind: Bucket spec: forProvider: region: us-east-2 patches: - type: CombineFromComposite combine: variables: - fromFieldPath: spec.desiredRegion - fromFieldPath: spec.field2 strategy: string string: fmt: "my-resource-%s-%s" toFieldPath: metadata.name ``` Describe the managed resource to see the applied patch. ```yaml {label="describeCombineFromComp",copy-lines="none"} $ kubectl describe bucket Name: my-resource-eu-north-1-field2-text ``` ### CombineToComposite The `CombineToComposite` patch takes values from the composed resource, combines them and applies them to the composite resource. {{}} Use `CombineToComposite` patches to create a single field like a URL from multiple fields in a managed resource. {{< /hint >}} For example, use the managed resource `name` and `region` to generate a custom `url` field. {{< hint "important" >}} Writing custom fields in the status field of a composite resource requires defining the custom fields in the CompositeResourceDefinition first. {{< /hint >}} The `CombineToComposite` patch only supports the `combine` option. The `variables` are the list of `fromFieldPath` the managed resource to combine. The only supported `strategy` is `strategy: string`. Optionally you can apply a `string.fmt`, based on [Go string formatting](https://pkg.go.dev/fmt) to specify how to combine the strings. The `toFieldPath` is the field in the composite resource to apply the new string to. ```yaml {label="combineToComposite",copy-lines="9-11"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: bucket1 base: apiVersion: s3.aws.m.upbound.io/v1beta1 kind: Bucket spec: forProvider: region: us-east-2 patches: - type: CombineToComposite combine: variables: - fromFieldPath: metadata.name - fromFieldPath: spec.forProvider.region strategy: string string: fmt: "https://%s.%s.com" toFieldPath: status.url ``` View the composite resource to verify the applied patch. ```yaml {copy-lines="none"} $ kubectl describe composite Name: my-example-bjdjw API Version: example.org/v1alpha1 Kind: Example # Removed for brevity Status: # Removed for brevity URL: https://my-example-bjdjw-r6ncd.us-east-2.com ``` ### FromEnvironmentFieldPath The `FromEnvironmentFieldPath` patch takes values from the in-memory environment and applies them to the composed resource. {{}} Use `FromEnvironmentFieldPath` to apply custom managed resource settings based on the current environment. {{< /hint >}} For example, use the environment's `locations.eu` value and apply it as the `region`. ```yaml {label="fromEnvField",copy-lines="9-11"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: bucket1 base: apiVersion: s3.aws.m.upbound.io/v1beta1 kind: Bucket spec: forProvider: region: us-east-2 patches: - type: FromEnvironmentFieldPath fromFieldPath: locations.eu toFieldPath: spec.forProvider.region ``` Verify managed resource to confirm the applied patch. ```yaml {copy-lines="none"} kubectl describe bucket Name: my-example-8vrvc-xx5sr # Removed for brevity Spec: For Provider: Region: eu-north-1 # Removed for brevity ``` ### ToEnvironmentFieldPath {{}} For more information about using an EnvironmentConfig, read the [EnvironmentConfigs documentation]({{}}). {{< /hint >}} The `ToEnvironmentFieldPath` patch takes a value from the composed resource and applies it to the in-memory environment. {{}} Use `ToEnvironmentFieldPath` to write data to the environment that any FromEnvironmentFieldPath patch can access. {{< /hint >}} For example, use the desired `region` value and apply it as the environment's `key1`. ```yaml {label="toEnvField",copy-lines="9-11"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: bucket1 base: apiVersion: s3.aws.m.upbound.io/v1beta1 kind: Bucket spec: forProvider: region: us-east-2 patches: - type: ToEnvironmentFieldPath fromFieldPath: spec.forProvider.region toFieldPath: key1 ``` Because the environment is in-memory, there is no command to confirm the patch wrote the value to the environment. ### CombineFromEnvironment {{}} For more information about using an EnvironmentConfig, read the [EnvironmentConfigs documentation]({{}}). {{< /hint >}} The `CombineFromEnvironment` patch combines multiple values from the in-memory environment and applies them to the composed resource. {{}} Use `CombineFromEnvironment` patch to create complex strings, like security policies and apply them to a managed resource. {{< /hint >}} For example, combine multiple fields in the environment to create a unique `annotation` . The `CombineFromEnvironment` patch only supports the `combine` option. The only supported `strategy` is `strategy: string`. The `variables` are the list of `fromFieldPath` values from the in-memory environment to combine. Optionally you can apply a `string.fmt`, based on [Go string formatting](https://pkg.go.dev/fmt) to specify how to combine the strings. The `toFieldPath` is the field in the composed resource to apply the new string to. ```yaml {label="combineFromEnv",copy-lines="11-20"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: bucket1 base: apiVersion: s3.aws.m.upbound.io/v1beta1 kind: Bucket spec: forProvider: region: us-east-2 patches: - type: CombineFromEnvironment combine: strategy: string variables: - fromFieldPath: key1 - fromFieldPath: key2 string: fmt: "%s-%s" toFieldPath: metadata.annotations[EnvironmentPatch] ``` Describe the managed resource to see new `annotation`. ```yaml {copy-lines="none",label="combineFromEnvDesc"} $ kubectl describe bucket Name: my-example-zmxdg-grl6p # Removed for brevity Annotations: EnvironmentPatch: value1-value2 # Removed for brevity ``` ### CombineToEnvironment {{}} For more information about using an EnvironmentConfig, read the [EnvironmentConfigs documentation]({{}}). {{< /hint >}} The `CombineToEnvironment` patch combines multiple values from the composed resource and applies them to the in-memory EnvironmentConfig environment. {{}} Use `CombineToEnvironment` patch to create complex strings, like security policies to use in other managed resources. {{< /hint >}} For example, combine multiple fields in the managed resource to create a unique string and store it in the environment's `key2` value. The string combines the managed resource `Kind` and `region`. The `CombineToEnvironment` patch only supports the `combine` option. The only supported `strategy` is `strategy: string`. The `variables` are the list of `fromFieldPath` values in the managed resource to combine. Optionally you can apply a `string.fmt`, based on [Go string formatting](https://pkg.go.dev/fmt) to specify how to combine the strings. The `toFieldPath` is the key in the environment to write the new string to. ```yaml {label="combineToEnv",copy-lines="none"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: bucket1 base: apiVersion: s3.aws.m.upbound.io/v1beta1 kind: Bucket spec: forProvider: region: us-east-2 patches: - type: CombineToEnvironment combine: strategy: string variables: - fromFieldPath: kind - fromFieldPath: spec.forProvider.region string: fmt: "%s.%s" toFieldPath: key2 ``` Because the environment is in-memory, there is no command to confirm the patch wrote the value to the environment. ## Transform a patch When applying a patch, Crossplane supports modifying the data before applying it as a patch. Crossplane calls this a "transform" operation. Summary of Crossplane transforms. {{< table "table table-hover" >}} | Transform Type | Action | | --- | --- | | [convert](#convert-transforms) | Converts an input data type to a different type. Also called "casting." | | [map](#map-transforms) | Selects a specific output based on a specific input. | | [match](#match-transform) | Selects a specific output based on a string or regular expression. | | [math](#math-transforms) | Applies a mathematical operation on the input. | | [string](#string-transforms) | Change the input string using [Go string formatting](https://pkg.go.dev/fmt). | {{< /table >}} Apply a transform directly to an individual patch with the `transforms` field. A `transform` requires a `type`, indicating the transform action to take. The other transform field is the same as the `type`, in this example, `map`. The other fields depend on the patch type used. This example uses a `type: map` transform, taking the input `spec.desiredRegion`, matching it to either `us` or `eu` and returning the corresponding AWS region for the `spec.forProvider.region` value. ```yaml {label="transform1",copy-lines="none"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: bucket1 base: apiVersion: s3.aws.m.upbound.io/v1beta1 kind: Bucket spec: forProvider: region: us-east-2 patches: - type: FromCompositeFieldPath fromFieldPath: spec.desiredRegion toFieldPath: spec.forProvider.region transforms: - type: map map: us: us-east-2 eu: eu-north-1 ``` ### Convert transforms The `convert` transform type changes the input data type to a different data type. {{< hint "tip" >}} Some provider APIs require a field to be a string. Use a `convert` type to change any boolean or integer fields to strings. {{< /hint >}} A `convert` transform requires a `toType`, defining the output data type. ```yaml {label="convert",copy-lines="none"} patches: - type: FromCompositeFieldPath fromFieldPath: spec.numberField toFieldPath: metadata.label["numberToString"] transforms: - type: convert convert: toType: string ``` Supported `toType` values: {{< table "table table-sm table-hover" >}} | `toType` value | Description | | -- | -- | | `bool` | A boolean value of `true` or `false`. | | `float64` | A 64-bit float value. | | `int` | A 32-bit integer value. | | `int64` | A 64-bit integer value. | | `string` | A string value. | | `object` | An object. | | `array` | An array. | {{< /table >}} #### Converting strings to booleans When converting from a string to a `bool` Crossplane considers the string values `1`, `t`, `T`, `TRUE`, `True` and `true` equal to the boolean value `True`. The strings `0`, `f`, `F`, `FALSE`, `False` and `false` are equal to the boolean value `False`. #### Converting numbers to booleans Crossplane considers the integer `1` and float `1.0` equal to the boolean value `True`. Any other integer or float value is `False`. #### Converting booleans to numbers Crossplane converts the boolean value `True` to the integer `1` or float64 `1.0`. The value `False` converts to the integer `0` or float64 `0.0` #### Converting strings to float64 When converting from a `string` to a `float64` Crossplane supports an optional `format: quantity` field. Using `format: quantity` translates size suffixes like `M` for megabyte or `Mi` for megabit into the correct float64 value. {{}} Refer to the [Go language docs](https://pkg.go.dev/k8s.io/apimachinery/pkg/api/resource#Quantity) for a full list of supported suffixes. {{}} Add `format: quantity` to the `convert` object to enable quantity suffix support. ```yaml {label="format",copy-lines="all"} - type: convert convert: toType: float64 format: quantity ``` #### Converting strings to objects Crossplane converts JSON strings to objects. Add `format: json` to the `convert` object which is the only supported string format for this conversion. ```yaml {label="object",copy-lines="all"} - type: convert convert: toType: object format: json ``` {{< hint "tip" >}} This conversion is useful for patching keys in an object. {{< /hint >}} The following example adds a tag to a resource with a `customized key`: ```yaml {label="patch-key",copy-lines="all"} - type: FromCompositeFieldPath fromFieldPath: spec.clusterName toFieldPath: spec.forProvider.tags transforms: - type: string string: type: Format fmt: '{"kubernetes.io/cluster/%s": "true"}' - type: convert convert: toType: object format: json ``` #### Converting strings to arrays Crossplane converts JSON strings to arrays. Add `format: json` to the `convert` object which is the only supported string format for this conversion. ```yaml {label="array",copy-lines="all"} - type: convert convert: toType: array format: json ``` ### Map transforms The `map` transform type _maps_ an input value to an output value. {{< hint "tip" >}} The `map` transform is useful for translating generic region names like `US` or `EU` to provider specific region names. {{< /hint >}} The `map` transform compares the value from the `fromFieldPath` to the options listed in the `map`. If Crossplane finds the value, Crossplane puts the mapped value in the `toFieldPath`. {{}} Crossplane throws an error for the patch if the value isn't found. {{< /hint >}} `spec.field1` is the string `"field1-text"` then Crossplane uses the string `firstField` for the `annotation`. If `spec.field1` is the string `"field2-text"` then Crossplane uses the string `secondField` for the `annotation`. ```yaml {label="map",copy-lines="none"} patches: - type: FromCompositeFieldPath fromFieldPath: spec.field1 toFieldPath: metadata.annotations["myAnnotation"] transforms: - type: map map: "field1-text": "firstField" "field2-text": "secondField" ``` In this example, the value of `spec.field1` is `field1-text`. ```yaml {label="comositeMap",copy-lines="none"} $ kubectl describe composite Name: my-example-twx7n Spec: # Removed for brevity field1: field1-text ``` The annotation applied to the managed resource is `firstField`. ```yaml {label="mrMap",copy-lines="none"} $ kubectl describe bucket Name: my-example-twx7n-ndb2f Annotations: crossplane.io/composition-resource-name: bucket1 myAnnotation: firstField # Removed for brevity. ``` ### Match transform The `match` transform is like the `map` transform. The `match` transform adds support for regular expressions along with exact strings and can provide default values if there isn't a match. A `match` object requires a `patterns` object. The `patterns` is a list of one or more patterns to attempt to match the input value against. ```yaml {label="match",copy-lines="1-8"} patches: - type: FromCompositeFieldPath fromFieldPath: spec.field1 toFieldPath: metadata.annotations["myAnnotation"] transforms: - type: match match: patterns: - type: literal # Removed for brevity - type: regexp # Removed for brevity ``` Match `patterns` can be either `type: literal` to match an exact string or `type: regexp` to match a regular expression. {{}} Crossplane stops processing matches after the first pattern match. {{< /hint >}} #### Match an exact string Use a `pattern` with `type: literal` to match an exact string. On a successful match Crossplane provides the `result:` to the patch `toFieldPath`. ```yaml {label="matchLiteral"} patches: - type: FromCompositeFieldPath fromFieldPath: spec.field1 toFieldPath: metadata.annotations["myAnnotation"] transforms: - type: match match: patterns: - type: literal literal: "field1-text" result: "matchedLiteral" ``` #### Match a regular expression Use a `pattern` with `type: regexp` to match a regular expression. Define a `regexp` key with the value of the regular expression to match. On a successful match Crossplane provides the `result:` to the patch `toFieldPath`. ```yaml {label="matchRegex"} patches: - type: FromCompositeFieldPath fromFieldPath: spec.field1 toFieldPath: metadata.annotations["myAnnotation"] transforms: - type: match match: patterns: - type: regexp regexp: '^field1.*' result: "foundField1" ``` #### Using default values Optionally you can provide a default value to use if there is no matching pattern. The default value can either be the original input value or a defined default value. Use `fallbackTo: Value` to provide a default value if a match isn't found. For example if the string `unknownString` isn't matched, Crossplane provides the `Value` `StringNotFound` to the `toFieldPath` ```yaml {label="defaultValue"} patches: - type: FromCompositeFieldPath fromFieldPath: spec.field1 toFieldPath: metadata.annotations["myAnnotation"] transforms: - type: match match: patterns: - type: literal literal: "UnknownString" result: "foundField1" fallbackTo: Value fallbackValue: "StringNotFound" ``` To use the original input as the fallback value use `fallbackTo: Input`. Crossplane uses the original `fromFieldPath` input for the `toFieldPath` value. ```yaml {label="defaultInput"} patches: - type: FromCompositeFieldPath fromFieldPath: spec.field1 toFieldPath: metadata.annotations["myAnnotation"] transforms: - type: match match: patterns: - type: literal literal: "UnknownString" result: "foundField1" fallbackTo: Input ``` ### Math transforms Use the `math` transform to multiply an input or apply a minimum or maximum value. {{}} A `math` transform only supports integer inputs. {{< /hint >}} ```yaml {label="math",copy-lines="1-7"} patches: - type: FromCompositeFieldPath fromFieldPath: spec.numberField toFieldPath: metadata.annotations["mathAnnotation"] transforms: - type: math math: ... ``` #### clampMin The `type: clampMin` uses a defined minimum value if an input is larger than the `type: clampMin` value. For example, this `type: clampMin` requires an input to be greater than `20`. If an input is lower than `20`, Crossplane uses the `clampMin` value for the `toFieldPath`. ```yaml {label="clampMin"} patches: - type: FromCompositeFieldPath fromFieldPath: spec.numberField toFieldPath: metadata.annotations["mathAnnotation"] transforms: - type: math math: type: clampMin clampMin: 20 ``` #### clampMax The `type: clampMax` uses a defined minimum value if an input is larger than the `type: clampMax` value. For example, this `type: clampMax` requires an input to be less than `5`. If an input is higher than `5`, Crossplane uses the `clampMax` value for the `toFieldPath`. ```yaml {label="clampMax"} patches: - type: FromCompositeFieldPath fromFieldPath: spec.numberField toFieldPath: metadata.annotations["mathAnnotation"] transforms: - type: math math: type: clampMax clampMax: 5 ``` #### Multiply The `type: multiply` multiplies the input by the `multiply` value. For example, this `type: multiply` multiplies the value from the `fromFieldPath` value by `2` ```yaml {label="multiply"} patches: - type: FromCompositeFieldPath fromFieldPath: spec.numberField toFieldPath: metadata.annotations["mathAnnotation"] transforms: - type: math math: type: multiply multiply: 2 ``` {{}} The `multiply` value only supports integers. {{< /hint >}} ### String transforms The `string` transform applies string formatting or manipulation to string inputs. ```yaml {label="string"} patches: - type: FromCompositeFieldPath fromFieldPath: spec.field1 toFieldPath: metadata.annotations["stringAnnotation"] transforms: - type: string string: type: ... ``` String transforms support the following `types` * [Convert](#string-convert) * [Format](#string-format) * [Join](#join) * [Regexp](#regular-expression-type) * [TrimPrefix](#trim-prefix) * [TrimSuffix](#trim-suffix) #### String convert The `type: convert` converts the input based on one of the following conversion types: * `ToUpper` - Change the string to all upper case letters. * `ToLower` - Change the string to all lower case letters. * `ToBase64` - Create a new base64 string from the input. * `FromBase64` - Create a new text string from a base64 input. * `ToJson` - Convert the input string to valid JSON. * `ToSha1` - Create a SHA-1 hash of the input string. * `ToSha256` - Create a SHA-256 hash of the input string. * `ToSha512` - Create a SHA-512 hash of the input string. * `ToAdler32` - Create an Adler32 hash of the input string. ```yaml {label="stringConvert"} patches: - type: FromCompositeFieldPath fromFieldPath: spec.field1 toFieldPath: metadata.annotations["FIELD1-TEXT"] transforms: - type: string string: type: Convert convert: "ToUpper" ``` #### String format The `type: format` applies [Go string formatting](https://pkg.go.dev/fmt) to the input. ```yaml {label="typeFormat"} patches: - type: FromCompositeFieldPath fromFieldPath: spec.field1 toFieldPath: metadata.annotations["stringAnnotation"] transforms: - type: string string: type: Format fmt: "the-field-%s" ``` #### Join The `type: Join` joins all values in the input array into a string using the given separator. This transform only works with array inputs. ```yaml {label="typeJoin"} patches: - type: FromCompositeFieldPath fromFieldPath: spec.parameters.inputList toFieldPath: spec.targetJoined transforms: - type: string string: type: Join join: separator: "," ``` #### Regular expression type The `type: Regexp` extracts the part of the input matching a regular expression. Optionally use a `group` to match a regular expression capture group. By default Crossplane matches the entire regular expression. ```yaml {label="typeRegex"} patches: - type: FromCompositeFieldPath fromFieldPath: spec.desiredRegion toFieldPath: metadata.annotations["euRegion"] transforms: - type: string string: type: Regexp regexp: match: '^eu-(.*)-' group: 1 ``` #### Trim prefix The `type: TrimPrefix` uses Go's [TrimPrefix](https://pkg.go.dev/strings#TrimPrefix) and removes characters from the beginning of a line. ```yaml {label="typeTrimP"} patches: - type: FromCompositeFieldPath fromFieldPath: spec.desiredRegion toFieldPath: metadata.annotations["north-1"] transforms: - type: string string: type: TrimPrefix trim: `eu- ``` #### Trim suffix The `type: TrimSuffix` uses Go's [TrimSuffix](https://pkg.go.dev/strings#TrimSuffix) and removes characters from the end of a line. ```yaml {label="typeTrimS"} patches: - type: FromCompositeFieldPath fromFieldPath: spec.desiredRegion toFieldPath: metadata.annotations["eu"] transforms: - type: string string: type: TrimSuffix trim: `-north-1' ``` ## Patch policies Crossplane supports two types of patch policies: * `fromFieldPath` * `toFieldPath` ### fromFieldPath policy Using a `fromFieldPath: Required` policy on a patch requires the `fromFieldPath` to exist in the data source resource. {{}} If a resource patch isn't working applying the `fromFieldPath: Required` policy may produce an error in the composite resource to help troubleshoot. {{< /hint >}} By default, Crossplane applies the policy `fromFieldPath: Optional`. With `fromFieldPath: Optional` Crossplane ignores a patch if the `fromFieldPath` doesn't exist. With `fromFieldPath: Required` the composite resource produces an error if the `fromFieldPath` doesn't exist. ```yaml {label="required"} patches: - type: FromCompositeFieldPath fromFieldPath: spec.desiredRegion toFieldPath: metadata.annotations["eu"] policy: fromFieldPath: Required ``` ### toFieldPath policy By default when applying a patch the function replaces the destination data. Use `toFieldPath` to allow patches to merge arrays and objects without overwriting them. The `toFieldPath` policy supports these options: {{< table "table table-hover" >}} | Policy | Action | | --- | --- | | `Replace` (default) | Replace the value at `toFieldPath`. | | `MergeObjects` | Recursively merge into the value at `toFieldPath`. Keep any conflicting object keys. | | `ForceMergeObjects` | Recursively merge into the value at `toFieldPath`. Replace any conflicting object keys. | | `MergeObjectsAppendArrays` | Like `MergeObjects`, but append values to arrays instead of replacing them. | | `ForceMergeObjectsAppendArrays` | Like `ForceMergeObjects`, but append values to arrays instead of replacing them. | {{< /table >}} ```yaml {label="merge"} patches: - type: FromCompositeFieldPath fromFieldPath: spec.desiredRegion toFieldPath: metadata.annotations["eu"] policy: toFieldPath: MergeObjectsAppendArrays ``` ## Composite resource connection details Function patch and Transform must define the specific secret keys a resource creates with the `connectionDetails` object. {{}} | Secret Type | Description | | --- | --- | | `FromConnectionSecretKey` | Create a secret key matching the key of a secret generated by the resource. | | `FromFieldPath` | Create a secret key matching a field path of the resource. | | `FromValue` | Create a secret key with a predefined value. | {{< /table >}} {{}} The `value` type must use a string value. The `value` isn't added to the individual resource secret object. The `value` only appears in the combined composite resource secret. {{< /hint >}} ```yaml {label="conDeet",copy-lines="none"} kind: Composition spec: mode: Pipeline pipeline: - step: patch-and-transform functionRef: name: function-patch-and-transform input: apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: key base: # Removed for brevity spec: forProvider: # Removed for brevity writeConnectionSecretToRef: namespace: docs name: key1 connectionDetails: - name: my-username type: FromConnectionSecretKey fromConnectionSecretKey: username - name: my-field-secret type: FromFieldPath fromFieldPath: spec.forProvider.user - name: my-status-secret type: FromValue value: "docs.crossplane.io" ``` The `connectionDetails` in a resource can reference a secret from a resource with `FromConnectionSecretKey`, from another field in the resource with `FromFieldPath` or a statically defined value with `FromValue`. Crossplane sets the secret key to the `name` value. Describe the secret to view the secret keys inside the secret object. {{}} If more than one resource generates secrets with the same secret key name, Crossplane only saves one value. Use a custom `name` to create unique secret keys. {{< /hint >}} {{}} Crossplane only adds connection details listed in the `connectionDetails` to the combined secret object. Any connection secrets in a managed resource, not defined in the `connectionDetails` aren't added to the combined secret object. {{< /hint >}} ```shell {copy-lines="1"} kubectl describe secret Name: my-access-key-secret Namespace: default Labels: Annotations: Type: connection.crossplane.io/v1alpha1 Data ==== myUsername: 20 bytes myFieldSecret: 24 bytes myStaticSecret: 18 bytes ``` {{}} The CompositeResourceDefinition can also limit which keys Crossplane stores from the composite resources. By default an XRD writes all secret keys listed in the composed resources `connectionDetails` to the combined secret object. For more information on connection secrets read about [managed resources]({{}}). {{}} ## Resource readiness checks By default function-patch-and-transform considers a composite resource as `READY` when the status of all created resource are `Type: Ready` and `Status: True` Some resources, for example, a ProviderConfig, don't have a Kubernetes status and are never considered `Ready`. Custom readiness checks allow Compositions to define what custom conditions to meet for a resource to be `Ready`. {{< hint "tip" >}} Use multiple readiness checks if a resource must meet multiple conditions for it to be `Ready`. {{< /hint >}} Define a custom readiness check with the `readinessChecks` field on a resource. Checks have a `type` defining how to match the resource and a `fieldPath` of which field in the resource to compare. ```yaml {label="check",copy-lines="none"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: my-resource base: # Removed for brevity readinessChecks: - type: fieldPath: ``` Compositions support matching resource fields by: * [string match](#match-a-string) * [integer match](#match-an-integer) * [non-empty match](#match-that-a-field-exists) * [always ready](#always-consider-a-resource-ready) * [condition match](#match-a-condition) * [boolean match](#match-a-boolean) ### Match a string `MatchString` considers the composed resource to be ready when the value of a field in that resource matches a specified string. {{}} Crossplane only supports exact string matches. Substrings and regular expressions aren't supported in a readiness check. {{}} For example, matching the string `Online` in the resource's `status.atProvider.state` field. ```yaml {label="matchstring",copy-lines="none"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: my-resource base: # Removed for brevity readinessChecks: - type: MatchString fieldPath: status.atProvider.state matchString: "Online" ``` ### Match an integer `MatchInteger` considers the composed resource to be ready when the value of a field in that resource matches a specified integer. {{}} Crossplane doesn't support matching `0`. {{}} For example, matching the number `4` in the resource's `status.atProvider.state` field. ```yaml {label="matchint",copy-lines="none"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: my-resource base: # Removed for brevity readinessChecks: - type: MatchInteger fieldPath: status.atProvider.state matchInteger: 4 ``` ### Match that a field exists `NonEmpty` considers the composed resource to be ready when a field exists with a value. {{}} Crossplane considers a value of `0` or an empty string as empty. {{}} For example, to check that a resource's `status.atProvider.state` field isn't empty. ```yaml {label="NonEmpty",copy-lines="none"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: my-resource base: # Removed for brevity readinessChecks: - type: NonEmpty fieldPath: status.atProvider.state ``` {{}} Checking `NonEmpty` doesn't require setting any other fields. {{< /hint >}} ### Always consider a resource ready `None` considers the composed resource to be ready as soon as it's created. Crossplane doesn't wait for any other conditions before declaring the resource ready. For example, consider `my-resource` ready as soon as it's created. ```yaml {label="none",copy-lines="none"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: my-resource base: # Removed for brevity readinessChecks: - type: None ``` ### Match a condition `Condition` considers the composed resource to be ready when it finds the expected condition type, with the expected status for it in its `status.conditions`. For example, consider `my-resource`, which is ready if there is a condition of type `MyType` with a status of `Success`. ```yaml {label="condition",copy-lines="none"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: my-resource base: # Removed for brevity readinessChecks: - type: MatchCondition matchCondition: type: MyType status: Success ``` ### Match a boolean Two types of checks exist for matching boolean fields: * `MatchTrue` * `MatchFalse` `MatchTrue` considers the composed resource to be ready when the value of a field inside that resource is `true`. `MatchFalse` considers the composed resource to be ready when the value of a field inside that resource is `false`. For example, consider `my-resource`, which is ready if ` status.atProvider.manifest.status.ready` is `true`. ```yaml {label="matchTrue",copy-lines="none"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: my-resource base: # Removed for brevity readinessChecks: - type: MatchTrue fieldPath: status.atProvider.manifest.status.ready ``` {{}} Checking `MatchTrue` doesn't require setting any other fields. {{< /hint >}} `MatchFalse` matches fields that express readiness with the value `false`. For example, consider `my-resource`, is ready if ` status.atProvider.manifest.status.pending` is `false`. ```yaml {label="matchFalse",copy-lines="none"} apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources resources: - name: my-resource base: # Removed for brevity readinessChecks: - type: MatchFalse fieldPath: status.atProvider.manifest.status.pending ``` {{}} Checking `MatchFalse` doesn't require setting any other fields. {{< /hint >}}