Command Palette

Search for a command to run...

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.

45 min Pennies (one S3 bucket) 5 steps 1 break-it drills

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/
  • shoplite-platform/
    • addons/
      • crossplane/
        • crossplane-app.yamlnew
        • provider-config.yamlnew
        • providers.yamlnew
    • examples/
      • bucket.yamlnew

Part 3

Build it, step by step

  1. 1

    Install Crossplane

    Chart crossplane from https://charts.crossplane.io/stable, into crossplane-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. 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. A DeploymentRuntimeConfig attaches 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. 3

    Tell providers how to authenticate

    source: PodIdentity uses 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: PodIdentity
    terminal
    $ kubectl get providers
    ── expected output ──
    NAME INSTALLED HEALTHY PACKAGE AGE
    provider-aws-rds True True xpkg.crossplane.io/crossplane-contrib/provider-aws-rds:v2.1.0 3m
    provider-aws-s3 True True xpkg.crossplane.io/crossplane-contrib/provider-aws-s3:v2.1.0 3m
  4. 4

    Create a bucket as YAML

    A managed resource maps one-to-one onto the AWS API. READY and SYNCED conditions show its state; kubectl describe shows 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 AGE
    shoplite-crossplane-demo-7f3a True False shoplite-crossplane-demo-7f3a 4s
    shoplite-crossplane-demo-7f3a True True shoplite-crossplane-demo-7f3a 21s
  5. 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'.

terminal
$ aws s3 ls s3://shoplite-crossplane-demo-7f3a
── what you'll see ──
An error occurred (NoSuchBucket) when calling the ListObjectsV2 operation: The specified bucket does not exist

Part 5

Interview questions from this mission

01

Crossplane vs Terraform?

Before you stop

Clean up

terminal
$ kubectl delete bucket shoplite-crossplane-demo-7f3a
0/4 · 0%