If you're deep in the AWS ecosystem, it's a fair question: why not just use CloudFormation? It's native, tightly integrated, and officially supported. On paper, it sounds like the obvious choice.
Yet many teams—especially those scaling infrastructure or working across environments—end up adopting Terraform instead. Not because CloudFormation is bad, but because its design assumptions don't always match how modern systems evolve.
Start with the obvious: CloudFormation isn’t broken
Before getting critical, it's worth acknowledging where CloudFormation shines:
- First-class AWS integration
- No external tooling required
- Strong consistency with AWS APIs
- Built-in rollback mechanisms
If you're building a small-to-medium AWS-only system with stable infrastructure, CloudFormation can be perfectly adequate.
But things start to shift once complexity creeps in.
Here’s where things get interesting
Let’s imagine a fairly common scenario:
- You have staging, production, and ephemeral environments
- You want reusable infrastructure modules
- You’re introducing services outside AWS (e.g., Datadog, Cloudflare, or GCP)
- Your team is growing and contributing to infrastructure
This is where Terraform tends to pull ahead.
CloudFormation is AWS-only by design
This isn’t a flaw—it’s a design choice. But it becomes limiting fast.
CloudFormation works exclusively within AWS. If your architecture includes:
- Multiple cloud providers
- Third-party services
- Hybrid environments
You’ll end up stitching together multiple tools or writing custom scripts.
Terraform, on the other hand, treats infrastructure as a unified graph across providers:
A single Terraform configuration can manage AWS, Azure, GCP, Kubernetes, and SaaS tools in one place.
That changes how teams think about infrastructure—not as cloud-specific templates, but as a cohesive system.
Syntax and developer experience matter more than you think
CloudFormation uses JSON or YAML. Both are verbose and not particularly expressive for logic.
Here’s a simplified CloudFormation snippet:
1Resources:
2 MyBucket:
3 Type: AWS::S3::Bucket
4 Properties:
5 BucketName: my-app-bucket
6Now compare it with Terraform:
1resource "aws_s3_bucket" "my_bucket" {
2 bucket = "my-app-bucket"
3}
4The difference becomes more obvious as complexity grows. Terraform’s HCL (HashiCorp Configuration Language) supports:
- Variables
- Loops and conditionals
- Reusable modules
- Readable interpolation
CloudFormation has similar capabilities—but often through more verbose or indirect constructs.
Reusability is where CloudFormation starts to hurt
Yes, CloudFormation has nested stacks. But in practice, they can feel rigid and cumbersome.
Terraform modules, by contrast, are straightforward and widely adopted.
A simple module usage in Terraform:
1module "vpc" {
2 source = "./modules/vpc"
3 cidr_block = "10.0.0.0/16"
4}
5This pattern scales cleanly across teams. Modules can be versioned, shared, and composed easily.
With CloudFormation, reuse often leads to duplication or overly complex nesting structures.
State management: different philosophies
CloudFormation handles state internally. You don’t see it, manage it, or think about it much.
Terraform, however, makes state explicit:
- Stored locally or remotely (e.g., S3 + DynamoDB)
- Used to track resource changes
- Essential for plan/apply workflows
This initially feels like extra work—but it unlocks powerful capabilities:
- Preview changes before applying (terraform plan)
- Detect drift more transparently
- Integrate into CI/CD pipelines with fine control
CloudFormation changesets exist, but many teams find Terraform’s workflow more intuitive and predictable.
Multi-team workflows and collaboration
A common pain point with CloudFormation is scaling collaboration.
Issues that tend to show up:
- Large monolithic templates
- Difficult ownership boundaries
- Limited tooling outside AWS ecosystem
Terraform, combined with tools like Terraform Cloud or remote backends, provides:
- State locking
- Workspace separation
- Policy enforcement
- Better Git-based workflows
This becomes crucial as infrastructure shifts from "ops-owned" to "team-owned".
A quick comparison snapshot
| Area | CloudFormation | Terraform |
|---|---|---|
| Cloud support | AWS only | Multi-cloud + SaaS |
| Syntax | YAML/JSON | HCL (more expressive) |
| State | Managed internally | Explicit and flexible |
| Reusability | Nested stacks | Modules |
| Workflow | AWS-centric | CLI + CI/CD friendly |
So why do teams switch?
It usually comes down to one of these:
- They outgrow AWS-only tooling
- They want cleaner, reusable infrastructure code
- They need better collaboration workflows
- They prefer Terraform’s planning and state model
It’s rarely a sudden decision. More often, teams start with CloudFormation and gradually feel friction as requirements expand.
When CloudFormation still makes sense
To be fair, Terraform isn’t automatically better in every situation.
CloudFormation can be the right choice when:
- You are fully committed to AWS
- Your infrastructure is relatively stable
- You want minimal tooling overhead
- You rely heavily on AWS-native features and updates
There’s also something to be said for using the tool closest to the platform.
The practical takeaway
The real question isn’t “Terraform vs CloudFormation” in isolation—it’s about how your infrastructure is expected to evolve.
If you’re building something small and contained, CloudFormation is perfectly fine.
If you’re building something that will grow across teams, services, and possibly clouds, Terraform gives you more room to move without rethinking your entire approach later.
That’s why many teams don’t start with Terraform—but eventually end up there.