1. The Dependency Problem#
Let’s assume our Developer Platform offers an XNetwork API. When a developer creates a claim, our Composition provisions two resources:
- An AWS VPC.
- An AWS Subnet.
The AWS REST API requires that you provide a vpcId when creating a Subnet.
However, before the XNetwork is deployed, the VPC ID doesn’t exist yet! AWS will generate the vpc-xxxxx string dynamically during boot. How do we pass that unknown ID from the VPC MR down into the Subnet MR?
2. Using Selectors (The Modern Way)#
Crossplane provides a native, elegant solution for this called Selectors.
Selectors allow you to bind resources together using Kubernetes labels, exactly like how a Kubernetes Service binds to a Pod.
Let’s write our XNetwork Composition.
Step 1: Label the Parent Resource (VPC)#
We must ensure that the VPC MR is created with a unique label so the Subnet can find it.
resources:
# 1. The VPC
- name: my-vpc
base:
apiVersion: ec2.aws.upbound.io/v1beta1
kind: VPC
metadata:
labels:
# We assign a hardcoded label to this specific VPC MR
network-name: internal-core
spec:
forProvider:
cidrBlock: 10.0.0.0/16
region: us-east-1Step 2: Configure the Child Resource (Subnet) to Select#
In the Subnet MR, instead of providing a hardcoded vpcId, we provide a vpcIdSelector.
# 2. The Subnet
- name: my-subnet
base:
apiVersion: ec2.aws.upbound.io/v1beta1
kind: Subnet
spec:
forProvider:
cidrBlock: 10.0.1.0/24
region: us-east-1
# Instead of vpcId, we use vpcIdSelector!
vpcIdSelector:
matchLabels:
network-name: internal-coreThe Execution Flow#
Here is exactly how the Crossplane Engine handles this:
- It creates the VPC MR and the Subnet MR simultaneously in the Kubernetes API.
- The AWS Provider tries to provision the Subnet, but realizes
vpcIdSelectoris configured. - The Provider looks across the cluster for a VPC with the label
network-name: internal-core. - It finds the VPC MR.
- It checks if the VPC MR is
READY: True. If it isFalse(because AWS is still booting the VPC), the Subnet Provider safely pauses and waits. - Once the VPC is
READY: True, the Provider automatically extracts the physicalvpc-12345string, injects it into the Subnet, and provisions the Subnet!
You have just orchestrated a strict dependency graph without writing a single line of script.
3. Dealing with Dynamic Labels#
In the example above, we hardcoded network-name: internal-core.
If two developers deploy an XNetwork claim at the exact same time, both claims will spawn VPCs with the exact same label. The Subnet Selector will find both VPCs, get confused, and throw a cannot resolve selector: multiple resources matched error.
To fix this, we must dynamically generate unique labels for every deployment based on the XR’s name.
We use the Patcher to inject the dynamic labels!
- name: my-vpc
base:
apiVersion: ec2.aws.upbound.io/v1beta1
kind: VPC
# (Leave metadata empty in the base)
patches:
# Patch the XR name into the label!
- type: FromCompositeFieldPath
fromFieldPath: metadata.name
toFieldPath: metadata.labels[network-name]
- name: my-subnet
base:
apiVersion: ec2.aws.upbound.io/v1beta1
kind: Subnet
# (Leave vpcIdSelector matchLabels empty in the base)
patches:
# Patch the XR name into the selector!
- type: FromCompositeFieldPath
fromFieldPath: metadata.name
toFieldPath: spec.forProvider.vpcIdSelector.matchLabels[network-name]Now, if a developer creates a claim named auth-network, the VPC is labeled network-name: auth-network, and the Subnet specifically searches for network-name: auth-network. Absolute isolation is guaranteed.
4. Alternative: Cross-Resource Patching (ToComposite -> FromComposite)#
While Selectors are the recommended way to handle parent-child relationships (like VPC -> Subnet), sometimes you need to pass data between two completely unrelated resources in a Composition that do not support Selectors natively.
To do this, you use the XR itself as an “in-memory message bus.”
- Resource A: Uses a
ToCompositeFieldPathpatch to write its generated ID up to a hidden status field on the XR. - Resource B: Uses a
FromCompositeFieldPathpatch to read that ID from the XR status field.
This is technically a race condition. When Crossplane first executes, the XR status field is empty, so Resource B will fail. However, because Crossplane is a continuous loop, it will simply retry Resource B 30 seconds later. By that time, Resource A will have booted and updated the XR status, and Resource B will succeed.
This is the beauty of “Eventually Consistent” architectures.
Troubleshooting & Common Errors#
cannot resolve selector: resource not found- Root Cause: The Subnet is looking for a VPC with specific labels, but no VPC exists with those exact labels.
- Solution:
kubectl describe vpcand verify that the Patches correctly injected the labels onto the parent MR.
The Subnet is stuck in
SYNCED: Falseindefinitely- Root Cause: The VPC is created, and the Selector found it, but the VPC is stuck in a failing state (
READY: False). Crossplane is deliberately refusing to provision the Subnet until the VPC becomes healthy. - Solution: Fix the underlying error on the VPC. Once the VPC goes green, the Subnet will automatically resume provisioning.
- Root Cause: The VPC is created, and the Selector found it, but the VPC is stuck in a failing state (
Conclusion & Next Steps#
You can now build complex, multi-layered architectures. By utilizing Selectors and dynamic patching, you ensure that physical cloud resources boot in the exact correct order without race conditions or manual intervention.
However, as Compositions grow to 10 or 20 resources, debugging them when they fail becomes incredibly difficult. If a developer submits a claim and it stays READY: False for 20 minutes, where do you look? The XR? The MR? The Provider Logs?
In Episode 9: Troubleshooting Crossplane Sync Errors, we will cover the essential commands and debugging strategies every Platform Engineer must know to maintain a production Crossplane cluster.

