Crossplane lets you define your own infrastructure APIs using the concept of "Composite Resource Definitions" (XRDs).

Let’s say you want to give your developers a simple way to provision a managed Kubernetes cluster. Normally, this involves a bunch of AWS resources: an EKS cluster, an IAM role for the cluster, a VPC, subnets, security groups, etc. This is complex and error-prone for developers.

With Crossplane, you can define a new, simpler API called XGKECluster (for "eXternal Google Kubernetes Engine Cluster"). This XGKECluster will be a custom resource that your developers can create, just like a Pod or Deployment.

Here’s how it looks in practice. First, you define the XRD:

apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: xgkeclusters.gcp.example.com
spec:
  group: gcp.example.com
  names:
    kind: XGKECluster
    plural: xgkeclusters
  versions:
    - name: v1alpha1
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                parameters:
                  type: object
                  properties:
                    location:
                      type: string
                      description: "The GCP region for the GKE cluster"
                    nodeCount:
                      type: integer
                      description: "Number of nodes in the node pool"
                    nodeMachineType:
                      type: string
                      description: "Machine type for the nodes"
                  required:
                    - location
                    - nodeCount
                    - nodeMachineType
              required:
                - parameters

This XRD tells Crossplane that we’re introducing a new API group gcp.example.com with a kind named XGKECluster. It also defines the spec for this new resource, including parameters like location, nodeCount, and nodeMachineType.

Now, when a developer creates an XGKECluster resource:

apiVersion: gcp.example.com/v1alpha1
kind: XGKECluster
metadata:
  name: my-dev-cluster
spec:
  parameters:
    location: us-central1
    nodeCount: 3
    nodeMachineType: n1-standard-2

This XGKECluster resource doesn’t directly create anything in GCP. Instead, it acts as a blueprint. You then define a "Composition" that tells Crossplane how to fulfill this XGKECluster request using existing managed resources.

Here’s a simplified Composition:

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: gkeclusters.gcp.example.com
  labels:
    provider: gcp
spec:
  compositeTypeRef:
    kind: XGKECluster
    apiVersion: gcp.example.com/v1alpha1
  resources:
    - name: gke-cluster
      base:
        apiVersion: container.gcp.upbound.io/v1beta1
        kind: Cluster
        spec:
          forProvider:

            location: "{{ .spec.parameters.location }}"


            initialNodeCount: "{{ .spec.parameters.nodeCount }}"

            nodeConfig:

              machineType: "{{ .spec.parameters.nodeMachineType }}"

      patches:
        - fromFieldPath: spec.parameters.location
          toFieldPath: spec.forProvider.location
        - fromFieldPath: spec.parameters.nodeCount
          toFieldPath: spec.forProvider.initialNodeCount
        - fromFieldPath: spec.parameters.nodeMachineType
          toFieldPath: spec.forProvider.nodeConfig.machineType

This Composition links the XGKECluster (the "composite") to one or more "managed" resources, in this case, a GCP Cluster resource from the gcp.upbound.io provider. The base block defines the desired state of the managed resource, and the patches section maps the parameters from your custom XGKECluster spec to the fields of the managed GCP Cluster resource.

When you apply both the XRD and the Composition, and then create an XGKECluster resource, Crossplane sees this and automatically creates the underlying GCP Cluster resource (and any other managed resources defined in the Composition, like IAM roles or networking components) according to the rules you’ve set.

The most surprising thing about this pattern is that your custom XGKECluster resource becomes a first-class Kubernetes API object, subject to all the same Kubernetes mechanisms like RBAC, admission controllers, and controllers. You’re not just writing YAML; you’re defining a new API that the Kubernetes control plane understands and enforces.

This allows you to abstract away the complexity of cloud provider resources, providing your developers with a simplified, self-service experience for provisioning infrastructure that conforms to your organization’s standards.

After defining your custom infrastructure APIs, the next logical step is to explore how to manage the lifecycle of these composite resources, including updates and deletions.

Want structured learning?

Take the full Crossplane course →