TL;DR
Terraform dominated Infrastructure as Code for a decade, but it’s showing its age. State files lock you out. Monolithic applies slow you down. Drift happens and you only find out when you remember to run a plan. Enter Crossplane: a Kubernetes-native approach that treats your cloud infrastructure like any other K8s resource. Continuous reconciliation, parallel operations, built-in policy enforcement, and no external state files. If you’re already running Kubernetes, Crossplane might be the IaC evolution you didn’t know you needed.
🔥 Introduction
Picture this: it’s 3 a.m., you’re staring at a Terraform state lock that won’t release, and your deploy is blocked. Someone on the team ran an apply, went to get coffee, and now the whole pipeline is frozen. Sound familiar?
Or maybe you’ve been there: you carefully craft a Terraform config to update one tiny database parameter. You run the plan and Terraform decides to recalculate the entire world. Ten minutes later, you’re still waiting for a plan that touches 47 resources when you only wanted to change one.
Infrastructure as Code promised us automation, consistency, and peace of mind. Terraform delivered on a lot of that. But after a decade of Terraform being the default answer to „how do we manage cloud stuff,“ some cracks are starting to show. The monolithic state file. The sequential apply process. The fact that drift only gets caught when you remember to check for it.
What if there was a different way? What if your infrastructure could be managed the same way you manage your Kubernetes workloads? Always reconciling. Always watching. No state files to wrestle with. Just declare what you want and let the system make it happen.
That’s what Crossplane offers. And if you’re already living in a Kubernetes world, it’s worth understanding why this shift might matter.
Why Infrastructure as Code Exists in the First Place
Let’s start with the basics. Modern cloud infrastructure is a mess of services, configurations, and dependencies. Clicking through a console to spin up resources is fine when you’re tinkering. It’s a disaster when you’re trying to run production at scale.
Infrastructure as Code emerged to bring sanity to the chaos. Instead of manual point-and-click adventures, you write code that describes what you want. Then you run a tool that makes it real. The benefits are obvious: version control, peer review, repeatability, and a clear audit trail of who changed what and when.[1]
IaC tools act as a wrapper around cloud provider APIs. You tell the tool „I want an S3 bucket with these settings“ and it translates that into the right sequence of REST API calls. This abstraction layer means you can define once and apply consistently. You can spin up identical environments. You can tear them down and rebuild them. You can track changes over time.
Without IaC, you get snowflake servers. Manual tweaks that nobody remembers. Configuration drift that sneaks in over months. And when something breaks, good luck figuring out what state things are actually in.
So IaC isn’t just nice to have. It’s essential when you’re managing anything beyond a handful of servers. The question isn’t whether you need IaC. The question is which approach works best.
graph LR
A[Manual Console Clicks] -->|Slow, Error-Prone| B[Snowflake Servers]
B -->|Configuration Drift| C[Unknown State]
D[Infrastructure as Code] -->|Automated, Repeatable| E[Consistent Environments]
E -->|Version Controlled| F[Audit Trail & Rollbacks]
style A fill:#ff6b6b
style B fill:#ff6b6b
style C fill:#ff6b6b
style D fill:#51cf66
style E fill:#51cf66
style F fill:#51cf66
The Terraform Era
HashiCorp’s Terraform became the gold standard for IaC over the past decade. And for good reason.
Terraform introduced a simple, declarative language called HCL that let you describe infrastructure in a way that felt natural. You’d write a .tf file defining an AWS instance or a GCS bucket, run terraform plan to preview changes, then terraform apply to make it happen. Clean. Repeatable. Understandable.
The real power was Terraform’s provider ecosystem. Need to manage AWS? There’s a provider. Azure? Covered. Google Cloud? Yep. GitHub repos? DNS records? Random third-party SaaS APIs? All handled by community-maintained providers. HashiCorp and the Terraform community have written over 1,000 providers to manage resources across multiple platforms.[2] You could standardize on one tool and one workflow for almost everything.
This consistency was huge for teams. Instead of learning five different tools for five different cloud services, you learned Terraform. You wrote HCL. You committed it to Git. You integrated it into your CI pipeline. Done.
Terraform also introduced the concept of a state file, a JSON blob that tracks what Terraform thinks your infrastructure looks like. This state file becomes the source of truth. When you run a plan, Terraform compares the desired state in your .tf files against the actual state recorded in the state file, calculates the diff, and shows you exactly what will change.
For years, this model worked beautifully. Terraform became the default choice for multi-cloud provisioning. Teams built entire deployment workflows around it. The HashiCorp ecosystem flourished.
But as organizations scaled up, as teams grew larger, and as infrastructure grew more complex, some limitations started to surface.
flowchart TD
A[Write .tf Files] --> B[terraform init]
B --> C[terraform plan]
C --> D{Review Changes}
D -->|Approve| E[terraform apply]
D -->|Reject| A
E --> F[Update State File]
F --> G[Call Cloud APIs]
G --> H[Infrastructure Provisioned]
I[(terraform.tfstate)] -.->|Read Current State| C
F -.->|Write New State| I
style A fill:#e3f2fd
style E fill:#51cf66
style I fill:#fff3bf
style H fill:#51cf66
Where Terraform Starts to Hurt
Terraform is great until it isn’t. Here are the pain points that started showing up as teams pushed the tool harder.
Another Language to Learn
Terraform uses HCL, a custom domain-specific language. It’s declarative and fairly readable, but it’s still another thing to learn. Loops and conditionals exist but they’re awkward. Want to do something programmatic? Good luck. You end up writing hacky workarounds or generating HCL with external scripts.
Meanwhile, the rest of the cloud-native world was standardizing on YAML or using real programming languages. This gap led to tools like Pulumi trying to solve the „let me write IaC in Python or TypeScript“ problem. But more on that later.
State File Hell
Terraform’s state file is a single source of truth. Which sounds good until you realize it’s also a single point of contention.
Only one person can modify the state at a time. Terraform locks the state file during an apply to prevent concurrent changes.[3] If someone is running an apply, everyone else is blocked. And if that apply takes ten minutes because your infrastructure is huge? Ten minutes of everyone else waiting. According to HashiCorp surveys, over 50% of Terraform users have encountered state-related issues.[4]
In practice, teams split large Terraform configs into smaller modules to reduce lock contention. But now you’ve got a different problem: managing dependencies between modules. You end up with a sprawl of state files and a complex web of outputs and inputs to wire them together.
Oh, and if your state file gets corrupted or out of sync with reality? You’re in for a bad time. Terraform’s diff engine depends on accurate state. When the state is wrong, Terraform makes bad decisions. I’ve seen teams spend days untangling state issues after someone accidentally deleted a resource outside of Terraform.
sequenceDiagram
participant Dev1 as Developer 1
participant Dev2 as Developer 2
participant Dev3 as Developer 3
participant State as State File (Locked)
Dev1->>State: terraform apply (acquires lock)
activate State
Note over State: Lock held by Dev1
Dev2->>State: terraform apply
State-->>Dev2: ❌ Error: State locked
Dev3->>State: terraform plan
State-->>Dev3: ❌ Error: State locked
Note over Dev2,Dev3: Waiting... ⏳
Note over Dev1: 10 minutes later...
Dev1->>State: Apply complete (releases lock)
deactivate State
Dev2->>State: terraform apply (acquires lock)
Monolithic, Sequential Applies
Terraform calculates a dependency graph and applies changes according to that graph. Sounds smart. And it is, to a point.
But here’s the thing: even if you only want to change one resource, Terraform has to evaluate the entire configuration. If your config defines 50 resources, Terraform will plan all 50. Sure, it might only propose changes to one, but you still wait for the whole plan.
And during an apply, Terraform is fundamentally sequential. It will parallelize independent resources when possible, but the overall process is one big transaction. There’s no clean way to say „just update this one thing and leave everything else alone.“ You can use -target to focus on specific resources, but that’s a workaround, not a feature.
This becomes a real bottleneck when you want to make small, isolated changes quickly. You can’t just tweak one database setting without Terraform recalculating the world.
Drift Happens When You’re Not Looking
Terraform only checks for drift when you tell it to. It’s a CLI tool. You run it, it does its thing, then it exits. It’s not continuously watching your infrastructure.
So if someone makes an emergency change directly in the AWS console at 2 a.m. (we’ve all been there), Terraform has no idea. The next time you run a plan, you might get a surprise. Terraform will see the drift and try to revert it back to the desired state. Which might be exactly what you want. Or it might undo a critical hotfix.
There’s no built-in reconciliation loop. No continuous enforcement of desired state. You have to manually run Terraform to detect and correct drift. And if you don’t run it often enough, your infrastructure and your Terraform config slowly diverge. Eventually, you hit a state where applying Terraform changes feels risky because you’re not entirely sure what it will do.
gantt
title Terraform Drift Detection Gap
dateFormat HH:mm
axisFormat %H:%M
section Terraform
terraform apply :done, 00:00, 5m
Terraform idle :crit, 00:05, 4h55m
terraform plan (drift!) :active, 05:00, 5m
section Infrastructure
Resources in sync :done, 00:00, 2h
Manual change (2am fix) :crit, 02:00, 1m
Drifted state :crit, 02:01, 2h59m
Drift detected :active, 05:00, 1m
No Native API or Integration Layer
Terraform is a standalone binary. You run it from the command line or from a CI job. There’s no REST API. No webhook support. No native way to build higher-level automation on top of it.
If you want to integrate Terraform into a larger platform or enforce policies, you need external tooling. HashiCorp Sentinel for policy as code. Terraform Cloud for remote state and collaboration features. Third-party tools to trigger runs based on events.
It all works, but it’s not seamless. You’re stitching together multiple systems to get what you want.
A Quick Detour: Pulumi
Before we dive into Crossplane, let’s talk about Pulumi. It’s worth mentioning because it tries to solve some of Terraform’s pain points without completely rethinking the model.
Pulumi’s big idea: let you write infrastructure code in real programming languages. Python, TypeScript, Go, C#, whatever you’re comfortable with. No more HCL. No more awkward loops. Just normal code.
This is genuinely nice. You can use functions, classes, libraries, and all the tooling you already know. You can unit test your infrastructure logic. You can abstract complex patterns into reusable modules with real language constructs.
But here’s the catch: Pulumi still follows the Terraform execution model. You run a CLI command. It calculates a plan. It applies changes. It stores state (either in Pulumi’s SaaS backend or in your own storage). It’s still a one-shot tool, not a continuous controller.
So you get a better developer experience for writing IaC, but you don’t escape the monolithic apply problem or the lack of continuous drift correction. Pulumi is „Terraform with a nicer language,“ which is valuable, but it doesn’t fundamentally change the paradigm.
flowchart LR
subgraph Terraform
A1[Write HCL] --> B1[terraform plan]
B1 --> C1[terraform apply]
C1 --> D1[(State File)]
end
subgraph Pulumi
A2[Write Python/TS/Go] --> B2[pulumi preview]
B2 --> C2[pulumi up]
C2 --> D2[(State Backend)]
end
style A2 fill:#51cf66
style A1 fill:#fff3bf
style D1 fill:#fff3bf
style D2 fill:#fff3bf
That’s where Crossplane comes in.
Crossplane: Kubernetes as Your Infrastructure Control Plane
Crossplane flips the script. Instead of a CLI tool you run occasionally, Crossplane is a control plane that runs continuously inside a Kubernetes cluster.[5]
The core idea: extend Kubernetes with custom resources that represent cloud infrastructure. Want an S3 bucket? Define it as a Kubernetes custom resource. Want an RDS database? Same thing. Crossplane controllers watch these resources and reconcile them against the actual cloud state, just like Kubernetes reconciles Pods and Services.[6]
This might sound weird at first. Why would you use Kubernetes to manage cloud resources? Isn’t that mixing concerns?
But think about what Kubernetes is really good at: declarative configuration, continuous reconciliation, API-driven automation, and extensibility. These are exactly the things IaC needs. Crossplane borrows Kubernetes‘ strengths and applies them to infrastructure.
Here’s what that gets you.
Continuous Reconciliation and Drift Correction
Crossplane controllers run in a loop. Always watching. Always reconciling. If you declare that a database should exist with certain settings, Crossplane makes sure it exists. Not just once. Continuously.
If someone manually changes the database in the cloud console, Crossplane detects the drift on the next reconciliation loop (usually within seconds or minutes) and reverts the change. Your desired state is enforced in near-real-time.
This is a fundamental shift. Terraform is a short-lived, one-shot process that only attempts to reconcile desired configuration with actual infrastructure when invoked.[7] Crossplane enforces state all the time. Drift becomes much harder to ignore because the system actively fights it.
For teams that care about compliance and consistency, this is huge. You define the policy once, and Crossplane makes sure it stays true.
flowchart TD
A[Crossplane Controller] --> B{Check Desired State}
B --> C{Compare with Actual State}
C -->|Match| D[Sleep 30s]
C -->|Drift Detected| E[Reconcile]
E --> F[Call Cloud API]
F --> G[Update Resource]
G --> H[Update Status]
H --> D
D --> A
I[Manual Change in Console] -.->|Causes Drift| C
J[kubectl apply] -.->|Updates Desired State| B
style A fill:#e3f2fd
style C fill:#fff3bf
style E fill:#ffd43b
style G fill:#51cf66
style I fill:#ff6b6b
No External State File
With Crossplane, there’s no separate state file to manage. The Kubernetes API server (backed by etcd) is the source of truth. Desired state lives in custom resource definitions. Actual state is reflected in the resource status fields.
No state locking issues. No corrupted state files. No remote backend configuration to worry about. If you have a highly available Kubernetes cluster, your „state“ is as durable and available as your cluster itself.
Want to see what resources exist? Just query the Kubernetes API with kubectl get <resource>. It’s all there.
Parallel, Independent Operations
Each Crossplane resource is an independent Kubernetes object. The system doesn’t need to calculate a global dependency graph for every operation. Resources reconcile in parallel whenever possible.
Want to update one database? Just edit that one resource with kubectl apply. Crossplane will reconcile that change without touching anything else. No monolithic plan. No global lock. Just targeted updates.
This makes small changes fast. It makes concurrent operations possible. Multiple teams can work on different resources at the same time without blocking each other.
flowchart LR
subgraph Terraform Sequential
T1[Resource 1] --> T2[Resource 2]
T2 --> T3[Resource 3]
T3 --> T4[Resource 4]
end
subgraph Crossplane Parallel
C1[Resource 1]
C2[Resource 2]
C3[Resource 3]
C4[Resource 4]
end
style T1 fill:#ffd43b
style T2 fill:#ffd43b
style T3 fill:#ffd43b
style T4 fill:#ffd43b
style C1 fill:#51cf66
style C2 fill:#51cf66
style C3 fill:#51cf66
style C4 fill:#51cf66
Kubernetes-Native Everything
Crossplane resources are defined in YAML, just like any Kubernetes object. If you know how to write a Deployment or Service manifest, you already know how to write Crossplane infrastructure definitions.
You can use Helm to template them. Kustomize to customize them. GitOps tools like Argo CD or Flux to continuously apply them. All the Kubernetes ecosystem tooling just works.
And because everything is a Kubernetes API object, you can use Kubernetes RBAC to control who can create or modify resources. You can use admission controllers to enforce policies. You can use standard Kubernetes monitoring and logging tools to track what’s happening.
It all fits together naturally if you’re already living in the Kubernetes world.
Policy, RBAC, and Self-Service
Here’s where Crossplane gets really interesting for platform teams.
Policy Enforcement via Admission Controllers
In Kubernetes, you can use admission webhooks to validate or mutate objects before they’re saved. Tools like Kyverno or OPA/Gatekeeper let you define policies as code.[8]
With Crossplane, this means you can enforce infrastructure policies centrally. Every database must be in a private subnet. Every S3 bucket must have encryption enabled. No resources allowed in unapproved regions. These rules apply automatically to any Crossplane resource someone tries to create.
If a developer tries to spin up a database without encryption, the admission controller rejects it before it ever hits the cloud. The system enforces compliance at the API level. No manual code review needed.
This is way harder with Terraform, where policy enforcement typically requires external tools or CI pipeline checks. With Crossplane, it’s baked into the platform.
sequenceDiagram
participant Dev as Developer
participant API as K8s API Server
participant AC as Admission Controller
participant Policy as Policy Engine (Kyverno/OPA)
participant CP as Crossplane
Dev->>API: kubectl apply database.yaml
API->>AC: Validate resource
AC->>Policy: Check policies
alt Policy Violation (No Encryption)
Policy-->>AC: ❌ DENY: Encryption required
AC-->>API: Reject
API-->>Dev: Error: Policy violation
else Policy Passed
Policy-->>AC: ✅ ALLOW
AC-->>API: Accept
API->>CP: Create Database resource
CP->>CP: Reconcile
CP-->>Dev: Database created ✅
end
RBAC and Multi-Tenancy
Kubernetes has a mature RBAC system. You can use it to control who can create or edit Crossplane resources.
For example, you might allow developers to create „Database“ custom resources in their namespace, but not give them direct access to the underlying cloud credentials or network configurations. The platform team encapsulates the complexity in higher-level abstractions, and developers get a self-service API for the resources they need.
This is the dream of internal platform engineering: devs can provision what they need without waiting for ops, and ops can sleep soundly knowing that devs are constrained by policies and RBAC.
GitOps for Infrastructure
Because Crossplane resources are Kubernetes manifests, you can treat infrastructure the same way you treat application deployments. Store the YAML in Git. Use a GitOps operator like Argo CD or Flux to continuously apply changes.[9] Every infrastructure change goes through a pull request. Full audit trail. Easy rollbacks.
Terraform can be used in a GitOps-ish workflow, but it’s not as natural. With Crossplane, it’s the default mode of operation.
Integrating with Application Workloads
When your apps run in Kubernetes and your infrastructure is managed by Crossplane, they can talk to each other seamlessly.
Crossplane can automatically publish connection details for provisioned resources as Kubernetes Secrets. For example, when Crossplane creates a database, it can drop a Secret in your app’s namespace containing the connection string, username, and password. Your app Deployment can consume that Secret directly.
No manual wiring. No separate step to inject credentials. The app and the infrastructure are defined in the same ecosystem, and they reference each other naturally.
You can also orchestrate dependencies. Don’t start the app until the database is ready. Crossplane resources have status conditions you can check. Kubernetes controllers can wait for those conditions before proceeding.
This tight integration reduces friction and speeds up environment spin-up.
flowchart TD
A[kubectl apply database.yaml] --> B[Crossplane Controller]
B --> C[Create RDS in AWS]
C --> D[Database Provisioned]
D --> E[Extract Connection Details]
E --> F[Create K8s Secret]
F --> G[my-app-namespace/db-credentials]
H[Application Pod] --> I{Check Secret Exists}
I -->|Secret Ready| J[Mount as Env Vars]
J --> K[App Connects to Database]
G -.->|Referenced by| I
style A fill:#e3f2fd
style C fill:#ffd43b
style F fill:#51cf66
style K fill:#51cf66
Architecture: How to Run Crossplane
Adopting Crossplane means running a Kubernetes cluster to host the control plane. Here are some common patterns.
Single Cluster with Namespaces
Start simple: install Crossplane in an existing Kubernetes cluster. Use namespaces and RBAC to isolate teams or environments. Each team gets a namespace and permissions to create certain Crossplane resources.
This works well for small to medium teams. You get multitenancy without the overhead of managing multiple clusters.
Dedicated Management Cluster
Some teams prefer a dedicated „infrastructure management“ cluster that runs Crossplane (and maybe GitOps tools) separately from application workloads. This provides isolation. Changes to infrastructure tooling don’t affect app clusters, and vice versa.
The management cluster provisions resources across multiple clouds and regions. Application clusters consume those resources.
Control Plane of Control Planes
Advanced pattern: a central Crossplane cluster that provisions other Kubernetes clusters, then installs Crossplane into those clusters. Each team gets their own dedicated Crossplane instance, but the creation of that instance is automated by the parent Crossplane.
This is overkill for most teams, but it scales well in large organizations with strong isolation requirements.
No Cloud Lock-In
Crossplane itself is cloud-agnostic. You can run the control plane anywhere (on-prem, in the cloud, locally for testing) and manage resources in AWS, Azure, GCP, or all of them at once. The Crossplane providers communicate with cloud APIs over the internet.
This flexibility means you can host your control plane wherever makes sense operationally.
graph TB
subgraph Pattern 1: Single Cluster
K1[K8s Cluster]
CP1[Crossplane]
NS1[Team A Namespace]
NS2[Team B Namespace]
K1 --> CP1
CP1 --> NS1
CP1 --> NS2
end
subgraph Pattern 2: Dedicated Management Cluster
MC[Management Cluster<br/>Crossplane + GitOps]
AC1[App Cluster 1]
AC2[App Cluster 2]
MC -.->|Provisions| AC1
MC -.->|Provisions| AC2
end
subgraph Pattern 3: Control Plane of Control Planes
Parent[Parent Crossplane]
Child1[Team 1 Cluster<br/>+ Crossplane]
Child2[Team 2 Cluster<br/>+ Crossplane]
Parent -.->|Creates & Configures| Child1
Parent -.->|Creates & Configures| Child2
end
AWS[AWS Resources]
Azure[Azure Resources]
GCP[GCP Resources]
CP1 -.->|Manages| AWS
MC -.->|Manages| Azure
Child1 -.->|Manages| GCP
Child2 -.->|Manages| AWS
style K1 fill:#e3f2fd
style MC fill:#51cf66
style Parent fill:#ffd43b
style AWS fill:#ff9800
style Azure fill:#2196f3
style GCP fill:#f44336
The Catch: Learning Curve and Maturity
Crossplane isn’t all sunshine. There are trade-offs.
You Need Kubernetes Skills
If your team isn’t comfortable with Kubernetes, Crossplane adds complexity. You’re using K8s as an orchestration layer for infrastructure, which means you need to understand how Kubernetes works.
For teams already running Kubernetes, this is a non-issue. For teams that aren’t, it’s a barrier.
Provider Maturity
Terraform has a massive provider ecosystem built over a decade. Crossplane’s providers are newer and not every cloud service has full coverage yet.
That said, the major providers (AWS, Azure, GCP) are well-maintained and cover the most common use cases. And the ecosystem is growing fast.
You might end up running Crossplane alongside Terraform for a while, using each where it fits best.
Operational Overhead
Running Crossplane means running a Kubernetes cluster. That cluster needs to be highly available, backed up, and monitored. It’s another piece of infrastructure to manage.
For teams already running Kubernetes, this is minimal incremental overhead. For teams that aren’t, it’s a real consideration.
The Future of IaC is Cloud-Native
Infrastructure as Code is evolving. The old model of CLI tools that run occasionally and dump state files is giving way to continuous, Kubernetes-native control planes.
Crossplane represents this shift. It addresses many of Terraform’s long-standing pain points: no external state management, no global locks, continuous drift correction, and deep integration with Kubernetes tooling.
This doesn’t mean Terraform is dead. It’s still a powerful, widely-used tool. And in some scenarios, Terraform and Crossplane can even complement each other.
But if you’re already invested in Kubernetes, if you care about continuous compliance, and if you want infrastructure that reconciles itself without manual intervention, Crossplane offers a compelling alternative.
The ability to encode policies, provide self-service APIs, and treat infrastructure as just another Kubernetes resource brings us closer to the dream of fully automated, compliant infrastructure.
Platform engineering teams at forward-thinking companies are building internal platforms on top of Crossplane, creating custom cloud APIs tailored to their needs. Crossplane graduated to CNCF’s highest maturity level in October 2025 and is used in production by companies including Accenture, Deutsche Bahn, and many others.[10] They’re combining the power of managed cloud services with the control and consistency of Kubernetes.
There’s a learning curve. There’s operational overhead. But the benefits in agility, compliance, and developer autonomy are real.
The future of IaC is cloud-native. And Crossplane is leading the charge.
graph LR
subgraph Terraform Model
T1[One-Shot Execution] --> T2[Manual Runs]
T2 --> T3[State File]
T3 --> T4[Sequential Apply]
T4 --> T5[Drift Detection Gap]
end
subgraph Crossplane Model
C1[Continuous Control Plane] --> C2[Always Reconciling]
C2 --> C3[K8s as State]
C3 --> C4[Parallel Operations]
C4 --> C5[Real-Time Drift Correction]
end
style T1 fill:#ffd43b
style T2 fill:#ffd43b
style T3 fill:#ffd43b
style T4 fill:#ffd43b
style T5 fill:#ff6b6b
style C1 fill:#51cf66
style C2 fill:#51cf66
style C3 fill:#51cf66
style C4 fill:#51cf66
style C5 fill:#51cf66
References
[1] HashiCorp. „What is Infrastructure as Code with Terraform?“ HashiCorp Developer. https://developer.hashicorp.com/terraform/tutorials/aws-get-started/infrastructure-as-code
[2] Build5Nines. „Software Innovation: HashiCorp Terraform Revolutionized Infrastructure As Code (IaC).“ https://build5nines.com/software-innovation-hashicorp-terraform-revolutionized-infrastructure-as-code-iac/
[3] AWS DevOps Blog. „Best practices for managing Terraform State files in AWS CI/CD Pipeline.“ https://aws.amazon.com/blogs/devops/best-practices-for-managing-terraform-state-files-in-aws-ci-cd-pipeline/
[4] Xavor. „Top 6 Terraform State Management Issues and How to Fix Them.“ https://www.xavor.com/blog/terraform-state-management/
[5] Crossplane Blog. „Crossplane vs Terraform.“ https://blog.crossplane.io/crossplane-vs-terraform/
[6] CNCF. „Crossplane Project.“ https://www.cncf.io/projects/crossplane/
[7] Platform Engineering. „Terraform vs. Pulumi vs. Crossplane: Choosing the right IaC Tool.“ https://platformengineering.org/blog/terraform-vs-pulumi-vs-crossplane-iac-tool
[8] Nirmata. „10 Reasons Why Kubernetes Users Choose Kyverno Over OPA Gatekeeper.“ https://nirmata.com/2025/04/14/10-reasons-why-kubernetes-users-choose-kyverno-over-opa-gatekeeper/
[9] CNCF. „GitOps in 2025: From Old-School Updates to the Modern Way.“ https://www.cncf.io/blog/2025/06/09/gitops-in-2025-from-old-school-updates-to-the-modern-way/
[10] CNCF. „Crossplane moves from Sandbox to CNCF Incubator.“ https://www.cncf.io/blog/2021/09/14/crossplane-moves-from-sandbox-to-cncf-incubator/
Additional Reading
- Spacelift. „Crossplane vs Terraform – IaC Tools Comparison.“ https://spacelift.io/blog/crossplane-vs-terraform
- Gruntwork. „How to manage Terraform state.“ https://blog.gruntwork.io/how-to-manage-terraform-state-28f5697e68fa
- Airbnb Engineering. „Continuous Delivery at Airbnb.“ https://airbnb.tech/infrastructure/continuous-delivery-at-airbnb/
- Kubernetes Blog. „OPA Gatekeeper: Policy and Governance for Kubernetes.“ https://kubernetes.io/blog/2019/08/06/opa-gatekeeper-policy-and-governance-for-kubernetes/
- The New Stack. „GitOps on Kubernetes: Deciding Between Argo CD and Flux.“ https://thenewstack.io/gitops-on-kubernetes-deciding-between-argo-cd-and-flux/