À la fin de ce tutoriel, vous installerez Crossplane, brancherez le provider AWS, créerez un bucket S3 via une managed resource Kubernetes, puis exposerez une API plateforme (XRD + Composition + Claim) — le tout en ca-central-1, profil lab, sans console AWS pour provisionner.

Niveau : Intermédiaire · Temps estimé : 60–80 min · Versions cibles : Crossplane 2.x / Helm chart stable · provider AWS Upbound (family + S3) · Kubernetes 1.29+ · AWS CLI v2 · Dernière vérification : 2026-09-11 · Region : ca-central-1

Slug : wow-crossplane-aws · Série : WOW Platform / DevOps · Mot-clé SEO : Crossplane AWS Kubernetes

← Connexe : Argo CD GitOps · EKS concepts · CloudFormation premier stack · Hub : Terraform overview

Prérequis

  • Cluster Kubernetes lab (kind, k3d, minikube ou EKS jetable) avec kubectl admin
  • Helm 3 et AWS CLI v2 ; profil lab avec droits S3 (et IAM lecture pour le debug)
  • Notions K8s (CRD, Secret, namespace) et un premier contact IaC (CloudFormation ou Terraform)
  • Budget d’alerte activé — lab quasi gratuit (bucket S3 vide), suppression obligatoire en fin de session
  • Aucun secret réel dans Git : placeholders uniquement
export AWS_PROFILE=lab
export AWS_DEFAULT_REGION=ca-central-1
aws sts get-caller-identity
kubectl cluster-info
helm version

Vérifiez l’identité lab — jamais un compte de production.

Ce que nous allons construire

Lab Crossplane AWS (ca-central-1)
  ├── Namespace crossplane-system + Helm Crossplane
  ├── Provider AWS (family + S3)
  ├── Secret creds + ProviderConfig default
  ├── Managed Resource : Bucket S3 (privé, BPA)
  ├── XRD + Composition → API XBucket / Claim
  ├── Conditions Ready / Synced + events
  └── Nettoyage : Claim → MR → Provider → Helm

(Schéma — alt : « Crossplane provisionne un bucket S3 AWS depuis des manifests Kubernetes ».)

Objectif plateforme : les équipes ne touchent plus la console AWS pour un bucket lab ; elles appliquent un Claim YAML, comme un Deployment. GitOps (Argo CD) peut versionner ces Claims ensuite.

Étape 1 — Pourquoi Crossplane (vs Terraform / CloudFormation)

Crossplane transforme Kubernetes en control plane multi-cloud : des Providers installent des CRD pour les ressources cloud ; Crossplane réconcilie l’état désiré (YAML) avec AWS.

Approche Forces Limites
Console AWS Rapide pour découvrir Non reproductible, drift humain
CloudFormation Natif AWS, stacks, drift Verbose ; hors API K8s
Terraform / OpenTofu Écosystème mature, plan Processus CLI/état séparé du cluster
Crossplane Même API que les apps (kubectl) ; Compositions = API plateforme Courbe CRD ; credentials cluster-scoped

Quand Crossplane brille : vous avez déjà Kubernetes, vous voulez une API interne (« crée-moi un datastore ») et GitOps unifié apps + infra. Terraform reste excellent pour le landing zone ; Crossplane complète pour le self-service dans le cluster.

Étape 2 — Installer Crossplane avec Helm

helm repo add crossplane-stable https://charts.crossplane.io/stable
helm repo update

helm upgrade --install crossplane crossplane-stable/crossplane 
  --namespace crossplane-system 
  --create-namespace 
  --wait

kubectl -n crossplane-system get pods
kubectl get crd | grep crossplane || true

Attendu : pods Crossplane Running/Ready. Les CRD providers.pkg.crossplane.io, compositions…, compositeresourcedefinitions… apparaissent.

Prod : pinnez une version de chart (--version X.Y.Z), RBAC restreint, réseau egress contrôlé vers l’API AWS, et préférer IRSA / Pod Identity plutôt que des access keys longues.

Étape 3 — Provider AWS et credentials

Installez le provider (family AWS + paquet S3 — adaptez le tag à une version healthy récente) :

# provider-aws-s3.yaml
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-aws-s3
spec:
  package: xpkg.upbound.io/upbound/provider-aws-s3:v1.21.0
kubectl apply -f provider-aws-s3.yaml
kubectl wait provider/provider-aws-s3 --for=condition=Healthy --timeout=300s
kubectl get providers

Créez un fichier credentials local (hors Git) au format INI AWS :

[default]
aws_access_key_id = REPLACE_ME
aws_secret_access_key = REPLACE_ME
kubectl -n crossplane-system create secret generic aws-secret 
  --from-file=creds=./aws-credentials.txt

# ProviderConfig (Upbound AWS)
cat <<'YAML' | kubectl apply -f -
apiVersion: aws.upbound.io/v1beta1
kind: ProviderConfig
metadata:
  name: default
spec:
  credentials:
    source: Secret
    secretRef:
      namespace: crossplane-system
      name: aws-secret
      key: creds
YAML

Least privilege lab : politique IAM limitée à s3:CreateBucket, s3:DeleteBucket, s3:PutBucket*, s3:GetBucket*, tags. En EKS prod, basculez vers IRSA ou Pod Identity (credentials.source: IRSA / PodIdentity).

Étape 4 — Premier bucket S3 (managed resource)

Nom de bucket globalement unique — suffixez un random :

SUFFIX=$(openssl rand -hex 4)
echo "SUFFIX=$SUFFIX"
# bucket.yaml — region ca-central-1
apiVersion: s3.aws.upbound.io/v1beta1
kind: Bucket
metadata:
  name: lab-xp-bucket
  labels:
    app.kubernetes.io/part-of: wow-crossplane-aws
spec:
  forProvider:
    region: ca-central-1
    tags:
      Environment: lab
      ManagedBy: crossplane
      Project: devops-elastic-hayway
  providerConfigRef:
    name: default
kubectl apply -f bucket.yaml
kubectl describe bucket.s3.aws.upbound.io lab-xp-bucket
kubectl get bucket.s3.aws.upbound.io lab-xp-bucket -o yaml | sed -n '/status:/,$p'

Attendu : conditions Synced=True, Ready=True. Vérification AWS :

aws s3api list-buckets --query "Buckets[?starts_with(Name, 'lab-xp')]" --output table
# ou récupérez le nom physique via status.atProvider

Bloquez le public dès la Composition (étape suivante) ; ici le lab minimal prouve le chemin control plane → AWS.

Étape 5 — API plateforme : XRD, Composition, Claim

Les équipes ne devraient pas manipuler les CRD bas niveau Bucket. Vous exposez une API métier.

1. XRD (CompositeResourceDefinition) — API XBucket / claim BucketClaim :

# xrd-xbucket.yaml (extrait conceptuel)
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: xbuckets.platform.deh.local
spec:
  group: platform.deh.local
  names:
    kind: XBucket
    plural: xbuckets
  claimNames:
    kind: BucketClaim
    plural: bucketclaims
  versions:
  - name: v1alpha1
    served: true
    referenceable: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              parameters:
                type: object
                properties:
                  region:
                    type: string
                    default: ca-central-1
                  environment:
                    type: string
                    default: lab
                required: [region]
            required: [parameters]

2. Composition — mappe le claim vers un Bucket Upbound + tags + region :

# composition-xbucket.yaml (idée)
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: xbuckets.aws.s3
  labels:
    provider: aws
spec:
  compositeTypeRef:
    apiVersion: platform.deh.local/v1alpha1
    kind: XBucket
  resources:
  - name: s3-bucket
    base:
      apiVersion: s3.aws.upbound.io/v1beta1
      kind: Bucket
      spec:
        forProvider:
          region: ca-central-1
          tags:
            ManagedBy: crossplane
        providerConfigRef:
          name: default
    patches:
    - type: FromCompositeFieldPath
      fromFieldPath: spec.parameters.region
      toFieldPath: spec.forProvider.region
    - type: FromCompositeFieldPath
      fromFieldPath: spec.parameters.environment
      toFieldPath: spec.forProvider.tags.Environment

3. Claim (ce que le développeur applique) :

apiVersion: platform.deh.local/v1alpha1
kind: BucketClaim
metadata:
  name: app-assets
  namespace: default
spec:
  parameters:
    region: ca-central-1
    environment: lab
kubectl apply -f xrd-xbucket.yaml
kubectl apply -f composition-xbucket.yaml
kubectl apply -f claim-app-assets.yaml
kubectl get bucketclaim,xbucket,bucket.s3.aws.upbound.io

C’est le cœur platform engineering : une API interne versionnée, des garde-fous (region forcée ca-central-1, tags obligatoires), et GitOps possible avec Argo CD.

Étape 6 — Day-2 : Ready, Synced, events

kubectl get bucket.s3.aws.upbound.io -o wide
kubectl describe bucket.s3.aws.upbound.io lab-xp-bucket | sed -n '/Conditions/,/Events/p'
kubectl -n crossplane-system logs deploy/crossplane --tail=80
Condition Signification
Synced Le desired state a été poussé / lu sans erreur API
Ready La ressource externe est utilisable

Drift manuel (console AWS) : Crossplane tente de reconcilier selon la politique du provider. En prod, couplez avec policy-as-code (OPA/Gatekeeper) et scans IaC (Trivy) dans le CI.

Nettoyage (obligatoire)

Ordre : Claims / composites → managed resources → ProviderConfig / Secret → Provider → Helm.

kubectl delete bucketclaim app-assets --ignore-not-found
kubectl delete bucket.s3.aws.upbound.io lab-xp-bucket --wait=true
# Attendez la disparition AWS avant de casser les creds
aws s3api list-buckets --query "Buckets[].Name" --output text | tr 't' 'n' | grep lab-xp || true

kubectl delete providerconfig.aws.upbound.io default --ignore-not-found
kubectl -n crossplane-system delete secret aws-secret --ignore-not-found
kubectl delete provider provider-aws-s3 --ignore-not-found
helm uninstall crossplane -n crossplane-system
kubectl delete namespace crossplane-system --ignore-not-found

Sur un cluster jetable (kind), détruire le cluster suffit — vérifiez tout de même qu’aucun bucket lab ne traîne dans ca-central-1.

Coûts du lab

Ressource Ordre de grandeur
Bucket S3 vide ≈ 0 USD (requêtes négligeables)
Control plane Crossplane Coût nœuds K8s lab seulement
EKS (si utilisé) ≈ 0,10 USD/h control plane — delete vite

Activez une alerte budget AWS ; ne laissez jamais un EKS « oublié » overnight.

Erreurs fréquentes

Symptôme Cause Correction
Provider pas Healthy Image / registry / DNS kubectl describe provider ; retry ; pin version
Bucket Synced=False Creds / région / nom pris Relire events ; Secret creds ; suffixe unique
AccessDenied S3 IAM trop strict Ajouter actions bucket ; vérifier compte lab
Claim sans Composition XRD/Composition mal liés compositionSelector / compositionRef ; labels
Bucket orphelin AWS Delete MR trop tôt / finalizer Attendre Ready delete ; delete bucket CLI en dernier recours lab

FAQ

Crossplane remplace-t-il Terraform ?
Non. Terraform (ou OpenTofu) reste fort pour le bootstrap et les grands modules partagés. Crossplane excelle quand l’infra doit être une API Kubernetes self-service, surtout avec GitOps.

Faut-il EKS pour ce lab ?
Non. kind/k3d suffisent pour apprendre CRD + provider. EKS devient pertinent pour IRSA/Pod Identity et un vrai réseau AWS.

Où stocker les credentials ?
Secret Kubernetes hors Git en lab. En prod : IRSA, Pod Identity, ou vault externe (External Secrets / Vault) — jamais d’access key longue dans un repo.

Comment versionner ça avec Argo CD ?
Mettez XRD, Composition, Claims (et éventuellement Providers) dans Git ; Argo CD sync le path. Les managed resources suivent alors le même flux que les Deployments.

Quelle région utiliser ?
Ce parcours force ca-central-1 (Canada). Alignez tags, budgets et data residency sur la même région.

XRD vs Composition ?
Le XRD définit l’API (schéma Claim). La Composition définit comment réaliser cette API (ressources AWS concrètes + patches).

Quiz (3 questions)

1. Qu’est-ce qu’une managed resource Crossplane ? – A. Un Pod système uniquement
– B. Un CR Kubernetes réconcilié vers une ressource cloud (ex. Bucket S3)
– C. Un fichier Terraform state

2. À quoi sert une Composition ? – A. Remplacer etcd
– B. Mapper une API XRD/Claim vers des ressources cloud concrètes
– C. Facturer AWS automatiquement

3. Pourquoi préférer IRSA/Pod Identity en prod EKS ? – A. C’est plus joli dans la console
– B. Éviter les access keys longues dans le cluster ; identité liée au ServiceAccount
– C. Crossplane refuse les Secrets

Réponses : 1‑B · 2‑B · 3‑B

Pour aller plus loin

Maillage WOW / Platform

Série WOW ×50 — Platform Engineering & GitOps
Voisins wow-platform-engineering-2026 · wow-argo-cd-gitops · wow-pulumi-vs-terraform · wow-backstage-idp
Hubs live AWS · Terraform · Kubernetes (menus du site)

Meta publication (SEO) — Publish HOLD

  • Title SEO : Crossplane AWS : infra comme Kubernetes (lab 2026)
  • Meta description (≤ 160) : Installez Crossplane, branchez le provider AWS et créez un bucket S3 via kubectl en ca-central-1. Lab XRD, Composition et Claim pour une API plateforme.
  • Image mise en avant : assets/web/devopelastichayway/cover-wow-crossplane-aws-1200x630.webp
  • Cover alt : Crossplane provisionne l’infra AWS depuis Kubernetes
  • Catégorie : DevOps / Platform · Niveau : Intermédiaire
  • KW principal : Crossplane AWS Kubernetes · Secondaires : Crossplane S3, provider AWS Crossplane, XRD Composition Claim, infra as Kubernetes
  • Schema : HowTo + FAQPage + Article
  • URL cible : https://devopelastichayway.com/tutoriels/wow-crossplane-aws/
  • Statut : Publish HOLD — draft only, pas de Rank Math live sans feu vert
  • Region lab : ca-central-1

← Catalogue Tutoriels

Pour aller plus loin — hubs live

Retour parcours Catalogue Tutoriels — hub de la série et leçons sœurs.