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.