Terraform vs Pulumi vs CDK: Which IaC Tool for 2026?

Kehinde Ogunlowo
2026-04-03T00:00:00-05:00

Detailed comparison of Terraform 1.9, Pulumi 3.x, and AWS CDK 2.x for infrastructure as code. Covers language, state, testing, multi-cloud, and team fit.

More

Terraform vs Pulumi vs CDK: Which IaC Tool Should You Learn in 2026?

Infrastructure as Code adoption crossed the 90% mark among engineering organizations in 2025 (HashiCorp State of Cloud Strategy Report). The question is no longer whether to use IaC — it is which tool fits your team, cloud strategy, and operational model. Three tools dominate the conversation: Terraform (and its open-source fork OpenTofu), Pulumi, and AWS CDK. Each makes different tradeoffs, and the right choice depends on variables most comparison articles ignore.

This guide provides a detailed, production-informed comparison across the dimensions that actually matter: language ergonomics, state management, testing capabilities, multi-cloud support, ecosystem maturity, and team adoption patterns. The goal is to help you make a decision you will not regret 18 months from now.

The Contenders (April 2026 Versions)

| Terraform | OpenTofu | Pulumi | AWS CDK
Current version | 1.9.x (BSL 1.1) | 1.8.x (MPL 2.0) | 3.x (Apache 2.0) | 2.x (Apache 2.0)
Vendor | HashiCorp (IBM) | Linux Foundation | Pulumi Corp | AWS
Language | HCL | HCL | Python, TypeScript, Go, C#, Java, YAML | TypeScript, Python, Java, C#, Go
Multi-cloud | Native (1000+ providers) | Native (Terraform-compatible) | Native (100+ providers) | AWS only (CDKTF for multi-cloud)
State | Remote backends / Terraform Cloud | Remote backends / compatible with TF | Pulumi Cloud / self-managed | CloudFormation (AWS-managed)
License | BSL 1.1 (source available, not open source) | MPL 2.0 (open source) | Apache 2.0 (open source) | Apache 2.0 (open source)

[IMAGE: Comparison matrix showing Terraform, Pulumi, and AWS CDK logos side by side with radar charts comparing each tool across six dimensions: multi-cloud support, language flexibility, testing capabilities, ecosystem maturity, learning curve, and enterprise adoption]

Language and Developer Experience

This is the dimension that generates the most debate and the strongest opinions.

Terraform HCL

HCL is a domain-specific language designed exclusively for infrastructure definition. It is declarative: you describe the desired end state, and Terraform figures out the operations to reach it.

resource "aws_lambda_function" "processor" {
  function_name = "order-processor"
  runtime       = "python3.12"
  handler       = "handler.process"
  memory_size   = 512
  timeout       = 30
  environment {
    variables = {
      TABLE_NAME = aws_dynamodb_table.orders.name
      QUEUE_URL  = aws_sqs_queue.notifications.url
    }
  }
  tracing_config {
    mode = "Active"
  }
  tags = local.common_tags
}

Strengths: Readable, predictable, forces declarative thinking. HCL's limited expressiveness is a feature — it prevents teams from writing complex imperative logic in infrastructure definitions. Plans are deterministic and reviewable.

Weaknesses: Dynamic patterns require workarounds (for_each, dynamic blocks, locals with complex expressions). Conditional logic is awkward: count = var.enable_feature ? 1 : 0. No real unit testing framework built into the language — you need external tools like terratest (Go) or terraform test (added in 1.6, still limited).

Pulumi (General-Purpose Languages)

Pulumi lets you define infrastructure using languages your team already knows. The same aws.lambda.Function in Python:

import pulumi
import pulumi_aws as aws
processor = aws.lambda_.Function(
    "order-processor",
    runtime="python3.12",
    handler="handler.process",
    memory_size=512,
    timeout=30,
    environment=aws.lambda_.FunctionEnvironmentArgs(
        variables={
            "TABLE_NAME": orders_table.name,
            "QUEUE_URL": notifications_queue.url,
        }
    ),
    tracing_config=aws.lambda_.FunctionTracingConfigArgs(
        mode="Active"
    ),
    tags=common_tags,
)

Strengths: Full language capabilities — loops, conditionals, classes, error handling, type checking. You can write infrastructure libraries with real abstractions. Testing uses your language's native test framework (pytest, Jest, Go test). IDE support (autocompletion, type hints, refactoring) comes free.

Weaknesses: Expressiveness is a double-edged sword. Teams can write overly complex, imperative infrastructure code that is hard to review and reason about. The declarative constraint that HCL enforces is genuinely useful for infrastructure. Pulumi's provider ecosystem, while growing, still lags Terraform's.

AWS CDK (TypeScript/Python on CloudFormation)

CDK synthesizes CloudFormation templates from higher-level constructs:

import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
const processor = new lambda.Function(this, 'OrderProcessor', {
  runtime: lambda.Runtime.PYTHON_3_12,
  handler: 'handler.process',
  memorySize: 512,
  timeout: Duration.seconds(30),
  environment: {
    TABLE_NAME: ordersTable.tableName,
    QUEUE_URL: notificationsQueue.queueUrl,
  },
  tracing: lambda.Tracing.ACTIVE,
});
// CDK handles IAM permissions automatically
ordersTable.grantReadWriteData(processor);
notificationsQueue.grantSendMessages(processor);

Strengths: L2 and L3 constructs encode AWS best practices. The grantReadWriteData() call above generates a least-privilege IAM policy automatically — no manual policy writing. CDK Pipelines provides CI/CD out of the box. For AWS-only shops, CDK is the fastest path to production.

Weaknesses: AWS only. CloudFormation as the execution engine means you inherit its limitations: 500-resource stack limits, slow deployments for large stacks, cryptic error messages when synthesis fails. Drift detection depends on CloudFormation's capabilities.

State Management

State management is the operational concern most teams underestimate until it causes an incident.

Terraform State

Terraform state is a JSON file tracking the mapping between your configuration and real infrastructure. You must manage it:

Remote backends: S3 + DynamoDB (locking), Azure Blob + Table Storage, GCS

Terraform Cloud / HCP Terraform: Managed state with RBAC, audit logging, run history

State locking: Critical for team workflows — prevents concurrent modifications

terraform {
  backend "s3" {
    bucket         = "citadel-terraform-state"
    key            = "production/vpc/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}

State is both Terraform's strength (accurate drift detection, dependency tracking) and its operational burden (state corruption, state file size, import complexity).

Pulumi State

Similar model to Terraform, with Pulumi Cloud as the default backend:

Pulumi Cloud: Managed state with RBAC, audit trail, secrets management, deployment history

Self-managed: S3, Azure Blob, GCS, or local filesystem

Secrets: Pulumi encrypts secrets in state by default (Terraform does not without external tooling)

CDK State

CDK delegates entirely to CloudFormation. No state file to manage. AWS handles state, locking, and rollback. The tradeoff: you cannot easily inspect or manipulate state, and you are dependent on CloudFormation's drift detection capabilities.

Testing Capabilities

Capability | Terraform | Pulumi | CDK
Unit tests | terraform test (HCL-based, limited) | Native (pytest, Jest, Go test) | Native (Jest, pytest) with assertions lib
Integration tests | Terratest (Go) | pulumi.automation API | integ-runner + integ-tests
Policy as code | Sentinel (paid) / OPA / tfsec | CrossGuard (built-in) | cdk-nag (built-in)
Snapshot testing | Not native | pulumi preview --diff | CDK snapshot tests (Jest)
Mocking | Limited | Full language mocking | Full language mocking

CDK Snapshot Testing Example

import { Template } from 'aws-cdk-lib/assertions';
test('Lambda function has correct configuration', () => {
  const template = Template.fromStack(stack);
  template.hasResourceProperties('AWS::Lambda::Function', {
    Runtime: 'python3.12',
    MemorySize: 512,
    Timeout: 30,
    TracingConfig: { Mode: 'Active' },
  });
  // Verify IAM policy grants
  template.hasResourceProperties('AWS::IAM::Policy', {
    PolicyDocument: Match.objectLike({
      Statement: Match.arrayWith([
        Match.objectLike({
          Action: Match.arrayWith(['dynamodb:GetItem', 'dynamodb:PutItem']),
          Effect: 'Allow',
        }),
      ]),
    }),
  });
});

Pulumi Unit Testing Example

import pulumi
import pytest
class MockResource(pulumi.runtime.Mocks):
    def new_resource(self, args):
        return [args.name + "_id", args.inputs]
    def call(self, args):
        return {}
@pulumi.runtime.test
def test_lambda_memory():
    pulumi.runtime.set_mocks(MockResource())
    from infra.compute import processor
    def check_memory(args):
        memory = args[0]
        assert memory == 512, f"Expected 512, got {memory}"
    pulumi.Output.all(processor.memory_size).apply(check_memory)

Pulumi and CDK have a clear testing advantage due to native language test frameworks. Terraform's terraform test (introduced in 1.6) is functional but limited compared to full-language testing capabilities.

Multi-Cloud Support

Scenario | Best Tool | Why
AWS only | CDK or Terraform | CDK for AWS best practices; Terraform for provider ecosystem
AWS + Azure | Terraform or Pulumi | Both have mature providers for both clouds
AWS + GCP + Azure | Terraform | Largest provider ecosystem, most battle-tested multi-cloud
Multi-cloud with strong typing | Pulumi | TypeScript/Go providers with full type safety
Kubernetes + cloud resources | Terraform or Pulumi | Both have Kubernetes providers; Pulumi's typing advantage

Terraform's provider ecosystem is its strongest competitive advantage: 4,000+ providers covering every major cloud service, SaaS platform, and infrastructure tool. Pulumi covers the major clouds well but has gaps for niche providers. CDK is AWS-only (CDKTF exists for multi-cloud but adds complexity).

[IMAGE: Venn diagram showing provider/integration coverage for Terraform (4000+ providers), Pulumi (100+ providers), and CDK (AWS services only with CDKTF overlay), highlighting unique and overlapping coverage areas]

Decision Framework

Choose Terraform When:

Your team manages multi-cloud infrastructure (AWS + Azure + GCP)

You value declarative constraints that prevent overly complex infrastructure code

You need the broadest provider ecosystem (databases, monitoring, DNS, CDN, SaaS integrations)

Your organization has existing Terraform expertise and module libraries

You want the largest hiring pool (Terraform remains the most-requested IaC skill in job postings)

Choose Pulumi When:

Your infrastructure team consists primarily of software engineers (not ops/SRE specialists)

You need complex abstractions — shared libraries, reusable components with real type safety

Testing infrastructure code is a priority and you want native language test frameworks

You manage Kubernetes clusters alongside cloud resources and want a unified tool

You need built-in secrets management in state

Choose CDK When:

Your organization is 100% AWS with no plans to use other clouds

You want AWS best practices encoded into high-level constructs (L2/L3)

Automatic IAM policy generation is valuable (it is — IAM is the hardest part of AWS)

Your team writes TypeScript or Python and wants infrastructure to feel like application code

You are comfortable with CloudFormation as the execution engine

Choose OpenTofu When:

You need Terraform compatibility with a true open-source license (MPL 2.0)

BSL 1.1 licensing concerns apply to your organization

You want community governance through the Linux Foundation

You need state encryption at rest (OpenTofu added this before Terraform)

What to Learn in 2026

If you are starting from zero: learn Terraform first. It has the largest market demand, the broadest applicability, and the most learning resources. The HCL mental model (declarative, plan-before-apply) transfers to every other IaC tool.

If you already know Terraform: add Pulumi or CDK as a second tool. Pulumi if you work multi-cloud or want stronger testing. CDK if you are deep in the AWS ecosystem.

Citadel Cloud Management's DevOps courses cover Terraform from fundamentals through advanced module development, plus dedicated modules on Pulumi and CDK. Our DevOps Tools collection includes production-ready Terraform modules, Pulumi component resources, and CDK constructs for common infrastructure patterns. Start with the free resources to build foundational skills.

Ready to master infrastructure as code? Enroll free at Citadel Cloud Management and start building production infrastructure today.

Terraform #Pulumi #CDK #InfrastructureAsCode #DevOps #CloudEngineering #OpenTofu #AWS #MultiCloud #IaC

Continue Learning

Explore 17 Free Cloud Courses

Browse DevOps Pipelines

Terraform vs CloudFormation vs Pulumi: Infrastructure as Code Comparison for 2026

CI/CD Pipeline Best Practices for Cloud Teams

Docker vs Kubernetes: What Cloud Engineers Need to Know