function-tag-manager is a Crossplane function that allows
Platform Operators to manage Cloud tags on managed resources.
AWS and Azure resources managed by upjet-based providers that support tags are supported, and the function can manage tags for both cluster and namespace-scoped resources.
There several use cases for this Function:
- Allowing external systems to set tags on Crossplane Managed Resources without conflict.
- Adding Common Tags to Resources without having to update every resource in a Composition.
- Allowing users the ability to add their own tags when Requesting new resources.
- Removing tags that have been set earlier in the pipeline by other functions.
The function is installed as a Crossplane Package. Apply the following YAML manifest to your Crossplane cluster.
apiVersion: pkg.crossplane.io/v1
kind: Function
metadata:
name: crossplane-contrib-function-tag-manager
spec:
package: xpkg.upbound.io/crossplane-contrib/function-tag-manager:v0.9.0This function is designed to be a step in a Composition Pipeline after other functions have created Desired State. Below is an example pipeline step:
- step: manage-tags
functionRef:
name: crossplane-contrib-function-tag-manager
input:
apiVersion: tag-manager.fn.crossplane.io/v1beta1
kind: ManagedTags
addTags:
- type: FromValue
policy: Replace
tags:
key1: value1
key2: value2
- type: FromCompositeFieldPath
fromFieldPath: spec.parameters.additionalTags
policy: Replace
- type: FromCompositeFieldPath
fromFieldPath: spec.parameters.optionalTags
policy: Retain
ignoreTags:
- type: FromValue
policy: Replace
keys:
- external-tag-1
- external-tag-2
- type: FromCompositeFieldPath
fromFieldPath: spec.parameters.ignoreTagKeysReplace
policy: Replace
- type: FromCompositeFieldPath
fromFieldPath: spec.parameters.ignoreTagKeyRetain
policy: RetainThe addTags field configures tags that will be added to every resource.
The FromValue type indicates that tags will be defined in the function input.
The FromCompositeField type indicates that the tags will be imported from the Composite Resource manifest.
The FromEnvironmentFieldPath type indicates that the tags will be imported from the Environment.
addTags:
- type: FromValue
policy: Replace
tags:
key1: value1
key2: value2
- type: FromCompositeFieldPath
fromFieldPath: spec.parameters.additionalTags
policy: Replace
- type: FromCompositeFieldPath
fromFieldPath: spec.parameters.optionalTags
policy: Retain
- type: FromEnvironmentFieldPath
fromFieldPath: tagsThe ignoreTags configures Observed tags in the Cloud that Crossplane will "ignore". In most
cases, Crossplane will attempt to manage every field of a resource, and if a difference is calculated
Crossplane will update the resource and remove fields that are not in the Desired state.
There are many Cloud management systems that set tags on Resources. By adding the keys
to those tags in the ignoreTags section, the function will populate the Desired state with
the values of the Observed tags for each key defined.
Tag keys to ignore can be defined in FromValue, in the Composite/Claim using FromCompositeFieldPath or from EnvironmentConfig using FromEnvironmentFieldPath
ignoreTags:
- type: FromValue
policy: Replace
keys:
- external-tag-1
- external-tag-2
- type: FromCompositeFieldPath
fromFieldPath: spec.parameters.ignoreTagKeysReplace
policy: Replace
- type: FromCompositeFieldPath
fromFieldPath: spec.parameters.ignoreTagKeysRetain
policy: Retain
- type: FromEnvironmentFieldPath
fromFieldPath: ignoreTagsAnother option for allowing external systems to manage tags is to use the initProvider field of a Managed Resource.
The function can remove tags defined in the desired state by specifying
removeTags and providing an array of keys to delete.
removeTags:
- type: FromValue
keys:
- fromValue2
- type: FromCompositeFieldPath
fromFieldPath: spec.parameters.removeTags
- type: FromEnvironmentFieldPath
fromFieldPath: removeTagsThe function tells Crossplane how long it may cache the function's response.
By default this is one minute. Set ttl in the input to change it for a
Composition. The value is a Go duration string such as 30s or 5m, and
0s disables caching. A negative or unparseable value returns a fatal result.
ttl: 5mTo change the default for every Composition, start the function with the
--ttl flag. A ttl in the input overrides it.
apiVersion: pkg.crossplane.io/v1beta1
kind: DeploymentRuntimeConfig
metadata:
name: function-tag-manager
spec:
deploymentTemplate:
spec:
selector: {}
template:
spec:
containers:
- name: package-runtime
args:
- --ttl=5mReference it from the Function with spec.runtimeConfigRef.name: function-tag-manager.
When Merging tags, a Policy can be set:
Replace(default) in the case the desired and observed tags don't match, the observed value will replace desired.Retainin the case the desired and observed tags don't match, the desired value will remain.
This function will skip any resource with the tag-manager.fn.crossplane.io/ignore-resource Kubernetes annotation set to True or true:
apiVersion: ec2.aws.upbound.io/v1beta1
kind: InternetGateway
metadata:
annotations:
tag-manager.fn.crossplane.io/ignore-resource: "True"
name: my-igwNote: Versions v0.7.0 and earlier of the function used a label instead of the annotation to skip a resource. For backward compatibility, the label tag-manager.fn.crossplane.io/ignore-resource is still supported. However, if both the annotation and label are present, the annotation takes precedence. Using annotations is the recommended approach as it follows Kubernetes best practices.
This function supports both AWS and Azure resources that allow setting of tags.
Counts below are per resource kind. Starting with the 2.x providers both Cluster-scoped and
Namespace-scoped resources are supported, so each kind has two Custom Resource Definitions
(for example ec2.aws.upbound.io/Instance and ec2.aws.m.upbound.io/Instance) and two entries
in the generated filter.
A scan of the v2.7.0 AWS provider shows that 539 resource kinds support tags and 493 do not.
The AWS Provider CRDs were scanned using cmd/generator/main.go to generate the list in filters/zz_provider-upjet-aws.go.
A scan of the v2.7.0 Azure provider shows that 294 resource kinds support tags and 475 do not. Both
Cluster-scoped (azure.upbound.io) and Namespace-scoped (azure.m.upbound.io) API groups are supported.
The Azure Provider CRDs were scanned using the same generator to create the list in filters/zz_provider-upjet-azure.go.
Note: As of v0.9.0 the filters are generated from provider v2.7.0 CRDs, and a small number of
resources are newly classified as taggable. If you manage cloudfront/Function,
ec2/VPCIpamPoolCidrAllocation, or cognitiveservices/AccountRaiBlocklist, upgrade the AWS or
Azure provider to at least v2.7.0 — older providers will reject the tags field with a Kubernetes
API validation error.
To regenerate the resource filters for both providers:
cd filters
go generate ./...This will clone the provider repositories, scan their CRDs, and regenerate the filter files.
# Run code generation - see input/generate.go
$ go generate ./...
# Run tests
$ go test -cover ./...
ok github.com/crossplane-contrib/function-tag-manager 0.535s coverage: 66.9% of statements
ok github.com/crossplane-contrib/function-tag-manager/cmd/generator 1.012s coverage: 44.3% of statements
github.com/crossplane-contrib/function-tag-manager/cmd/generator/render coverage: 0.0% of statements
github.com/crossplane-contrib/function-tag-manager/filters coverage: 0.0% of statements
github.com/crossplane-contrib/function-tag-manager/input/v1beta1 coverage: 0.0% of statements
# Lint the code
$ docker run --rm -v $(pwd):/app -v ~/.cache/golangci-lint/v2.12.2:/root/.cache -w /app golangci/golangci-lint:v2.12.2 golangci-lint run
0 issues.
# Build a Docker image - see Dockerfile
$ docker build .To test this function, it can be run locally in debug mode:
# Run your Function in insecure mode
go run . --insecure --debugOnce your Function is running, in another window you can use the render command.
# Install Crossplane CLI
$ curl -sL https://raw.githubusercontent.com/crossplane/crossplane/master/install.sh | XP_CHANNEL=stable shTo build the function, run:
docker build . --tag=function-tag-managerPlease note that this command builds an image for your local computer architecture. In general, Crossplane projects build images for linux/amd64 and linux/arm64. See the Github ci.yaml workflow for an example.
Next create the Crossplane Package, embedding the function we just built:
crossplane xpkg build -f package --embed-runtime-image=function-tag-manager -o function-tag-manager.xpkgI use the up binary to push to the Upbound Marketplace
up xpkg push xpkg.upbound.io/crossplane-contrib/function-tag-manager:v0.9.0 -f function-tag-manager.xpkg