Blog
Nov 22, 2025 - 13 MIN READ
Infrastructure as Code: The Terraform Supremacy and Its Challengers

Infrastructure as Code: The Terraform Supremacy and Its Challengers

A critical examination of Infrastructure as Code tools in 2025, with Terraform's dominance facing serious competition from Pulumi, CloudFormation, and emerging alternatives.

Julian Morley

Infrastructure as Code (IaC) has become the backbone of how we manage cloud infrastructure these days. But here's the thing—even after a decade of evolution, the IaC world is still kind of a mess. Everyone's got their favorite tool, everyone swears theirs is the best, and nobody can agree on anything. And sitting right in the middle of all this chaos is Terraform—HashiCorp's infrastructure tool that's basically become synonymous with IaC itself.

But is Terraform actually the best? I'm not so sure anymore.

I've spent years building infrastructure for all kinds of companies, and I've used pretty much every major IaC tool out there in real production environments. I've celebrated their wins, cursed their limitations, and felt the very specific pain each one brings. What I've learned is that the "right" tool really depends on what you're trying to do—but more importantly, I think we need to be more honest about Terraform's problems instead of just singing its praises all the time.

Why Infrastructure as Code Matters

Before comparing tools, let's establish why IaC matters in the first place.

The Pre-IaC Dark Ages

I still remember the "bad old days"—and honestly, they weren't even that long ago. Setting up infrastructure meant:

  • Opening up your cloud console and clicking through endless wizards
  • Writing down the steps in a Confluence doc or some kind of wiki (if you were organized)
  • Crossing your fingers that you'd remember all the settings next time
  • Hoping production and testing environments actually matched
  • Spending literal days trying to rebuild things after something broke

This approach was a nightmare. Nothing was reproducible. Everything lived in people's heads. And trying to audit who changed what? Forget about it.

The IaC Revolution

Infrastructure as Code changed all this by treating infrastructure like, well, code:

  • Version controlled — Every change is tracked in Git, so you know who changed what and when
  • Reviewable — You can do pull requests for infrastructure changes just like application code
  • Testable — You can validate changes before deploying them
  • Reproducible — You can spin up identical environments whenever you need them
  • Self-documenting — The code itself tells you how things are configured

These benefits are so huge that IaC has gone from "nice to have" to "you're crazy if you're not doing this" for any serious engineering team.

But here's the million-dollar question: which tool should you actually use?

Terraform: The 800-Pound Gorilla

Let's talk about the elephant—or gorilla—in the room. Terraform has basically become the default choice for managing infrastructure across multiple clouds, and there are some pretty good reasons for that (HashiCorp, n.d.).

Why Terraform Won

Multi-Cloud from Day One

Terraform's provider system was genuinely revolutionary. Instead of locking you into one cloud vendor, Terraform could manage AWS, Azure, GCP, and hundreds of other services through the same interface (HashiCorp, n.d.). If you're worried about vendor lock-in (and you should be), this is incredibly appealing.

Declarative and Predictable

Terraform's declarative approach lets you describe the desired end state, and Terraform figures out how to get there. The plan command shows you exactly what will change before you apply it—a safety feature that has saved countless production incidents.

Massive Ecosystem

The Terraform Registry contains thousands of providers and modules. Need to manage Datadog monitors? There's a provider. Want to configure GitHub repositories? There's a provider. This ecosystem effect created a virtuous cycle—people chose Terraform because it had providers, which encouraged more provider development.

HashiCorp Configuration Language (HCL)

HCL strikes a balance between readability and functionality. It's not a full programming language, which some view as a limitation, but it's more expressive than JSON or YAML while remaining declarative.

Terraform's Real-World Strengths

In production environments, Terraform excels at:

State Management

Terraform's state file tracks the relationship between your configuration and real infrastructure. This enables accurate drift detection and dependency management. When you use remote state with locking, it prevents concurrent modifications that could corrupt infrastructure.

Dependency Graph

Terraform automatically builds a dependency graph and parallelizes resource creation where possible. This makes provisioning fast and reliable.

Import Existing Resources

You can import existing infrastructure into Terraform state, enabling gradual migration from manual provisioning to IaC.

The Terraform Pain Points Nobody Talks About

Okay, here's where I'm going to disagree with the Terraform fanboys.

HCL Is a Straight Jacket

Sure, HCL is better than raw JSON. But it's still yet another domain-specific language you have to learn. And when you need to do something even slightly complex? Get ready for nested for_each loops and conditional expressions that'll make your head spin.

I've seen Terraform code that was way harder to understand than the equivalent in Python or TypeScript would've been. HCL's limitations force you into these weird workarounds that just... shouldn't be necessary.

State File Nightmares

The state file is Terraform's biggest weakness, hands down. Lose it, corrupt it, or have it drift from reality, and you're going to have a bad time. I've burned entire days debugging state file issues that feel like they shouldn't even be a thing.

Yes, remote state helps. But now you need locking mechanisms, Azure Storage Blob, S3 buckets, DynamoDB tables, proper permissions... And if two people accidentally work on the same infrastructure at the same time without coordination? Good luck cleaning up that mess.

Provider Quality Varies Wildly

While the major cloud providers maintain excellent Terraform providers, third-party providers range from "excellent" to "abandoned and buggy." I've wasted days working around provider bugs or missing features.

Refactoring is Painful

Renaming resources, moving them between modules, or restructuring your Terraform code often requires state manipulation with terraform state mv. Get it wrong and you might accidentally destroy and recreate production infrastructure.

Testing is an Afterthought

Testing Terraform code is possible but awkward. Tools like Terratest exist, but they're clunky compared to testing frameworks in real programming languages. Most teams skip testing entirely, which is terrifying for production infrastructure.

The Challengers

Terraform isn't the only game in town, and its competitors address some of its fundamental limitations.

Pulumi: Infrastructure in Real Languages

Pulumi takes a completely different approach: what if you could just use actual programming languages to define infrastructure? (Pulumi, n.d.)

The Pulumi Pitch

Instead of learning HCL, just write your infrastructure in TypeScript, Python, Go, C#, or Java—languages you probably already know. Use actual for loops, real if statements, classes, functions, and all the tooling those ecosystems already have (Pulumi, n.d.).

Where Pulumi Shines

Actual Programming Languages

Want to loop over something? Just use a for loop. Need conditional logic? Use an if statement. Want reusable components? Write a class or function (Pulumi, n.d.).

// Pulumi TypeScript - this is just... normal code
const buckets = ["dev", "staging", "prod"].map(env => 
  new aws.s3.Bucket(`app-${env}`, {
    acl: "private",
    tags: { Environment: env }
  })
);

Compare that to Terraform's for_each gymnastics. The difference is pretty stark.

Superior Testing

Because you're writing in real languages, you can use real testing frameworks. Unit tests, integration tests, property-based tests—all the testing approaches mature ecosystems provide (Pulumi, n.d.).

Better IDE Support

TypeScript in VS Code gives you autocomplete, inline documentation, and type checking. This catches errors before you run anything, unlike Terraform where you only discover problems during plan or apply.

Pulumi Automation API

Pulumi's Automation API lets you embed infrastructure provisioning in applications. Want to build an internal developer platform that provisions infrastructure on-demand? Pulumi makes this straightforward.

Where Pulumi Falls Short

Smaller Ecosystem

Pulumi has fewer providers than Terraform. While it supports all major clouds, niche services might lack Pulumi support. Though Pulumi can use Terraform providers, this defeats the purpose of using Pulumi in the first place.

Steep Learning Curve

Ironically, using real programming languages can be more complex than HCL for simple use cases. You need to understand async/await, promises, and other programming concepts that HCL abstracts away.

State Management (Still Present)

Pulumi still uses a state file (though they call it a checkpoint), so you haven't escaped this fundamental IaC problem. Pulumi Cloud offers hosted state management, but that's a commercial service.

Team Adoption

Try convincing a team that knows Terraform to switch to Pulumi. The network effects and existing Terraform investment make this a hard sell, even if Pulumi is technically superior for your use case.

AWS CloudFormation: The Native Option

CloudFormation is AWS's built-in IaC service. If you're all-in on AWS, it's worth a serious look (Amazon Web Services, n.d.).

CloudFormation's Advantages

Native Integration

CloudFormation is baked right into AWS. New AWS features often show up in CloudFormation before any third-party tool supports them. Plus, authentication is dead simple—it just uses your existing IAM permissions.

Service Catalog Integration

CloudFormation works seamlessly with AWS Service Catalog, which lets you build self-service infrastructure with proper governance controls.

No State File Management

Here's the big one: CloudFormation manages state on AWS's servers. You don't have to worry about state files, locking, or keeping things in sync. This alone eliminates a huge category of headaches.

StackSets for Multi-Account Deployment

CloudFormation StackSets let you deploy the same infrastructure across multiple AWS accounts and regions in one go. For enterprise environments, this is incredibly powerful.

CloudFormation's Limitations

AWS Only

This one's obvious: CloudFormation only works with AWS. Need multi-cloud? You'll need a different tool.

YAML/JSON Hell

CloudFormation templates are written in YAML or JSON. For anything complex, these turn into massive, unreadable files. I've seen 3,000-line CloudFormation templates that nobody wants to touch.

Limited Expressiveness

CloudFormation's template language is primitive. Simple loops require intricate nested structures. Conditionals are awkward. Complex logic is nearly impossible.

Slow Updates

CloudFormation updates can be painfully slow. What takes Terraform seconds can take CloudFormation minutes, especially for large stacks.

The CDK Solution

AWS Cloud Development Kit (CDK) addresses CloudFormation's language limitations by generating CloudFormation from TypeScript, Python, or Java. This gives you real programming language benefits while leveraging CloudFormation's native AWS integration.

CDK is essentially AWS's answer to Pulumi, and it's compelling if you're AWS-only.

Ansible: The Configuration Management Crossover

Ansible isn't strictly an IaC tool—it's a configuration management platform. But it can provision infrastructure, and many organizations use it for both (Red Hat, n.d.).

Ansible's Unique Position

Agentless

Ansible doesn't require agents on target systems—it uses SSH. This simplifies deployment and reduces security surface area.

Configuration + Provisioning

Ansible can provision infrastructure and configure it in the same playbook. For organizations already using Ansible for configuration management, using it for infrastructure makes sense.

Procedural Approach

Unlike Terraform's declarative approach, Ansible is procedural—you define steps to execute. This is more intuitive for some use cases but less elegant for infrastructure management.

Why Ansible Struggles as Pure IaC

Not Idempotent by Design

While Ansible modules can be idempotent, the language doesn't enforce it. You can easily write non-idempotent playbooks that behave unpredictably when run multiple times.

No Dependency Graph

Ansible executes tasks in order, not based on dependencies. You manually handle ordering, which is error-prone for complex infrastructure.

State Management is Awkward

Ansible doesn't maintain state like Terraform. Determining what changed requires complex logic in your playbooks.

Limited Cloud Provider Support

While Ansible has cloud modules, they're less comprehensive than Terraform providers or native cloud tools.

Crossplane: The Kubernetes-Native Approach

Crossplane takes a radically different approach: manage infrastructure using Kubernetes APIs (Crossplane, n.d.).

The Crossplane Vision

If you're already on Kubernetes, why not manage your infrastructure through Kubernetes too? Crossplane extends Kubernetes to provision and manage cloud resources using custom resource definitions (CRDs) (Crossplane, n.d.).

Where Crossplane Excels

Kubernetes-Native

If your entire ecosystem is Kubernetes, Crossplane provides a consistent interface. Manage infrastructure with kubectl and Kubernetes manifests.

Composition and Abstraction

Crossplane's composition features let you create custom infrastructure APIs. Platform teams can expose simplified interfaces while hiding complexity.

GitOps Integration

Crossplane integrates naturally with GitOps workflows using tools like ArgoCD or Flux.

Where Crossplane Struggles

Kubernetes Requirement

You must run Kubernetes. If you're not already deeply invested in Kubernetes, running it just for infrastructure management is overkill.

Complexity

Crossplane adds significant complexity. You need to understand Kubernetes operators, CRDs, and Crossplane's specific patterns.

Maturity

Crossplane is younger than Terraform or CloudFormation. Provider coverage is improving but still limited (Crossplane, n.d.).

The Comparison Matrix

Let me provide a pragmatic comparison based on real-world experience:

Best Multi-Cloud Solution: Terraform

If you need to manage infrastructure across AWS, Azure, GCP, and various SaaS providers, Terraform remains the best option. The provider ecosystem is unmatched, and the learning investment pays off across providers.

Best for AWS-Only: CloudFormation + CDK

If you're exclusively on AWS, CloudFormation with CDK gives you native integration, real programming languages, and no state file management. The velocity and reliability gains outweigh multi-cloud portability you don't need.

Best for Complex Logic: Pulumi

If your infrastructure provisioning requires complex logic, dynamic resource creation, or tight integration with application code, Pulumi's real programming languages provide capabilities that Terraform's HCL simply can't match.

Best for Configuration + Infrastructure: Ansible

If you need configuration management and infrastructure provisioning and your infrastructure is relatively simple, Ansible provides a unified tool. But for complex infrastructure, combine it with a dedicated IaC tool.

Best for Kubernetes Platforms: Crossplane

If you're building an internal platform on Kubernetes and want infrastructure management integrated with your existing Kubernetes workflows, Crossplane's approach makes sense.

The Uncomfortable Truth

Here's what nobody really wants to say out loud: every single one of these tools has some pretty major flaws.

State management is just broken in Terraform and Pulumi. The state file is a single point of failure that creates constant headaches.

CloudFormation's template language is awful, even though the actual service works well. CDK helps, but now you've got another layer of abstraction to deal with.

Ansible wasn't built for infrastructure provisioning, and you can tell. Using it for IaC is like using a screwdriver as a hammer—sure, it works, but it's awkward.

Crossplane makes you run Kubernetes just to manage infrastructure, which feels like massive overkill for what should be a simpler problem.

Pulumi's "use any language" approach sounds great until you realize that real programming languages bring real complexity. Simple infrastructure tasks become complicated code.

Basically, we're all just picking the least-bad option for our specific situation. There's no perfect tool here.

Looking Forward: What I Actually Recommend

After doing this for years, here's my honest take:

For Startups and Small Teams

Go with CloudFormation + CDK if you're AWS-only, Terraform if you need multi-cloud.

Startups don't have time for complicated tooling. CloudFormation eliminates the whole state file problem. Terraform gives you flexibility if you need to work with multiple clouds.

Skip Ansible (too procedural), Crossplane (way too complex), and Pulumi (more power than you need).

For Mid-Size Companies

Use Terraform for infrastructure, Ansible for configuration management.

At this stage, you've got people who can handle Terraform state properly. The huge provider ecosystem starts to really pay off as you integrate with more services. Use Ansible for configuring servers and deploying apps.

For Large Enterprises

Use different tools for different jobs.

  • Terraform for multi-cloud stuff
  • CloudFormation for AWS-specific services
  • Pulumi for platform engineering teams building internal developer platforms
  • Crossplane if you're heavily invested in Kubernetes

Big companies have the resources to support multiple tools. Pick what works best for each team instead of forcing everyone to use the same thing.

The Evolution Continues

The IaC landscape continues evolving. New players emerge, existing tools improve, and patterns shift. We're moving toward:

  • Higher-level abstractions — Tools like Crossplane and Pulumi focus on creating custom APIs that hide infrastructure complexity
  • Better testing — Testing infrastructure code is becoming more sophisticated
  • Platform engineering — IaC is becoming part of larger internal developer platforms
  • AI-assisted infrastructure — Tools incorporating AI to generate and optimize infrastructure code

Conclusion: Choose Wisely, But Choose

The worst thing you can do is not pick a tool at all and keep doing everything manually. The second worst thing is picking a tool because it's trendy instead of actually thinking about what you need.

Terraform's popularity is deserved in a lot of ways—it's mature, well-documented, and has a massive ecosystem. But it's not perfect, and honestly, the alternatives are getting really compelling for certain use cases.

My advice:

  • Figure out your constraints first — Are you AWS-only? Multi-cloud? What does your team already know?
  • Match the tool to the job — Don't try to force one tool to do everything
  • Actually invest in learning — Taking time to properly learn IaC pays off big time
  • Start simple and iterate — Begin with basic stuff, get fancy later

The "best" IaC tool is whatever solves your problems with the least amount of hassle. Sometimes that's Terraform. Sometimes it's not.

And you know what? That's completely fine.

References

Amazon Web Services. (n.d.). AWS CloudFormation. https://aws.amazon.com/cloudformation/

Crossplane. (n.d.). The cloud native control plane framework. https://www.crossplane.io/

HashiCorp. (n.d.). Terraform by HashiCorp. https://www.terraform.io/

Pulumi. (n.d.). Pulumi - Infrastructure as Code in Any Programming Language. https://www.pulumi.com/

Red Hat. (n.d.). Red Hat Ansible Automation Platform. https://www.redhat.com/en/technologies/management/ansible


What's your experience with Infrastructure as Code tools? Have you made the switch from one tool to another? I'm interested in hearing about your journey—reach out to share your perspective.

Julian Morley • © 2025