Velocity Stream LogoVelocity Stream Logo
Back to Insights
Platform Engineering

Terraform vs Crossplane: Moving to Kubernetes-Native IaC

An engineering postmortem on migrating a massive multi-cloud infrastructure from Terraform state files to Crossplane's continuous reconciliation loop.

For nearly a decade, Terraform has been the undisputed king of Infrastructure as Code (IaC). But as platform engineering evolves toward self-service developer portals and GitOps, the traditional "plan and apply" CI/CD pipeline model is showing its cracks.

Recently, we helped a rapidly scaling fintech company migrate their core infrastructure provisioning from Terraform to Crossplane. This move fundamentally shifted how their developers requested and managed AWS resources, turning infrastructure into just another Kubernetes manifest.


The Problem with Terraform at Scale

The client had over 300 microservices. Whenever a developer needed a new S3 bucket, an SQS queue, or a Redis cluster, they had to open a PR against a monolithic Terraform repository.

This workflow created massive friction:

  • State File Nightmares: State locks constantly failed in CI during concurrent deployments, forcing manual intervention.
  • Configuration Drift: If someone manually clicked around the AWS Console (ClickOps) during an emergency, Terraform wouldn't know until the next time terraform plan ran, which could be weeks later.
  • Context Switching: Developers had to write HCL (HashiCorp Configuration Language) in one repository, and Kubernetes YAML in another, just to deploy a single app.

Enter Crossplane: The Kubernetes Control Plane

Crossplane extends the Kubernetes API to manage external cloud resources. Instead of running a CLI tool, you apply a standard Kubernetes YAML manifest to the cluster, and Crossplane provisions the AWS resource and continually ensures it matches the desired state.

# Requesting an RDS Database via Crossplane Composite Resource (XRD)
apiVersion: database.example.org/v1alpha1
kind: PostgreSQLInstance
metadata:
  name: prod-payment-db
  namespace: payment-team
spec:
  parameters:
    storageGB: 100
    version: "15"
  compositionSelector:
    matchLabels:
      provider: aws
      environment: production

Engineering Reality

"The biggest mindset shift was understanding continuous reconciliation. With Terraform, drift is fixed only when you run an 'apply'. With Crossplane, if someone deletes the S3 bucket in the AWS console, Crossplane notices within seconds and immediately recreates it to match the YAML in Git."

Self-Service Infrastructure via Compositions

The true power of Crossplane was unlocked using Compositions. We built a Golden Path for developers. Instead of exposing 50 different AWS RDS parameters (subnet groups, security groups, KMS keys), we created a simple custom API (the XRD shown above).

When a developer applied that 10-line YAML file alongside their application Deployment YAML, Crossplane automatically generated the subnets, attached the security groups, provisioned the RDS instance, and injected the database connection string directly into a Kubernetes Secret for the application to consume instantly.

The Outcomes

0
State Locks

By abandoning central state files, concurrent infrastructure provisioning is now handled natively by the Kubernetes etcd consensus.

2 min
Lead Time

Down from a 3-day ticket SLA. Developers self-provision infrastructure safely within guardrails.

Verdict

Terraform is not dead, and it is still the best tool for bootstrapping the initial VPCs, networking, and the EKS clusters themselves. However, for application-dependent infrastructure (buckets, queues, databases), shifting to Crossplane provides a vastly superior developer experience and guarantees iron-clad drift protection through continuous reconciliation.

Is your architecture slowing you down?

We specialize in auditing complex cloud environments, eliminating technical debt, and building pragmatic, high-performance infrastructure. Let's simplify your stack.

Chat with an Engineer