By Vaibhav Pendhare · June 30, 2026 · 8 min read
TL;DR
- Terraform is the right default for most teams: cloud-agnostic, the largest module ecosystem by far, and a declarative HCL syntax that’s become the industry’s shared vocabulary. It’s what we reach for in the vast majority of engagements.
- Pulumi is the better choice when your team wants infrastructure defined in a real programming language — TypeScript, Python, Go, C# — with loops, functions, and unit tests, not a DSL.
- AWS CDK only makes sense if you’re AWS-only and want infrastructure and application code sharing a language and a repo, often for CDK-native serverless patterns (Lambda, Step Functions, EventBridge).
- The decision comes down to team language preference, multi-cloud requirements, and how much you value a mature module ecosystem over programming-language ergonomics — not raw feature count.

The question behind the question
Nobody asks “Terraform vs Pulumi vs CDK” out of idle curiosity. They ask it because they’re standing up infrastructure-as-code for the first time, or because their current setup — usually a pile of ClickOps and a few undocumented scripts — has become the thing nobody trusts to touch. The anxiety is the same as any tooling decision made early: whichever tool you pick is the one your CI/CD, your onboarding docs, and your team’s muscle memory will be built around for years.
The reassuring part: all three tools solve the core problem — declare desired infrastructure state, diff it against reality, apply the delta — reliably and at production scale. The differences that matter are narrower than the marketing pages suggest, and mostly come down to what language your team already writes in and how many clouds you actually need to support.
Side-by-side comparison
Language and syntax
Terraform uses HCL (HashiCorp Configuration Language) — a declarative, purpose-built DSL. It’s not a general-purpose language: no real loops (just for_each and count), no functions you define yourself, and logic lives in a constrained expression language. This is a feature as much as a limitation — HCL is readable by anyone on the team, including people who don’t write code day-to-day.
Pulumi uses actual programming languages — TypeScript, Python, Go, C#, Java — meaning you get real control flow, your IDE’s autocomplete and type-checking, and the ability to write unit tests for your infrastructure code the same way you’d test application code.
AWS CDK also uses general-purpose languages (TypeScript, Python, Java, C#, Go) but compiles down to CloudFormation templates rather than calling cloud provider APIs directly. Your infrastructure code and your application code can live in the same repository, same language, same CI pipeline.
When this matters: If your team is platform engineers who think in infrastructure primitives, HCL’s constraints are a feature — less room to write clever, hard-to-review code. If your team is strong in TypeScript or Python and wants infrastructure to feel like software (with real abstractions, loops, and tests), Pulumi or CDK removes the translation tax.
State management
Terraform tracks a state file (local, or remote in S3/Terraform Cloud/etc.) that maps your configuration to real-world resources. State locking, drift detection, and terraform plan are all built around this file. It’s mature, well-understood, and the terraform-cloud/terraform-enterprise ecosystem exists largely to manage state and runs at scale.
Pulumi also tracks state, stored either in Pulumi Cloud (their SaaS backend, free tier available) or self-hosted (S3, Azure Blob, GCS). The mechanics are conceptually identical to Terraform’s — a state file, a plan/preview step, an apply step — just with Pulumi’s own format and CLI.
AWS CDK doesn’t manage its own state at all — it compiles to CloudFormation and hands state management entirely to CloudFormation, which AWS operates. You never touch a state file directly, but you also inherit CloudFormation’s quirks: stack size limits, slower rollbacks on failure, and drift detection that’s less granular than Terraform’s plan.
When this matters: Terraform’s state model is the most battle-tested and the most portable (you own the file, you choose the backend). Pulumi’s is nearly identical in practice. CDK’s hands-off approach removes a class of state-file mistakes but ties you fully to CloudFormation’s operational behavior — including its slower, more opaque failure modes on large stacks.
Multi-cloud and provider coverage
Terraform has, by a wide margin, the largest provider ecosystem — AWS, Azure, GCP, DigitalOcean, Hetzner, Cloudflare, Datadog, GitHub, Kubernetes, and hundreds more, all through the same workflow. This is Terraform’s single biggest structural advantage.
Pulumi wraps the same underlying provider APIs (in many cases, it’s built on top of Terraform’s own provider plugins via a bridge), so coverage is broad but occasionally a version or two behind native Terraform providers for edge-case resources.
AWS CDK is AWS-only, full stop. If you need Azure or GCP resources, CDK is not an option — you’d need a separate tool entirely, which means two IaC systems if you’re multi-cloud.
When this matters: If you’re on more than one cloud — or might be — Terraform or Pulumi are your only real options. CDK is disqualifying the moment a second cloud enters the picture, no matter how much your team likes writing infrastructure in TypeScript.
Ecosystem and reusable modules
Terraform has, by far, the deepest module ecosystem — the public Terraform Registry alone hosts tens of thousands of community and vendor modules. We maintain 227+ open-source Terraform modules across AWS, Azure, GCP, and DigitalOcean ourselves, under the CloudDrove and terraform-do-modules GitHub orgs — VPCs, EKS clusters, RDS instances, IAM baselines, all tested and versioned. Most of our engagements start from one of these modules rather than a blank file.
Pulumi has its own registry of “packages” (often auto-generated from Terraform providers) plus Automation API for programmatic infrastructure orchestration, but the ecosystem of pre-built, opinionated modules is meaningfully smaller than Terraform’s.
AWS CDK ships “constructs” — reusable, higher-level abstractions — and the CDK construct hub has a decent library, particularly for serverless patterns (API Gateway + Lambda + DynamoDB stacks are common), but again, nothing close to Terraform’s registry depth.
When this matters: If you want to stand up infrastructure fast using tested, maintained modules rather than writing every resource from scratch, Terraform’s registry depth is hard to beat. This is the single biggest reason we default to it.
Testing and CI/CD integration
Terraform testing has matured significantly — native terraform test (HCL-based test files), plus terraform plan in CI as a built-in dry-run. Third-party tools (Terratest, Checkov, tfsec) round out policy and unit testing.
Pulumi testing benefits directly from being real code — you can unit-test infrastructure logic with your language’s native test framework (Jest, pytest, etc.), mock cloud resources, and use Pulumi’s own testing SDK for property-based assertions.
AWS CDK similarly benefits from general-purpose-language testing — CDK’s assertions module lets you snapshot-test synthesized CloudFormation templates and assert on specific resource properties using your language’s test runner.
When this matters: If your team already has strong software testing discipline (unit tests, CI gates, code review culture built around a real language), Pulumi and CDK feel more natural to test. Terraform’s testing has closed most of the gap, but it’s still a purpose-built test DSL rather than your team’s existing test framework.
Decision matrix
| Criterion | Choose Terraform | Choose Pulumi | Choose CDK |
|---|---|---|---|
| Multi-cloud | Yes — broadest support | Yes — broad support | No — AWS only |
| Team background | Platform/infra engineers, DSL-comfortable | Strong TypeScript/Python/Go engineers | AWS-only team, app-and-infra in one repo |
| Module ecosystem | Deepest by far (Terraform Registry) | Smaller, growing | AWS-focused constructs, strong for serverless |
| State ownership | You own the state file, any backend | You own the state file, Pulumi Cloud or self-hosted | Fully delegated to CloudFormation |
| Testing style | Purpose-built test DSL + third-party tools | Native language test frameworks | Native language test frameworks |
| Learning curve | New DSL to learn | Minimal if already fluent in the language | Minimal if already fluent in the language |
| Serverless/AWS-native patterns | Supported, less opinionated | Supported | Best-in-class, purpose-built |
Real-world example
This multi-provider reasoning isn’t hypothetical — it’s exactly what drove the infrastructure choice on our engagement with Bridj.ai, a mobility-tech platform running primarily on GCP with Hetzner Cloud handling supporting workloads. Multi-provider from day one ruled out CDK entirely. The whole platform — Cloud Run services, networking, and the Hetzner-hosted pieces — was provisioned through Terraform, giving them one IaC workflow instead of two: nine applications across five repositories, deployed through a single Terraform-managed infrastructure layer.
A different engagement — a client running an investment portfolio management tool, AWS-only, with a strong TypeScript team building serverless APIs (API Gateway, Lambda, Step Functions, EventBridge) — was a clean fit for CDK. Their application and infrastructure code lived in the same monorepo, deployed through the same CI pipeline, in the same language their engineers already used daily. We didn’t push Terraform here even though it’s our default — CDK’s serverless-native constructs and single-language repo were a genuinely better fit for their AWS-only, TypeScript-first team.
Trade-offs and what we’d avoid
- Don’t pick CDK if there’s any chance of a second cloud. It’s the single most common regret we see — teams adopt CDK for its AWS ergonomics, then need Azure or GCP eighteen months later and have to stand up a second IaC system from scratch.
- Don’t switch IaC tools to chase a nicer syntax. Migrating from Terraform to Pulumi (or vice versa) is a real project — re-importing state, rewriting modules, retraining the team. “The new tool looks cleaner” is rarely worth the migration cost on its own.
- Don’t write your own modules from scratch if a tested one exists. Whichever tool you pick, check the registry (Terraform Registry, Pulumi’s package registry, or the CDK Construct Hub) before writing a VPC or EKS module by hand — it’s almost never a good use of a senior engineer’s time.
- Don’t skip state locking and remote backends “for now.” Local Terraform or Pulumi state with no locking is fine for a solo prototype and actively dangerous the moment a second engineer runs
applyat the same time. Set up remote state (S3 + DynamoDB lock table, or Pulumi Cloud/Terraform Cloud) before onboarding a second contributor.
What to do next
- If you’re standing up infrastructure-as-code for the first time — the free Infrastructure Assessment reviews your current setup and recommends the right tool and module structure before you write a single resource.
- If you already know it’s Terraform — see Infrastructure Audit or browse our 227+ open-source Terraform modules across AWS, Azure, GCP, and DigitalOcean before writing new ones from scratch.
- If you’re migrating clouds or consolidating multiple IaC tools into one — see Cloud Migration or book a 30-minute call to scope the migration before committing.
Related reading: EKS vs GKE vs AKS in 2026 — once your IaC tool is picked, the cloud and cluster architecture decision usually comes next.
Tags