Skip to main content

IaC — Crossplane

Architecture

platform-gitops / management cluster Cloud Providers
──────────────────────────────────────────────────────────────────────
crossplane/providers/ → Provider CRDs installed GCP / AWS / Azure / IBM
crossplane/xrds/ → XRD: XGKECluster registered (defines the API)
crossplane/compositions/ → Composition: gke-cluster-gcp (implements the API)

<domain>-gitops / management cluster Cloud
──────────────────────────────────────────────────────────────────────
crossplane/claims/prod/gke-main.yaml
→ Claim: GKECluster in payments-prod-infra
→ Composite (XR) created by Crossplane
→ GKE cluster provisioned in GCP

Infra Namespace

One namespace per domain per env, separate from application namespaces:

apiVersion: v1
kind: Namespace
metadata:
name: payments-prod-infra
labels:
project: payments
env: prod
purpose: infra
team: team-payments
backstage.io/domain: payments
backstage.io/managed-by: crossplane
argocd/app-set: crossplane-payments

Claim Example

# payments-gitops/crossplane/claims/prod/cloudsql-main.yaml
apiVersion: platform.myorg.io/v1alpha1
kind: CloudSQLInstance
metadata:
name: cloudsql-main # {resourceType}-{name}
namespace: payments-prod-infra # {domain}-{env}-infra
labels:
project: payments
env: prod
resource-type: cloudsql
provider: gcp
team: team-payments
backstage.io/system: gateway
backstage.io/resource: gcp-payments-prod-cloudsql-main
argocd/app: crossplane-payments-prod-cloudsql-main
argocd/app-set: crossplane-payments
annotations:
platform.myorg/backstage-entity: gcp-payments-prod-cloudsql-main
platform.myorg/ownership-level: system
spec:
parameters:
domain: payments
env: prod
project: payments-prod
location: us-central1
engine: POSTGRES_15
tier: db-custom-2-7680
availabilityType: REGIONAL
backupRetentionDays: 30
deletionPolicy: Orphan # never auto-delete prod resources
writeConnectionSecretToRef:
name: gcp-payments-prod-cloudsql-main-conn
namespace: payments-prod-infra
compositionSelector:
matchLabels:
provider: gcp
resource-type: cloudsql

Ownership Levels

LevelClaim ownsUse case
domainbackstage.io/domain onlyCluster, VPC, KMS — shared by all systems
systembackstage.io/systemShared DB, message bus — shared within a system
componentbackstage.io/system + Component dependsOnPer-service queue, cache

Supported Providers and Resource Types

Type slugBackstage spec.typeGCPAWSAzureIBM
gke/eks/aks/ikskubernetes-cluster
cloudsql/rds/postgres/cosmosdatabase
pubsub/sqs/servicebus/eventstreamsmessage-queue
gcs/s3/blobstorage/cosobject-storage
memorystore/elasticache/rediscache
bigquery/dynamodbdata-store
artifact-registry/ecr/acrcontainer-registry
secretmanager/keyvault/secrets-managersecret-store

Provider Setup

# platform-gitops/crossplane/providers/provider-gcp.yaml
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-gcp
annotations:
argocd.argoproj.io/sync-wave: "-10" # before Claims
spec:
package: xpkg.upbound.io/upbound/provider-gcp:v0.41.0
---
apiVersion: gcp.upbound.io/v1beta1
kind: ProviderConfig
metadata:
name: gcp-provider-config
spec:
projectID: myorg-platform
credentials:
source: Secret
secretRef:
namespace: crossplane-system
name: gcp-credentials
key: credentials.json

Sync Wave Order

Wave -10: Providers + XRDs + Compositions → crossplane-platform AppSet
Wave 0: Claims → crossplane-<domain> AppSets
Wave 10: App workloads (optional) → domain ApplicationSets

Crossplane vs Terraform

ConcernTerraformCrossplane
Resource definition.tf files + modulesXRD + Composition
Resource instanceterraform apply pipelineClaim (Kubernetes object)
State.tfstate in S3/GCSetcd (Kubernetes)
ReconciliationPipeline on pushContinuous controller loop
Drift correctionterraform plan in CICrossplane selfHeal: true
Secret outputTerraform outputKubernetes Secret in infra namespace