What is Infrastructure as Code? Terraform vs Pulumi vs CDK Compared
Compare Terraform, AWS CDK, and Pulumi for Infrastructure as Code. Covers languages, state management, multi-cloud support, pricing, and practical guidance on choosing the right tool.
Infrastructure engineer with 10+ years building production systems on AWS, GCP,…

Fifteen Years of Infrastructure Tooling in Five Turns
It is worth knowing how we got here, because the current IaC debate (Terraform vs AWS CDK vs Pulumi) makes sense only as a reaction to what came before. The timeline is shorter than most people realise.
- 2006-2011 -- shell scripts and AMI bakes: the first generation was
#!/bin/bashplus a jump host. State lived in someone's head. Puppet (2005) and Chef (2009) formalised configuration management for existing servers but not provisioning -- you still clicked "launch instance" in the console. - 2011-2014 -- CloudFormation and Heat: AWS shipped CloudFormation in 2011, bringing declarative provisioning to one cloud. JSON (and later YAML) templates gave you repeatability, but vendor lock-in was total and error messages were catastrophic.
- 2014 -- Terraform: HashiCorp released Terraform with HCL and a multi-cloud provider model. The
plan/applyloop and a shared state file became the default mental model for an entire decade. - 2018-2019 -- Pulumi and AWS CDK: the general-purpose-language camp arrived. Pulumi (2018) offered TypeScript/Python/Go for any cloud; AWS CDK (2019) offered the same languages but synthesised down to CloudFormation.
- 2023 -- the BSL fork: HashiCorp relicensed Terraform to BSL 1.1. The Linux Foundation forked it as OpenTofu in September 2023. Every serious IaC conversation now includes a licensing footnote that did not exist eighteen months earlier.
Everyone agrees infrastructure should be code. The meaningful question in 2026 is which code -- HCL, TypeScript, Python -- and whose runtime executes it. This guide compares the three tools that dominate real-world decisions, with the licensing, state, and language trade-offs that actually matter.
Why IaC Matters: The Core Benefits
- Reproducibility -- deploy identical environments for dev, staging, and production from the same code
- Auditability -- every change is a Git commit with an author, timestamp, and review trail
- Speed -- spinning up a new environment goes from days of manual work to a single pipeline run
- Drift detection -- compare the actual state of your infrastructure to what the code declares
- Disaster recovery -- rebuild your entire infrastructure from code if a region goes down
The Big Three: Terraform vs CDK vs Pulumi
| Feature | Terraform | AWS CDK | Pulumi |
|---|---|---|---|
| Language | HCL (domain-specific) | TypeScript, Python, Java, C#, Go | TypeScript, Python, Go, C#, Java, YAML |
| Cloud support | Multi-cloud (AWS, GCP, Azure, 3000+ providers) | AWS only | Multi-cloud (AWS, GCP, Azure, Kubernetes) |
| State management | Remote backends (S3, Terraform Cloud) | CloudFormation stacks | Pulumi Cloud or self-managed backends |
| Dry-run | terraform plan | cdk diff | pulumi preview |
| Maturity | 2014, massive ecosystem | 2019, AWS-maintained | 2018, growing ecosystem |
| License | BSL 1.1 (was MPL 2.0) | Apache 2.0 | Apache 2.0 |
| Learning curve | Low (HCL is simple) to medium (modules) | Medium (need to know CloudFormation concepts) | Low if you know the language |
Terraform: The Industry Standard
How It Works
You write .tf files in HCL (HashiCorp Configuration Language), run terraform plan to see what will change, and terraform apply to execute. Terraform maintains a state file that maps your code to real resources.
resource "aws_s3_bucket" "data" {
bucket = "my-app-data-bucket"
tags = {
Environment = "production"
ManagedBy = "terraform"
}
}
resource "aws_s3_bucket_versioning" "data" {
bucket = aws_s3_bucket.data.id
versioning_configuration {
status = "Enabled"
}
}
Strengths
- Largest provider ecosystem -- 3,000+ providers covering every cloud and SaaS product
- HCL is purpose-built and readable, even for non-developers
- Massive community: Stack Overflow answers, blog posts, and pre-built modules for everything
terraform plangives clear, predictable diffs before any change
Weaknesses
- HCL lacks loops, conditionals, and abstractions that general-purpose languages provide (though
for_eachanddynamicblocks help) - State file management is a source of bugs -- state locks, state corruption, and manual state surgery are real operational burdens
- The BSL license change in 2023 led to the OpenTofu fork, creating ecosystem fragmentation
AWS CDK: Infrastructure in Real Languages
How It Works
CDK lets you define AWS resources using TypeScript, Python, or other supported languages. Under the hood, CDK synthesizes your code into CloudFormation templates and deploys them as CloudFormation stacks.
import * as cdk from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';
export class DataStack extends cdk.Stack {
constructor(scope: cdk.App, id: string) {
super(scope, id);
new s3.Bucket(this, 'DataBucket', {
bucketName: 'my-app-data-bucket',
versioned: true,
removalPolicy: cdk.RemovalPolicy.RETAIN,
});
}
}
Strengths
- Use real programming languages with IDE autocompletion, type checking, and refactoring tools
- L2 and L3 constructs provide sensible defaults -- a single
new ApplicationLoadBalancedFargateService()creates an ALB, ECS service, task definition, security groups, and IAM roles - CloudFormation handles state management, rollbacks, and drift detection
- AWS maintains it -- first-class support for new AWS services
Weaknesses
- AWS-only. If you use GCP or Azure alongside AWS, CDK can't manage those resources.
- CloudFormation is slow. Large stacks take 10-30 minutes to deploy.
- CloudFormation's 500-resource limit per stack forces you to split large applications into multiple stacks.
- Debugging CloudFormation errors through CDK adds an abstraction layer that obscures the real problem.
Pulumi: Multi-Cloud with Real Languages
How It Works
Pulumi uses general-purpose languages to define infrastructure, similar to CDK, but it manages state directly (no CloudFormation intermediary). It supports AWS, GCP, Azure, Kubernetes, and dozens of other providers.
import * as aws from '@pulumi/aws';
const bucket = new aws.s3.Bucket('data-bucket', {
bucket: 'my-app-data-bucket',
versioning: { enabled: true },
tags: {
Environment: 'production',
ManagedBy: 'pulumi',
},
});
Strengths
- Multi-cloud with real languages -- the best of both CDK and Terraform
- Direct state management (no CloudFormation) means faster deployments
- Pulumi AI and the converter tool can translate Terraform HCL to Pulumi code
- Built-in secrets management -- encrypted in state by default
Weaknesses
- Smaller ecosystem and community than Terraform
- Pulumi Cloud (the managed backend) is a paid service for teams; self-managed backends work but require more setup
- Some Terraform providers don't have Pulumi equivalents yet
For reference: Infrastructure as Code (IaC) is the practice of managing and provisioning computing infrastructure through machine-readable configuration files -- typically HCL, TypeScript, Python, or YAML -- rather than manual console clicks. IaC enables version control, peer review, automated deployment, and drift detection as first-class capabilities.
State Management Compared
| Aspect | Terraform | AWS CDK | Pulumi |
|---|---|---|---|
| State storage | S3 + DynamoDB, Terraform Cloud | CloudFormation (managed by AWS) | Pulumi Cloud, S3, local file |
| State locking | DynamoDB or Terraform Cloud | Built into CloudFormation | Built into Pulumi Cloud |
| Drift detection | terraform plan (compares state to cloud) | CloudFormation drift detection | pulumi refresh |
| Import existing resources | terraform import | cdk import | pulumi import |
Pricing and Tooling
- Terraform CLI -- free (BSL license). Terraform Cloud free tier: 500 managed resources. Team tier: $20/user/month.
- AWS CDK -- free. CloudFormation is free (you pay only for provisioned resources).
- Pulumi CLI -- free (Apache 2.0). Pulumi Cloud Individual: free. Team: $50/user/month. Enterprise: custom pricing.
- OpenTofu -- free fork of Terraform under MPL 2.0, maintained by the Linux Foundation.
- Infracost -- shows cost estimates in Terraform pull requests. Free for open source, $50+/month for teams.
- Spacelift -- CI/CD for Terraform/Pulumi/CloudFormation. Starts at $40/month.
Failure Modes: What Actually Breaks in Production
Everyone writes the happy-path tutorial. These are the sharp edges that turn a 10-minute change into a weekend.
Terraform State Lock Contention
Two CI jobs kick off simultaneously against the same workspace. One grabs the DynamoDB lock, the other sits in Acquiring state lock for twenty minutes and times out. The lock orphans. The only fix is terraform force-unlock <id>, which feels terrifying because it is. Either serialise IaC runs through Terraform Cloud / Atlantis / Spacelift, or accept that every CI platform needs an explicit mutex.
CDK Stack Resource Limit
CloudFormation's 500-resource-per-stack limit is not visible in CDK code. A team built a monolithic cdk.Stack that synth'd to 503 resources and every deploy failed with a CFN error nobody had seen before. The refactor -- splitting across nested stacks -- took ten days. Design for multiple stacks from day one.
Pulumi Secret Rotation Breaks Plan Output
Pulumi stores secrets encrypted in state. Rotate the KMS key and existing state becomes unreadable until you run pulumi stack export + pulumi stack import with the new key. Not a crash, just a 45-minute recovery while production deploys are queued behind you.
Provider Version Drift Between Laptops
Developer A's local Terraform uses AWS provider 5.12, developer B's uses 5.67. The plan output differs for the same code because provider defaults changed between versions. Always pin required_providers with an exact constraint, and commit the .terraform.lock.hcl file.
Choosing: A Decision Tree for Real Teams
The comparison table gives you the features; the choice depends on constraints you already have.
- Single-cloud AWS shop, TypeScript team: AWS CDK. The L2/L3 constructs save weeks of boilerplate and CloudFormation's rollback semantics are genuinely nice once you stop fighting them.
- Multi-cloud (AWS + GCP, AWS + Azure): Terraform or OpenTofu. The provider ecosystem is unmatched, and HCL is boring in a good way.
- Multi-cloud with a Go/Python team that hates HCL: Pulumi. Real languages, real test frameworks, and the
pulumi convert --from tfpath lets you start from existing Terraform code. - Regulated industry with BSL licensing concerns: OpenTofu. Drop-in replacement, MPL 2.0, Linux Foundation governance.
- Kubernetes-only infrastructure: Crossplane or Kubernetes-native tools. None of the three in this guide are ideal if every resource is a Kubernetes CRD.
Pro tip: whatever you pick, invest in modules / components early. The long-term cost of IaC is not the tool -- it is how often you reinvent the same VPC + subnet + IAM scaffolding across projects. A good internal module library pays for itself in weeks.
Frequently Asked Questions
Which IaC tool should a beginner start with?
Start with Terraform. It has the largest community, the most learning resources, and skills transfer to any cloud provider. HCL is simpler than learning a general-purpose language's IaC patterns. Once you're comfortable with IaC concepts, you can evaluate CDK or Pulumi if their strengths align with your needs.
Can I use Terraform and CDK together?
Yes, but carefully. Some teams use Terraform for shared infrastructure (networking, DNS) and CDK for application-specific resources. The key is clear ownership boundaries -- don't manage the same resource from both tools. Use Terraform outputs and CDK's Fn.importValue to pass values between them.
What is OpenTofu and should I use it?
OpenTofu is a community fork of Terraform created after HashiCorp changed Terraform's license from MPL 2.0 to BSL 1.1. It's maintained by the Linux Foundation and is a drop-in replacement for Terraform. If the BSL license is a concern for your organization, OpenTofu is a viable alternative with growing community support.
How do I handle secrets in IaC?
Never store secrets in plain text in your IaC files. Terraform integrates with Vault, AWS Secrets Manager, and SSM Parameter Store via data sources. CDK can reference Secrets Manager and SSM parameters directly. Pulumi encrypts secrets in state by default and integrates with cloud KMS services.
Is CloudFormation still worth learning?
Only if you're deep in the AWS ecosystem and need to understand what CDK generates under the hood. Writing raw CloudFormation YAML/JSON is tedious compared to CDK or Terraform. However, understanding CloudFormation concepts (stacks, change sets, rollbacks) is valuable because CDK depends on them.
How do I migrate from manual infrastructure to IaC?
Use import commands (terraform import, cdk import, pulumi import) to bring existing resources under IaC management without recreating them. Start with the most critical resources (networking, databases) and work outward. Tools like Former2 can generate CloudFormation or Terraform from existing AWS resources automatically.
Pick a Tool and Commit
The worst IaC decision is using three tools across five teams. Pick one primary tool, standardize on it, and invest in modules and patterns that your team can reuse. Terraform is the safe default. CDK is the right choice for AWS-only shops that want TypeScript end-to-end. Pulumi is compelling for multi-cloud teams that want real languages without CloudFormation's limitations. All three are production-proven -- the choice matters less than consistency.
Written by
Abhishek Patel
Infrastructure engineer with 10+ years building production systems on AWS, GCP, and bare metal. Writes practical guides on cloud architecture, containers, networking, and Linux for developers who want to understand how things actually work under the hood.
Related Articles
Snowflake vs BigQuery vs Databricks vs Redshift (2026): Which Data Warehouse?
Snowflake wins on concurrency, BigQuery on serverless simplicity, Databricks on ML, Redshift on AWS depth. Real 2026 pricing, TPC-DS benchmarks, and a clear decision matrix.
16 min read
AI/ML EngineeringRunPod vs Vast.ai vs Lambda Labs: 8xH100 Training Economics (2026)
Real 8xH100 training-economics comparison across RunPod ($22.32/hr Secure Cloud), Vast.ai (spot $12.16/hr floor), and Lambda Labs (reserved $14.80/hr). MFU benchmarks, break-even math for spot vs reserved, interruption rates, and which provider wins per job shape.
16 min read
CloudRender vs Railway vs Fly.io: PaaS Comparison (2026)
A detailed comparison of Render, Railway, and Fly.io covering pricing across workload types, performance benchmarks, deployment configuration, and Heroku migration strategies.
12 min read
Enjoyed this article?
Get more like this in your inbox. No spam, unsubscribe anytime.