Watch our latest talk from KubeCon + CloudNativeCon EU 2024!
Carvel Logo

Blog Posts

Using ytt to create Crossplane Template Function

by Rohit Aggarwal — Feb 14, 2024

In this blog, we are going to learn on how to create a Crossplane Composition function which will compose the Crossplane resources using ytt templates.

What is Crossplane?

Crossplane is an open-source Kubernetes extension that empowers organizations to manage cloud infrastructure across any cloud through standard Kubernetes APIs. It allows platform teams to declaratively define and manage the cloud infrastructure, like databases, storage volumes, virtual machines, etc., through Kubernetes APIs.

Crossplane has several core components referred to as ‘Concepts’ that can work together to acheive your infrastructure goals. A few that we will use in this example are Composite Resource, Compositions, and Composition functions.

Crossplane Composite Resource

A composite resource represents a set of managed resources as a single Kubernetes object. It uses Composition template to create multiple managed resources as a single Kubernetes object.

Crossplane Compositions

Compositions are a template for creating multiple managed resources as a single object. A Composition composes individual managed resources together into a larger, reusable solution. An example of Composition may combine a virtual machine, storage resources, and networking policies. A Composition template links all these individual resources together.

Crossplane Compositions have some limitations though:

  1. Compositions don’t support conditions, meaning that the transformations they provide are applied on an “all or nothing” basis.
  2. They also don’t support loops, which means that you cannot apply transformations iteratively.
  3. Finally, advanced operations are not supported either, like checking for statuses in other systems.

To overcome this, Crossplane introduced the composition function.

Crossplane Composition Function

Composition functions (or just functions, for short) are custom programs that template Crossplane resources. Crossplane calls composition functions to determine what resources it should create when you create a composite resource (XR).


  • kind – to create a test Kubernetes cluster. Use this link to set up Kubernetes cluster on kind.
  • kubectl – to apply manifests to provision cloud resources. Use this link to install kubectl.
  • Helm – to install Crossplane on the test cluster. Use this link to install helm.

Install Crossplane

Use this link to install Crossplane.

Create the ytt templating composition function

YTT is a tool to template and patch YAML files written in Go. Mostly, it has been used as an executable in the form of CLI. However, in this example we will use it as a library to create Crossplane Composition function.

Read the Composition Function documentation for more details on how to build your own functions.

Use ytt as a library

Crossplane has already created a template github repository (function-template-go) for writing composition function in Go. We will fork this repo and setup our Go project by making the necessary changes as mentioned in the README. To consume ytt as a library:

  1. Fork the repo.

  2. Add ytt as a dependency in go.mod:

    require  (
        ... v0.46.2
  3. Define input.go to contain the type for the Crossplane function.

    type YTT struct {
        metav1.TypeMeta   `json:",inline"`
        metav1.ObjectMeta `json:"metadata,omitempty"`
        // Source specifies the different types of input sources that can be used with this function
        Source TemplateSource `json:"source"`
        // Inline is the inline form input of the templates
        Inline string `json:"inline,omitempty"`
        // FileSystem is the folder path where the templates are located
        FileSystem *TemplateSourceFileSystem `json:"fileSystem,omitempty"`
    type TemplateSource string
    const (
        // InlineSource indicates that function will get its input as inline
        InlineSource TemplateSource = "Inline"
        // FileSystemSource indicates that function will get its input from a folder
        FileSystemSource TemplateSource = "FileSystem"
    type TemplateSourceFileSystem struct {
        DirPath string `json:"dirPath,omitempty"`

    Sample input.go can be found here.

  4. Run go generate command.

  5. create and populate an instance of the ytt template command:

    templatingOptions := yttcmd.NewOptions()
  6. invoke the ytt template command to evaluate:

    output := templatingOptions.RunWithFiles(input, noopUI)

    Internally, we are going to convert the inline ytt template into a file and then pass the file as input to the ytt RunWithFiles function. Sample code and more logic can be found here.

  7. Add logic to the RunFunction in fn.go.

I have created 1 sample of Crossplane ytt function here. This sample only supports the template and not the overlay functionality of ytt.

Test locally

To test the logic locally and iterate over it incrementally, use crossplane beta render. To test it locally, see thee example here.

$ crossplane beta render xr.yaml composition.yaml function.yaml

Build and push the function to a package registry

Once we see that the ytt generates the expected yaml from the templated file, the next step is to build and push the function to a package registry.

You build a function in two stages. First, you build the function’s runtime. This is the Open Container Initiative (OCI) image Crossplane uses to run your function. You then embed that runtime in a package and push it to a package registry.

# Build the function's runtime image - see Dockerfile
$ docker build . --tag=runtime

Use the Crossplane CLI to build a package.

# Build a function package - see package/crossplane.yaml
$ crossplane xpkg build -f package --embed-runtime-image=runtime

Push the function package to the repository.

$ crossplane xpkg push --package-files=package/function-ytt-templating-f58cf278b530.xpkg

This function can now be used in composite resources.

Join the Carvel Community

We are excited to hear from you and learn with you! Here are several ways you can get involved:

  • Join Carvel’s slack channel, #carvel in Kubernetes workspace, and connect with over 1000+ Carvel users.
  • Find us on GitHub. Suggest how we can improve the project, the docs, or share any other feedback.
  • Attend our Community Meetings! Check out the Community page for full details on how to attend.

We look forward to hearing from you and hope you join us in building a strong packaging and distribution story for applications on Kubernetes!