Mission 2.1 · Stage 2 — Self-Service Infrastructure, Automation, and Measuring the Platform
Crossplane: Cloud Resources as Kubernetes Objects
Goal: Crossplane running in the cluster with the AWS provider, creating and continuously reconciling an S3 bucket declared as YAML in Git.
By the end of this mission
- Explain what Crossplane is and how it compares with Terraform
- Install Crossplane and an AWS provider with workload identity
- Create a managed resource and watch it reconcile drift
Part 1
Understand it first
Crossplane in one paragraph
Crossplane extends Kubernetes so cloud resources become API objects. PROVIDERS (AWS, GCP, Azure, and many more) install CRDs for each cloud resource type (Bucket, Instance, DBInstance) and controllers that create them through the cloud API. A MANAGED RESOURCE is one such object. Like every Kubernetes controller (and like Argo CD), it RECONCILES continuously: if someone changes the bucket in the console, Crossplane puts it back. Combined with GitOps, cloud infrastructure flows through the same pipeline as apps.
Crossplane vs Terraform
TERRAFORM (Terraform course) runs when you run it: plan, apply, done, with state in a file. It's excellent for foundational infrastructure owned by the platform team: VPCs, EKS clusters, accounts, IAM baselines. CROSSPLANE runs all the time inside a cluster, with state in the Kubernetes API, and its killer feature is COMPOSITION (Mission 2.2): the platform team publishes a simple API ('PostgreSQL, size small') that app teams consume, with RBAC, quotas, and policies from Kubernetes. A common split: Terraform for the platform's foundation, Crossplane for self-service resources teams request.
Part 2
Your project after this mission · 4 files change
- shoplite-platform/
- addons/
- crossplane/
- crossplane-app.yamlnew
- provider-config.yamlnew
- providers.yamlnew
- examples/
- bucket.yamlnew
Part 3
Build it, step by step
- 1
Install Crossplane
Chart
crossplanefromhttps://charts.crossplane.io/stable, intocrossplane-system, through Argo CD like every add-on.shoplite-platform/addons/crossplane/crossplane-app.yamlwhole fileyaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: crossplane namespace: argocd annotations: { argocd.argoproj.io/sync-wave: "-5" } spec: project: default source: repoURL: https://charts.crossplane.io/stable chart: crossplane targetRevision: 2.0.2 destination: server: https://kubernetes.default.svc namespace: crossplane-system syncPolicy: automated: { prune: true, selfHeal: true } syncOptions: [CreateNamespace=true] - 2
Install the AWS providers
Providers are split per service family (
provider-aws-s3,provider-aws-rds,provider-aws-ec2) so you only install the CRDs you need; each has hundreds. ADeploymentRuntimeConfigattaches the service account that EKS Pod Identity maps to an IAM role.shoplite-platform/addons/crossplane/providers.yamlwhole fileyaml apiVersion: pkg.crossplane.io/v1beta1 kind: DeploymentRuntimeConfig metadata: { name: aws-irsa } spec: serviceAccountTemplate: metadata: { name: crossplane-provider-aws } --- apiVersion: pkg.crossplane.io/v1 kind: Provider metadata: { name: provider-aws-s3 } spec: package: xpkg.crossplane.io/crossplane-contrib/provider-aws-s3:v2.1.0 runtimeConfigRef: { name: aws-irsa } --- apiVersion: pkg.crossplane.io/v1 kind: Provider metadata: { name: provider-aws-rds } spec: package: xpkg.crossplane.io/crossplane-contrib/provider-aws-rds:v2.1.0 runtimeConfigRef: { name: aws-irsa } - 3
Tell providers how to authenticate
source: PodIdentityuses the pod's AWS identity, with no access keys stored anywhere.shoplite-platform/addons/crossplane/provider-config.yamlwhole fileyaml apiVersion: aws.upbound.io/v1beta1 # for cluster-scoped managed resources kind: ProviderConfig metadata: { name: default } spec: credentials: source: PodIdentity --- apiVersion: aws.m.upbound.io/v1beta1 # for namespaced managed resources (Mission 2.2) kind: ClusterProviderConfig metadata: { name: default } spec: credentials: source: PodIdentityterminal$ kubectl get providers── expected output ──NAME INSTALLED HEALTHY PACKAGE AGEprovider-aws-rds True True xpkg.crossplane.io/crossplane-contrib/provider-aws-rds:v2.1.0 3mprovider-aws-s3 True True xpkg.crossplane.io/crossplane-contrib/provider-aws-s3:v2.1.0 3m - 4
Create a bucket as YAML
A managed resource maps one-to-one onto the AWS API.
READYandSYNCEDconditions show its state;kubectl describeshows API errors.shoplite-platform/examples/bucket.yamlwhole fileyaml apiVersion: s3.aws.upbound.io/v1beta1 kind: Bucket metadata: name: shoplite-crossplane-demo-7f3a spec: forProvider: region: ap-south-1 tags: { owner: team-platform, managed-by: crossplane } providerConfigRef: { name: default }terminal$ kubectl apply -f examples/bucket.yaml && kubectl get bucket -w── expected output ──NAME SYNCED READY EXTERNAL-NAME AGEshoplite-crossplane-demo-7f3a True False shoplite-crossplane-demo-7f3a 4sshoplite-crossplane-demo-7f3a True True shoplite-crossplane-demo-7f3a 21s - 5
Watch drift correction
Change the bucket's tags in the console or CLI. Within the poll interval (default ~10 minutes, configurable), Crossplane restores them from the spec.
terminal$ aws s3api put-bucket-tagging --bucket shoplite-crossplane-demo-7f3a --tagging 'TagSet=[{Key=owner,Value=someone-else}]'# ...after the next reconcile:aws s3api get-bucket-tagging --bucket shoplite-crossplane-demo-7f3a --query 'TagSet[?Key==`owner`].Value' --output text── expected output ──team-platform
Checkpoint — you should now have
- ✓Crossplane and the AWS S3/RDS providers are healthy, installed via Argo CD.
- ✓A Bucket declared in YAML exists in AWS and reverts manual changes.
Part 4
Break it on purpose
Make each change, run the command, and read the error before revealing the diagnosis. Recognising these messages on sight is what makes you fast on a real team. Undo the change afterwards.
Break #1
Delete the object, lose the data
Someone deletes the Bucket object with kubectl delete bucket ... to 'clean up Kubernetes'.
Part 5
Interview questions from this mission
Crossplane vs Terraform?
Before you stop