How do you implement environment-specific infrastructure using Crossplane (dev, staging, prod)?
Quick Answer
Environment-specific infrastructure in Crossplane is implemented through a combination of Composition patches that map environment labels to different resource configurations, separate ProviderConfigs for account isolation, and Composition selection labels that route claims to environment-appropriate Compositions with distinct sizing, redundancy, and security settings.
Detailed Answer
Implementing environment-specific infrastructure is one of the most common patterns in Crossplane and touches nearly every aspect of the platform architecture. The goal is to allow the payments-api team to submit the same claim shape in dev, staging, and production, with the underlying infrastructure automatically adapting to each environment's requirements for sizing, redundancy, security, networking, and cost optimization. This pattern eliminates the need for separate Terraform workspaces or CloudFormation stacks per environment, centralizing environment logic in Compositions that the platform team maintains.
The simplest approach uses patch transforms within a single Composition to vary resource properties based on an environment field in the claim. When the checkout-service team sets environment: dev in their database claim, a map transform converts that to db.t3.micro for the instance class, 20 for allocated storage, and false for multi-AZ deployment. The same claim with environment: prod maps to db.r5.xlarge, 200 GB storage, and true for multi-AZ. This approach keeps everything in one Composition file, making it easy to see all environment variations at a glance. String format transforms can generate environment-specific resource names like checkout-service-db-dev and checkout-service-db-prod, preventing naming collisions when dev and staging share an AWS account. Math transforms can apply multipliers, such as giving production three times the IOPS of staging by multiplying a base value by an environment-specific factor.
For organizations with more complex requirements, separate Compositions per environment provide stronger isolation. The XRD defines a compositionSelector field, and three Compositions are created with labels like environment: dev, environment: staging, and environment: prod. When the order-processing-service team submits a claim with compositionSelector matching environment: prod, Crossplane routes it to the production Composition that includes additional resources like CloudWatch alarms, SNS topics for alerting, cross-region read replicas, and automated backup configurations that the dev Composition omits entirely. This pattern gives platform teams fine-grained control over what exists in each environment without relying on conditional patches, and makes it easier to audit the production configuration separately from dev. The tradeoff is maintaining multiple Compositions that can drift from each other, so teams often use a shared library Composition that all environment Compositions reference through Composition Functions.
Account and network isolation between environments is handled through ProviderConfig references in the Composition. The dev and staging environments might share an AWS sandbox account with relaxed IAM policies and cost alerts, while production uses a dedicated account with strict SCPs, GuardDuty enabled, and CloudTrail logging to a separate audit account. The Composition patches the providerConfigRef based on the environment field, routing dev claims to aws-sandbox and prod claims to aws-prod-payments. Network isolation follows the same pattern, where dev resources go into a shared VPC with liberal security groups and prod resources go into a dedicated VPC with private subnets, NAT gateways, and VPC endpoints for AWS service access without traversing the public internet. The inventory-sync service in production might additionally get VPC peering to a data warehouse VPC that does not exist in dev.
Operational practices for environment promotion deserve careful consideration. Teams should never copy claims between environments by changing the environment field manually. Instead, a GitOps workflow stores claims in a Git repository with environment-specific directories or overlays. The user-auth-service team maintains a base claim definition and environment-specific patches using Kustomize overlays, where the dev overlay sets environment: dev with 10 GB storage and the prod overlay sets environment: prod with 500 GB storage and enables deletion protection. ArgoCD or Flux deploys each overlay to the appropriate cluster or namespace, and promotion from staging to production is a pull request that updates the prod overlay values. This workflow provides audit trails, peer review for production changes, and the ability to roll back infrastructure changes through git revert, ensuring that environment-specific configurations are tracked and reproducible.