> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo Compositions with Crossplane

> Demonstrates building XRDs and Compositions in Crossplane to expose a Database API, mapping user-friendly specs to provider resources using patches and transforms.

[Crossplane](https://crossplane.io/) is where platform engineering becomes practical and powerful. It lets platform teams expose a simple Kubernetes API (for example, a Database resource) and have Crossplane provision and manage the underlying infrastructure on behalf of users. Consumers declare intent; the platform handles the implementation details.

Key concepts:

* XRD (CompositeResourceDefinition) — defines the user-facing API and which fields users can set.
* Composition — implements the XRD by creating provider or Kubernetes resources and applying patches/transforms.

In this lesson we will:

1. Inspect the Crossplane control plane components.
2. Review a Database XRD and its Composition.
3. Create two example Database composite resources and show how inputs are transformed into provider or Kubernetes resources.

## What is running in the control plane

Check the Crossplane controllers, the RBAC manager, the patch-and-transform function and the Kubernetes provider:

```shell theme={null}
controlplane ~ ➜ kubectl get pods -n crossplane-system
NAME                                                         READY   STATUS    RESTARTS   AGE
crossplane-5cb76b766d-zh6fd                                   1/1     Running   0          24m
crossplane-rbac-manager-74494cb9bf-55wgx                      1/1     Running   0          24m
function-patch-and-transform-c05a9add149b-5bf6d6868d-qwbr2    1/1     Running   0          21m
provider-kubernetes-fd54bbc7f877-574d9bdb9-g9dxs               1/1     Running   0          23m
```

List the XRDs available:

```shell theme={null}
controlplane ~ ➜ kubectl get xrd
NAME                            ESTABLISHED   OFFERED   AGE
databases.platform.example.com  True          20m
```

Quick commands (useful references while following this demo):

| Action | Command |
| - | - |
| List Crossplane pods | `kubectl get pods -n crossplane-system` |
| List XRDs | `kubectl get xrd` |
| Describe a composite instance | `kubectl describe database <name> -n <namespace>` |
| Get generated provider/Kubernetes resource (example) | `kubectl get configmap db-config -n <targetNamespace> -o yaml` |

## Inspect the XRD

An XRD (CompositeResourceDefinition) looks similar to a CRD. It defines:

* the composite's API group and kind,
* its scope (Namespaced or Cluster),
* and the openAPI schema for the user-facing `spec`.

Here is the Database XRD used in this demo:

```yaml theme={null}
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  creationTimestamp: "2026-04-13T03:09:00Z"
  finalizers:
  - defined.apiextensions.apiextensions.io
  generation: 1
  name: databases.platform.example.com
  resourceVersion: "5514"
  uid: dff98277-ba0c-4b63-8d5d-8b8a793fc6a1
spec:
  defaultCompositeDeletePolicy: Background
  defaultCompositionUpdatePolicy: Automatic
  group: platform.example.com
  names:
    kind: Database
    plural: databases
  scope: Namespaced
  versions:
  - name: v1
    referenceable: true
    schema:
      openAPIV3Schema:
        properties:
          spec:
            properties:
              engine:
                enum:
                - postgresql
                - mysql
                type: string
              environment:
                type: string
              size:
                enum:
                - small
                - medium
                - large
                type: string
              targetNamespace:
                type: string
            required:
            - engine
            - size
            - environment
            - targetNamespace
            type: object
        type: object
  served: true
status:
  conditions:
  - lastTransitionTime: "2026-04-13T03:09:01Z"
```

The `scope` controls where composite instances live. `Namespaced` means users create Database resources in a namespace (and RBAC applies at namespace level). `Cluster` scope makes them cluster-wide. Choose scope deliberately to match your platform UX and RBAC model.

## Inspect the Composition

Compositions map a composite type (the XRD) to concrete provider or Kubernetes resources. This example uses a pipeline Composition with a function that applies patches and transformations. The Composition is bound to the Database XRD via `compositeTypeRef`.

```yaml theme={null}
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: database-kubernetes
spec:
  compositeTypeRef:
    apiVersion: platform.example.com/v1
    kind: Database
  mode: Pipeline
  pipeline:
  - functionRef:
      name: function-patch-and-transform
    input:
      apiVersion: pt.fn.crossplane.io/v1beta1
      kind: Resources
      resources:
      - name: database-config
        base:
          apiVersion: kubernetes.m.crossplane.io/v1alpha1
          kind: Object
          spec:
            forProvider:
              manifest:
                apiVersion: v1
                kind: ConfigMap
                metadata:
                  name: db-config
                  namespace: default
                data: {}
            providerConfigRef:
              kind: ClusterProviderConfig
              name: default
        patches:
        - fromFieldPath: spec.targetNamespace
          toFieldPath: spec.forProvider.manifest.metadata.namespace
          type: FromCompositeFieldPath
        - fromFieldPath: spec.engine
          toFieldPath: spec.forProvider.manifest.data.engine
          type: FromCompositeFieldPath
        - fromFieldPath: spec.environment
          toFieldPath: spec.forProvider.manifest.data.environment
          type: FromCompositeFieldPath
        - fromFieldPath: spec.size
          toFieldPath: spec.forProvider.manifest.data.instanceClass
          transforms:
          - type: map
            map:
              large: db.r5.large
              medium: db.t3.medium
              small: db.t3.micro
    step: patch-and-transform
```

Why this Composition matters:

* The `base` resource is a Kubernetes Object (here a ConfigMap) which Crossplane will create in the target cluster and namespace.
* `patches` copy fields from the composite `spec` into the provider/Kubernetes resource.
* The `map` transform translates user-friendly choices (`small`, `medium`, `large`) into provider-specific instance classes.

Comparison: XRD vs Composition

| Component | Purpose | Example from demo |
| - | - | - |
| XRD (CompositeResourceDefinition) | Define the user-facing API and schema | `databases.platform.example.com` defining `spec.engine`, `spec.size`, etc. |
| Composition | Implement the XRD by creating provider/Kubernetes resources and applying patches/transforms | `database-kubernetes` creating a `ConfigMap` and mapping values with `map` transform |

We will now demonstrate two user-facing Database resources and observe what Crossplane creates.

## Example 1 — orders-db (postgresql, large)

User creates a Database composite resource named `orders-db`:

```yaml theme={null}
# orders-db.yaml
apiVersion: platform.example.com/v1
kind: Database
metadata:
  name: orders-db
  namespace: default
spec:
  engine: postgresql
  size: large
  environment: prod
  targetNamespace: team-frontend
```

Apply it:

```shell theme={null}
controlplane ~ ➜ kubectl apply -f orders-db.yaml
database.platform.example.com/orders-db created
```

Check the composite instance:

```shell theme={null}
controlplane ~ ➜ kubectl get database orders-db
NAME       SYNCED   READY   COMPOSITION            AGE
orders-db  True     True    database-kubernetes   17s
```

Crossplane used the Composition to create a ConfigMap in the `team-frontend` namespace. Inspect the generated ConfigMap:

```shell theme={null}
controlplane ~ ➜ kubectl get configmap db-config -n team-frontend -o yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: db-config
  namespace: team-frontend
  creationTimestamp: "2026-04-13T03:33:04Z"
  resourceVersion: "9044"
  uid: b53a553a-5c5f-4e8e-a0a7-607ef629f6d3
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: '{"apiVersion":"v1","data":{"engine":"postgresql","environment":"prod","instanceClass":"db.r5.large"},"kind":"ConfigMap","metadata":{"name":"db-config","namespace":"team-frontend"}}'
data:
  engine: postgresql
  environment: prod
  instanceClass: db.r5.large
```

Observation:

* The user specified `size: large`; the Composition mapped that to `instanceClass: db.r5.large` using a `map` transform. The platform handled provider-specific details, so the user didn’t need to know the exact instance class name.

## Example 2 — small-db (mysql, small)

Create a second Database with smaller size and a different target namespace:

```yaml theme={null}
# small-orders-db.yaml
apiVersion: platform.example.com/v1
kind: Database
metadata:
  name: small-db
  namespace: default
spec:
  engine: mysql
  size: small
  environment: dev
  targetNamespace: team-backend
```

Apply it:

```shell theme={null}
controlplane ~ ➜ kubectl apply -f small-orders-db.yaml
database.platform.example.com/small-db created
```

Check the composite and the generated ConfigMap:

```shell theme={null}
controlplane ~ ➜ kubectl get database small-db
NAME       SYNCED   READY   COMPOSITION            AGE
small-db   True     True    database-kubernetes   12s

controlplane ~ ➜ kubectl get configmap db-config -n team-backend -o yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: db-config
  namespace: team-backend
  creationTimestamp: "2026-04-13T03:36:32Z"
  resourceVersion: "9548"
  uid: cae60d0b-b1e7-4548-8db5-34e024fd56b9
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: '{"apiVersion":"v1","data":{"engine":"mysql","environment":"dev","instanceClass":"db.t3.micro"},"kind":"ConfigMap","metadata":{"name":"db-config","namespace":"team-backend"}}'
data:
  engine: mysql
  environment: dev
  instanceClass: db.t3.micro
```

Again, the `size: small` value from the composite was translated to `db.t3.micro` in the generated ConfigMap by the Composition’s `map` transform.

To diagnose Crossplane-managed resources, inspect resource conditions first. Use `kubectl describe` (for example, `kubectl describe database orders-db`) and review the `.status.conditions` fields. `Synced` and `Ready` follow the same operator-style condition patterns used by Kubernetes controllers.

## Summary

* XRDs define the user-facing API and schema; Compositions implement that API by provisioning provider or Kubernetes resources.
* Patches and transforms in Compositions allow platform authors to expose simple, user-friendly fields while mapping them to provider-specific configuration.
* Transform types like `map` let platform authors present meaningful choices (small/medium/large) and translate these to concrete provider settings (`db.t3.micro`, `db.t3.medium`, `db.r5.large`).
* Users declare high-level intent (engine, size, environment, targetNamespace); Crossplane translates that intent into real resources in the correct namespaces with provider-specific values.

Next steps:

* Open the Composition and explore additional transform types (e.g., `string`, `fromCompositeFieldPath`, `math` transforms).
* Try adding or modifying transforms in the Composition to observe how they change the generated provider resources.

## Links and references

* [Crossplane Documentation](https://crossplane.io/docs/)
* [Crossplane Composition transforms reference](https://doc.crds.crossplane.io) (search for transforms and functions)
* [Kubernetes documentation - CustomResourceDefinitions](https://kubernetes.io/docs/concepts/overview/working-with-objects/custom-resources/)

> **[Watch Video](https://learn.kodekloud.com/user/courses/prep-course-certified-cloud-native-platform-engineer-cnpe/module/756ffaae-767b-4743-9724-c05d3fbf9a18/lesson/61b03ce1-88c5-43c4-9da4-a305d870bc02)**

> **[Practice Lab](https://learn.kodekloud.com/user/courses/prep-course-certified-cloud-native-platform-engineer-cnpe/module/756ffaae-767b-4743-9724-c05d3fbf9a18/lesson/07b9a478-b38c-404c-a60d-6626e84a91ce)**


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.