Comparison: Infrastructure as Code Tools¶
Category: Infrastructure as Code Last meaningful update consideration: 2026-03 Verdict (opinionated): Terraform (or OpenTofu) for multi-cloud. CDK for AWS-only shops that want real programming languages. Pulumi for teams that hate HCL but want Terraform-level provider support. Crossplane for K8s-native infrastructure management.
Quick Decision Matrix¶
| Factor | Terraform / OpenTofu | Pulumi | CloudFormation | CDK | Crossplane |
|---|---|---|---|---|---|
| Learning curve | Medium (HCL) | Low-Medium (your language) | Medium (JSON/YAML) | Medium (your language) | High (K8s + XRDs) |
| Operational overhead | Low | Low | None (AWS-managed) | None (AWS-managed) | Medium (K8s operator) |
| Cost at small scale | Free | Free (OSS) | Free | Free | Free |
| Cost at large scale | Free (OSS) / HCP paid | Pulumi Cloud paid | Free | Free | Free |
| Community/ecosystem | Massive (providers) | Growing | AWS-only | AWS-only (mostly) | Growing (CNCF) |
| Hiring | Easy | Moderate | Easy (AWS) | Growing | Niche |
| Multi-cloud | Excellent | Excellent | No | Mostly no | Yes (providers) |
| State management | S3/GCS/remote backends | Pulumi Cloud / self-managed | AWS-managed | AWS-managed | K8s etcd |
| Language | HCL | Python, TS, Go, C#, Java | JSON/YAML | Python, TS, Go, C#, Java | YAML (K8s CRDs) |
| Drift detection | terraform plan |
pulumi preview |
Drift detection | Via CloudFormation | Continuous reconciliation |
| Testing | Terratest, plan analysis | Unit tests in your language | TaskCat, cfn-lint | CDK Assertions | K8s tooling |
| Provider coverage | 3000+ providers | 150+ providers + TF bridge | AWS only | AWS only (mostly) | 50+ providers |
When to Pick Each¶
Pick Terraform / OpenTofu when:¶
- You need to manage infrastructure across multiple cloud providers
- Your team is comfortable with a domain-specific language (HCL) purpose-built for IaC
- Provider coverage matters — every cloud service has a Terraform provider
- You want the largest community, most examples, and most modules available
- Reproducible
plan→applyworkflow is your standard operating procedure - OpenTofu if the BSL license concerns you; Terraform if you want HashiCorp ecosystem integration
Pick Pulumi when:¶
- Your team strongly prefers using a general-purpose programming language (TypeScript, Python, Go)
- You want real programming constructs: loops, conditionals, functions, classes, unit tests
- The Terraform Bridge gives you access to most Terraform providers without writing HCL
- You want component resources (reusable infrastructure abstractions) in your language
- You dislike HCL and value developer experience over ecosystem breadth
Pick CloudFormation when:¶
- You are 100% AWS and want a fully managed IaC service with no state files to manage
- You need native integration with AWS features like StackSets (multi-account), Change Sets, and Service Catalog
- Your organization is conservative and prefers an AWS-supported tool
- Compliance requirements favor AWS-native tools
Pick CDK when:¶
- You want CloudFormation's reliability with real programming languages
- Your team writes TypeScript, Python, or Go and wants infrastructure defined alongside application code
- You want L2/L3 constructs that provide sensible defaults (e.g.,
new Vpc()with best-practice subnets) - You are building AWS-centric and CloudFormation is the backing engine you trust
Pick Crossplane when:¶
- You want infrastructure management as a Kubernetes-native experience
- Platform teams building self-service infrastructure APIs for product teams
- Continuous reconciliation (like GitOps but for cloud resources) is important
- You want to compose custom platform APIs (Composite Resources) on top of cloud providers
- You are already operating Kubernetes and want to extend its control plane to cloud resources
Nobody Tells You¶
Terraform / OpenTofu¶
- State file management is the #1 source of Terraform pain. State locking, state corruption, state drift, and "someone ran apply locally" are constant battles. Use remote backends (S3 + DynamoDB) from day one.
- The BSL license change fractured the community. OpenTofu exists as a fork but provider ecosystem parity is not guaranteed long-term. Choose deliberately.
terraform planoutput is not a reliable diff for complex changes. Resource recreation, count/for_each changes, and provider-side defaults create surprises.- Module versioning and the Terraform Registry are convenient but create supply chain risk. Vendoring modules or writing your own is safer for production.
- Large state files (1000+ resources) make
planandapplyslow. State splitting across workspaces or Terragrunt configurations adds management overhead. terraform importis painful. It brings the resource into state but does not generate the HCL. You write the code manually and iterate untilplanshows no diff.- The
movedblock helps with refactoring but does not cover all cases. Renaming a module still risks resource recreation.
Pulumi¶
- "Use your favorite language" is the pitch, but it means your IaC is as complex as your team makes it. Overengineered abstractions in Python/TypeScript are harder to debug than flat HCL.
- Pulumi's state management defaults to Pulumi Cloud (their SaaS). Self-managed backends (S3, Azure Blob) work but lack the UI and collaboration features.
- The Terraform Bridge is clever — it wraps Terraform providers for Pulumi — but bridged providers sometimes lag behind Terraform provider releases.
- Pulumi's community is smaller. When you hit an edge case, there are fewer Stack Overflow answers and blog posts.
- Pulumi Automation API (embedding Pulumi in your application) is powerful for platform engineering but is a complexity multiplier.
- Testing IaC with real programming language test frameworks (pytest, Jest) is Pulumi's superpower but requires discipline — most teams do not write IaC tests.
CloudFormation¶
- CloudFormation error messages are infamously unhelpful. "UPDATE_ROLLBACK_FAILED" tells you nothing about what went wrong.
- Stack updates that fail can leave you in a stuck state that requires manual intervention or stack deletion.
- CloudFormation resource coverage lags behind the AWS API. New services and features often take months to appear.
- Nested stacks add dependency management headaches. Cross-stack references via Exports create coupling that resists refactoring.
- The 500-resource limit per stack forces you into nested stacks or stack splitting for large environments.
- YAML/JSON templates are verbose and repetitive. CloudFormation intrinsic functions (
!Ref,!GetAtt,!Sub) are not a real programming language.
CDK¶
- CDK generates CloudFormation templates under the hood. When things break, you debug CloudFormation, not CDK. Understanding both layers is required.
- CDK synth output is massive, auto-generated CloudFormation. Reviewing it in PRs is impractical, which means you lose the "infrastructure change review" benefit.
- L2/L3 constructs are opinionated. When the defaults do not match your requirements, you drop to L1 (raw CloudFormation) and lose the abstraction benefit.
- CDK version upgrades break things. Construct library versions must be compatible with the CDK core, and peer dependency issues are common.
- The CDK Bootstrap stack must be deployed per account per region before CDK works. This is a manual step that surprises new teams.
Crossplane¶
- Crossplane is complex. You are running a K8s operator that manages cloud resources via CRDs. Debugging "why did my RDS instance not create" requires understanding K8s controllers, Crossplane providers, and cloud APIs.
- Provider readiness varies wildly. The AWS provider covers most services, but GCP and Azure providers have gaps.
- Composition (building custom APIs) is powerful but the authoring experience is YAML-heavy and hard to test.
- Crossplane turns your K8s cluster into a control plane for cloud resources. If your cluster goes down, cloud resource reconciliation stops. This is a different risk model than Terraform.
- Resource creation is slower than Terraform — the reconciliation loop adds latency.
Migration Pain Assessment¶
| From → To | Effort | Risk | Timeline |
|---|---|---|---|
| CloudFormation → Terraform | Medium-High | Medium | 2-4 months |
| Terraform → Pulumi | Medium | Low | 1-3 months (tf2pulumi tool helps) |
| Pulumi → Terraform | Medium | Low | 1-3 months |
| CloudFormation → CDK | Medium | Low | 1-2 months |
| Terraform → Crossplane | High | High | 3-6 months |
| Manual (console) → Terraform | High | Medium | 2-6 months |
The scariest migration is from manual (console/CLI) to any IaC tool. It requires importing existing resources, which means writing code for infrastructure you did not provision through code. Budget for a thorough resource inventory first.
The Interview Answer¶
"Terraform is my default for multi-cloud because the provider ecosystem is unmatched and HCL strikes a good balance between readability and expressiveness. For AWS-only organizations, CDK lets you use real programming languages with CloudFormation reliability. The deeper insight is that the tool matters less than the practice: whatever you choose, the value comes from peer-reviewed infrastructure changes, automated plan-and-apply pipelines, and state management discipline. Teams that 'terraform apply' from laptops get the same chaos as teams with no IaC at all."
Cross-References¶
- Topic Packs: Terraform, Terraform Deep Dive, Pulumi, Crossplane, OpenTofu
- Related Comparisons: Config Management, K8s Templating
Pages that link here¶
- Comparison: Configuration Management
- Comparison: Kubernetes Templating
- Comparison: Secrets Management
- Crossplane - Primer
- How We Got Here: Infrastructure as Code
- Infrastructure as Code with Terraform - Primer
- OpenTofu & Terraform Ecosystem - Primer
- Pulumi - Primer
- Terraform Deep Dive - Primer
- Tool Comparison Matrices