AWS CDK vs Terraform: The Complete 2026 Comparison

AWS CDK vs Terraform: The Complete 2026 Comparison

AWS CDK vs Terraform 2026 comparison: provisioning speed benchmarks, side-by-side code, CDKTF deprecation guide, and a clear decision framework.

January 6th, 2026
0 views
--- likes

If you're a software developer managing AWS-only infrastructure, CDK will almost certainly make you more productive. If you're a platform engineer managing multi-cloud or working alongside an ops-heavy team, Terraform is probably the right call.

That's the short answer. But the infrastructure as code decision is a 3-5 year commitment, and two events in 2025 changed the calculus in ways most comparisons haven't caught up to: CDKTF was deprecated by HashiCorp (eliminating the "best of both worlds" escape hatch), and CDK gained a series of major features (CDK Toolkit Library, CDK Refactor, and CDK Mixins) that widen the gap against Terraform for AWS-native teams.

Here's what this guide gives you: a specific recommendation for your situation, a performance benchmark you can reference, side-by-side code showing the real abstraction difference, a migration path if you need to switch tools, and an honest answer to the CDKTF question.

I've deployed production CDK stacks across client engagements and maintained both CDK and Terraform codebases. The differences I describe below are practical, not theoretical.

Executive Summary: Which Tool Should You Choose?

Let me give you the direct answer before we dig into the details.

Quick Recommendation Matrix

Choose AWS CDK if:

  • Your infrastructure is 100% AWS
  • Your team are developers who prefer TypeScript, Python, Java, C#, or Go
  • You want sensible defaults and high-level abstractions (L2/L3 constructs)
  • You don't need multi-cloud support now or in the foreseeable future

Choose Terraform if:

  • You need multi-cloud support (AWS, Azure, GCP)
  • You prefer declarative configuration over programming
  • You have existing Terraform expertise and modules
  • Your ops team (not developers) manages infrastructure

Consider both if: You have complex use cases where CDK handles application infrastructure and Terraform manages foundational or multi-cloud resources. This adds real complexity, so only go hybrid if you have specific reasons.

TL;DR: Key Takeaways

  1. Neither is "better": they're optimized for different scenarios
  2. CDK = AWS-native programming with automatic CloudFormation-managed state, rich L2/L3 abstractions, and a fast-moving 2025-2026 feature set (Toolkit Library, Refactor, Mixins)
  3. Terraform = multi-cloud declarative with manual state management and a mature BSL-licensed ecosystem
  4. CDKTF is dead: HashiCorp deprecated it in December 2025; that exit ramp is closed
  5. CDK deploys via CloudFormation, which is roughly 2-3x slower than Terraform for the same resource set due to the intermediary service layer
  6. Hybrid is valid but complex: use it only with clear team and resource boundaries

If you want more depth to validate this recommendation, the comparison table below shows the key technical differences.

The Quick Comparison Table

Here's a side-by-side view of the major differences, now including Pulumi:

FeatureAWS CDKTerraformPulumi
LanguagesTypeScript, Python, Java, C#, Go, JavaScriptHCL (HashiCorp Configuration Language)TypeScript, Python, Go, .NET, Java, YAML
Cloud SupportAWS onlyMulti-cloud (3,000+ providers)Multi-cloud (50+ providers)
State ManagementAutomatic via CloudFormationManual configuration (S3 backend recommended)Pulumi Cloud or self-managed backend
Deployment EngineCloudFormationTerraform engine (direct AWS API calls)Pulumi engine (direct AWS API calls)
RollbackAutomatic on failureManual intervention requiredManual intervention required
TestingBuilt-in assertions moduleThird-party tools (Terratest, etc.)Built-in testing support
Setup RequiredBootstrap per account/regionBackend configuration per projectNo bootstrapping required
AbstractionsL1, L2, L3 constructs + MixinsFlat module structureFlat component structure
Current VersionConstruct Library 2.243.0, CLI 2.1000+BSL (since August 2023)Apache 2.0 (open source)
LicensingApache 2.0 (open source)Business Source License (BSL)Apache 2.0 (open source)
Learning Curve (Devs)Low (familiar languages)Medium (learn HCL)Low (familiar languages)
Learning Curve (Ops)Medium (requires programming)Low (declarative config)Medium (requires programming)
Drift Detectioncdk drift (wraps CloudFormation detection)terraform plan / terraform refreshpulumi refresh

Why these differences matter: CDK generates CloudFormation templates and uses AWS's deployment engine. Terraform and Pulumi both call AWS APIs directly. This architectural difference affects provisioning speed, rollback behavior, and how you debug failures.

Understanding the Fundamentals

Before comparing these tools, you need to understand what they actually do under the hood. Both are Infrastructure as Code tools, but they approach the problem from different angles.

What is AWS CDK?

AWS CDK (Cloud Development Kit) is an open-source framework that lets you define cloud infrastructure using familiar programming languages. Your CDK code is synthesized into CloudFormation templates, which CloudFormation then deploys to provision resources.

Key characteristics:

  • Supports six languages: TypeScript, JavaScript, Python, Java, C#, and Go
  • Synthesizes to CloudFormation: CDK isn't a replacement for CloudFormation. It's a layer on top
  • Three levels of constructs for different abstraction needs, plus CDK Mixins (GA March 2026) for cross-cutting features
  • Version structure: Since February 2025, the CDK CLI (now at 2.1000+) and Construct Library (currently at 2.243.0) follow independent release cadences, so the version numbers will look mismatched, but that's intentional

The construct model is the heart of CDK:

  • L1 (CFN Resources): Direct 1:1 mapping to CloudFormation resources (e.g., CfnBucket). No abstraction, complete control
  • L2 (Curated Constructs): Opinionated resources with sensible defaults and best practices built in (e.g., s3.Bucket). Most commonly used
  • L3 (Patterns): Multi-resource architectures that create complete solutions (e.g., ApplicationLoadBalancedFargateService creates ECS Fargate + ALB with one construct)
  • Mixins: Cross-cutting features (encryption, auto-delete, compliance settings) applied to any L1 or L2 construct via .with(), available as of March 2026

If you want to dig into CDK fundamentals, check out my beginner's guide to AWS CDK.

What is Terraform?

Terraform is a platform-agnostic Infrastructure as Code tool using HashiCorp Configuration Language (HCL). It manages infrastructure through its own deployment engine and state files, supporting multiple cloud providers and services.

Key characteristics:

  • Uses HCL: A declarative configuration language designed to be human-readable
  • Platform-agnostic: Supports AWS, Azure, GCP, and 3,000+ other providers through plugins
  • State files: JSON files that track your deployed infrastructure (critical for knowing what exists)
  • Own deployment engine: Terraform handles deployments by calling cloud APIs directly, not through CloudFormation

The Terraform workflow:

  1. terraform init - Initialize working directory, download providers
  2. terraform plan - Preview what changes will be applied
  3. terraform apply - Provision or update infrastructure
  4. terraform destroy - Remove all managed infrastructure

The Imperative vs Declarative Paradigm

Here's the fundamental difference in how you think about infrastructure:

CDK is imperative: you write code that describes HOW to create infrastructure using programming logic (loops, conditionals, functions). You have full programming capabilities at your disposal.

Terraform is declarative: you write configuration that describes WHAT infrastructure should exist, not how to create it. The tool figures out how to get there.

Let me show you the difference with a simple S3 bucket:

// CDK (TypeScript) - imperative approach
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as cdk from 'aws-cdk-lib';

const bucket = new s3.Bucket(this, 'MyBucket', {
  encryption: s3.BucketEncryption.S3_MANAGED,
  versioned: true,
  removalPolicy: cdk.RemovalPolicy.RETAIN
});
# Terraform (HCL) - declarative approach
resource "aws_s3_bucket" "my_bucket" {
  bucket = "my-unique-bucket-name"
}

resource "aws_s3_bucket_versioning" "my_bucket" {
  bucket = aws_s3_bucket.my_bucket.id
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "my_bucket" {
  bucket = aws_s3_bucket.my_bucket.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

Notice how CDK's L2 construct handles encryption and versioning with simple properties, while Terraform requires separate resource blocks. This is the abstraction benefit of CDK's constructs, but it also means less explicit visibility into what's being created.

Neither paradigm is superior. They serve different mental models and team skillsets.

CDK vs Terraform vs Pulumi: The Third Option

Most comparisons treat this as a two-horse race. But search trends show a meaningful volume of queries for "AWS CDK vs Terraform vs Pulumi," and since CDKTF is now deprecated, Pulumi is the answer teams reach for when they want programming languages plus multi-cloud support.

Pulumi isn't the right default, but it belongs in your decision. Here's how to think about where it fits.

Where Pulumi Fits

Pulumi uses general-purpose programming languages (TypeScript, Python, Go, .NET, Java, YAML) across more than 50 cloud providers. State is managed via Pulumi Cloud (managed service) or a self-hosted backend, which is neither the automatic CloudFormation approach of CDK nor the fully manual setup of Terraform.

AWS Prescriptive Guidance describes Pulumi this way: if your organization can tolerate a high level of risk and needs to support multi-cloud or hybrid-cloud environments, consider Pulumi. The "high level of risk" qualifier is significant: it refers to Pulumi's smaller community relative to Terraform, which means fewer battle-tested modules, less StackOverflow coverage, and a steeper debugging path when things go wrong.

Pulumi is licensed under Apache 2.0, which is one concrete advantage over Terraform's BSL since CDKTF was deprecated.

When to Consider Each

Here's the decision logic:

The short version:

  • AWS-only and your team writes code: CDK wins
  • Multi-cloud and your team writes code: Pulumi is worth evaluating
  • Multi-cloud and your team prefers declarative config: Terraform
  • Mixed team with no clear IaC owner: Terraform's HCL is the neutral middle ground

Now that you know where Pulumi fits, let's return to the primary comparison and start with what's changed in CDK since most comparison articles were written.

What Changed in 2025-2026: CDK's New Features

People searching for "aws cdk latest version 2026" and "aws cdk updates 2026" are landing on this post without finding the answer. This section fixes that.

AWS CDK shipped four significant features in the 15 months between February 2025 and March 2026. None of these appear in competitor posts yet. If you're reading a comparison article that doesn't mention CDK Mixins or CDK Refactor, you're reading outdated information.

DateFeatureWhat It Solves
February 2025CLI/Construct Library version splitCLI and library now release independently; CLI jumps to 2.1000+ range while Construct Library continues 2.x numbering
May 2025CDK Toolkit Library (GA)Programmatic CDK operations without CLI subprocess calls
September 2025CDK Refactor (Preview)Rename constructs or move them between stacks without resource replacement
March 2026CDK Mixins (GA)Apply cross-cutting features to any construct via .with() without subclassing

CDK Toolkit Library: Programmatic CDK Without CLI Subprocesses

The CDK Toolkit Library (@aws-cdk/toolkit-lib) gives you programmatic access to CDK operations (synth, deploy, rollback, destroy, watch) without spawning CLI subprocess calls. Install with:

npm install --save @aws-cdk/toolkit-lib

This matters for teams building custom CI/CD workflows, automation tooling, or ephemeral environment managers. Instead of shelling out to cdk deploy from your pipeline code, you call the library directly. It also opens the door to programmatically enforcing guardrails: your CI tool can inspect the synthesized template before allowing deployment.

CDK Refactor: Safe Code Restructuring Without Resource Replacement

This one is a big deal for anyone who has ever had a CDK stack grow organically and then wanted to reorganize it. Before CDK Refactor, renaming a construct or moving it between stacks caused CloudFormation to treat it as a new resource, which means deleting and recreating the old one. For stateful resources (databases, S3 buckets, SQS queues), that's a production incident waiting to happen.

The cdk refactor command (using --unstable=refactor) detects when resources have been moved or renamed, then uses CloudFormation's refactoring API to update logical IDs without touching the underlying physical resources. It verifies your CDK app contains exactly the same set of resources as the deployed state before proceeding.

One constraint: if you need to add, delete, or modify resources at the same time as a refactor, you have to split those into separate deployments. It's a meaningful limitation, but it's a lot better than losing data.

CDK Mixins: Cross-Cutting Features Without Subclassing

CDK Mixins solve a specific pain point: applying the same compliance or security settings (encryption, auto-delete, block public access) across many constructs without creating a sprawling inheritance hierarchy.

Before Mixins, you had three options when an L2 construct didn't support a feature natively: use an L1 construct directly, apply raw property overrides, or create a CloudFormation custom resource. Mixins add a fourth option: apply the feature via .with() on any L1 or L2 construct:

new s3.CfnBucket(scope, 'MyL1Bucket')
  .with(new BucketBlockPublicAccess())
  .with(new BucketAutoDeleteObjects());

You can also combine multiple Mixins into a custom compliance policy and apply it across an entire scope. For teams building internal CDK libraries with governance requirements, this is a meaningful reduction in boilerplate. See my post on CDK best practices for production for how to apply this in practice.

These CDK updates matter specifically to the CDKTF deprecation story, because some users were on CDKTF precisely to get the programming language experience that CDK now delivers natively on AWS.

The CDKTF Deprecation: What It Means for Your Decision

If you searched "AWS CDK for Terraform," you're in the right place: that's CDKTF, and this section explains exactly what happened to it.

What Was CDKTF?

CDK for Terraform (CDKTF) was HashiCorp's attempt to combine CDK's programming language benefits with Terraform's multi-cloud capabilities. Built on the JSII library (the same engine that powers AWS CDK), it enabled writing Terraform configurations in C#, Python, TypeScript, Java, or Go.

The workflow: define infrastructure using CDK-style constructs, CDKTF generates Terraform HCL configurations, then deploy using standard Terraform workflows.

It promised the best of both worlds: familiar programming languages with Terraform's multi-cloud provider ecosystem.

Why HashiCorp Deprecated It

HashiCorp deprecated CDKTF in December 2025. The likely reasons include:

  • Maintenance burden of keeping two abstraction layers in sync
  • Limited adoption compared to native Terraform
  • Complexity that didn't justify the benefits
  • Strategic focus on the core Terraform product

One nuance worth noting: as of March 2026, AWS documentation still references CDKTF as a viable tool; AWS prescriptive guidance hasn't been updated to reflect the HashiCorp-side deprecation decision. So if you're reading AWS docs and see CDKTF mentioned, that reflects documentation lag, not a resurrection.

What to Do If You're Currently on CDKTF

Existing CDKTF projects continue to work; the deprecation means no new features or updates, not immediate breakage. There's no forced migration deadline.

But you should plan a migration path. Here's the decision:

  • If your infrastructure is AWS-only: migrate to native AWS CDK. Your programming language skills transfer directly. CDKTF constructs closely resemble CDK constructs; the main work is replacing CDKTF provider constructs with CDK L1/L2 equivalents. The new CDK features (Mixins, Refactor, Toolkit Library) make native CDK more capable than CDKTF ever was for AWS workloads.
  • If you genuinely need multi-cloud: evaluate Pulumi. It's architecturally the closest successor: programming languages, multi-cloud, actively maintained, and Apache 2.0 licensed. The community is smaller than Terraform's, but it's the right answer if you need both programming languages and multi-cloud.
  • If your team prefers declarative: move to native Terraform HCL. CDKTF was generating HCL anyway, so the migration removes an abstraction layer and gives you full Terraform ecosystem access.

The key question: were you on CDKTF primarily for the programming language experience, with AWS-focused infrastructure? If yes, CDK is your answer.

Implications for Tool Selection Today

Don't choose CDKTF: it's deprecated and won't evolve.

The "best of both worlds" doesn't exist anymore. You have to choose between CDK (AWS-only) or Terraform/Pulumi (multi-cloud).

This validates the binary choice we're discussing: if HashiCorp couldn't make the synthesis approach work well, hybrid approaches are likely complex.

Now that the CDKTF question is settled, let's address the second recent change affecting Terraform: its licensing shift.

The Licensing Question: Terraform's BSL and What It Means

Terraform's License Change Explained

Terraform moved from Mozilla Public License (open source) to Business Source License (BSL) in August 2023. This is not a minor change.

BSL restricts commercial use in certain scenarios, specifically around creating competing products. The license automatically converts to open source after a specified time period.

AWS Prescriptive Guidance documents this directly as a disadvantage of Terraform: "In August 2023, HashiCorp made an announcement that it would no longer be licensed as open source under the Mozilla Public License. Instead, it is now licensed under the Business Source License."

Practical Implications for Your Organization

For most users: No change. You can still use Terraform freely for infrastructure management.

For enterprises: Review with your legal team if you're a cloud provider or building competing IaC products.

For open-source advocates: The loss of pure open-source licensing may be a philosophical concern.

Future uncertainty: HashiCorp could change license terms again.

The Fundamental Business Model Problem

Here's a perspective that often gets overlooked in technical comparisons.

AWS's position: AWS doesn't need to monetize CloudFormation or CDK directly. They make money when you run resources on AWS. CDK exists to help you deploy infrastructure faster, safer, and more automatically, which means more AWS consumption.

AWS's aligned incentives: The better CDK is, the more AWS resources you'll deploy. Their business model aligns with your success.

HashiCorp's dilemma: No cloud infrastructure revenue means they must monetize the tooling itself. This is why Terraform Cloud exists and why the license changed.

Why the license changed: Other companies (Spacelift, env0, Scalr) were building commercial products on top of open-source Terraform, threatening HashiCorp's revenue.

Strategic risk: When your IaC vendor's survival depends on monetizing the tool, future changes may not align with your interests. AWS will continue investing in CDK because it drives their core business. This stability doesn't exist for Terraform.

This isn't a technical limitation; it's a business risk factor for long-term infrastructure decisions.

OpenTofu as an Alternative

OpenTofu is a Terraform fork created in response to the BSL change, backed by the Linux Foundation. AWS's acknowledgment of the BSL impact is visible in a concrete operational change: AWS Service Catalog replaced all references to "Terraform Open Source" as a product type with "External," a generic label that now covers OpenTofu and any other Terraform-compatible tool. AWS Control Tower made the same transition in 2023.

AWS doesn't explicitly name OpenTofu in its documentation, but the "External" product type signal is clear. If you're using AWS Service Catalog with Terraform, you'll need to migrate existing products to the External product type.

Consider OpenTofu if open-source licensing is critical to your organization. Trade-off: smaller ecosystem and less mature than either Terraform or CDK.

Provisioning Speed: CDK vs Terraform vs Pulumi

This is one of the most common questions in the AWS CDK vs Terraform comparison, and it gets very few honest answers. Let me give you one.

There are no AWS-published benchmarks comparing CDK, Terraform, and Pulumi provisioning speeds. Anyone citing specific numbers as "official" is making them up. But the architectural reason for the speed difference is well-documented and explainable, and the practical difference is real.

Why CDK Deploys Through CloudFormation

CDK does not provision resources itself. Here's the actual deployment chain:

  1. cdk synth runs locally: CDK code compiles to CloudFormation JSON/YAML templates (this step is fast, happens on your machine)
  2. CDK CLI uploads assets to S3/ECR in the bootstrapped environment
  3. CDK CLI submits the CloudFormation template to CloudFormation service
  4. CloudFormation provisions your AWS resources

The synthesis step (step 1) is local and fast. Steps 2-4 involve the CloudFormation service as an intermediary, and CloudFormation adds overhead because it's managing state, processing events, and handling rollback logic internally. CloudFormation does manage resource parallelism automatically, but the service overhead is real.

How Terraform and Pulumi Deploy Directly

Terraform and Pulumi skip the intermediary:

  1. terraform plan computes changes by comparing state and target configuration
  2. terraform apply calls AWS APIs directly in dependency order, provisioning resources in parallel where no dependencies exist

There's no CloudFormation service involved. Terraform talks directly to the AWS API. This is architecturally faster because you remove an entire service layer from the critical path.

Benchmark Data and What to Expect

Based on deploying typical stacks across client environments, here's what I observe:

  • A stack of 10-20 resources: CDK/CloudFormation typically takes 3-5 minutes; Terraform typically 1-3 minutes for the same resources
  • Large, complex stacks (100+ resources): The gap narrows because CloudFormation's automatic dependency ordering and parallelism partially offset its overhead
  • Lambda and ECS updates in development: CDK's cdk watch command uses hot-swap deployments that bypass CloudFormation entirely for code-only changes, which is dramatically faster and partially closes the speed gap in development workflows

To be clear: these are practitioner estimates based on typical workloads, not formal benchmarks. Your actual results will vary by resource type, stack complexity, and AWS region capacity.

The bottom line: if raw provisioning speed is your primary concern, Terraform has an architectural advantage. If you're on AWS and speed matters specifically in development iteration, CDK's cdk watch feature narrows the gap considerably.

Understanding why CDK is slower leads directly to understanding its deepest technical difference from Terraform: the abstraction level. Let's look at that with actual code.

Core Differences That Matter

Now let's get into the technical differences that affect your day-to-day work.

Language and Syntax

CDK: Use your preferred programming language with full IDE support. You get autocomplete, type checking, refactoring, and all the tooling you're used to. TypeScript is the most popular choice, but Python, Java, C#, and Go are all first-class citizens.

Terraform: Learn HCL, a declarative language designed specifically for infrastructure. It's relatively simple but requires learning new syntax. IDE support exists through plugins but isn't as rich as general-purpose language tooling.

CDK advantage: Leverage existing language skills, strong typing prevents configuration errors at compile time.

Terraform advantage: HCL is easier for non-developers to read and learn. A declarative config file is more approachable than imperative code for many ops teams.

Multi-Cloud vs AWS-Native Focus

Terraform's superpower: 3,000+ providers enable multi-cloud (AWS, Azure, GCP) and SaaS integration (GitHub, Datadog, Kubernetes, etc.). If you need to manage anything beyond AWS, Terraform likely has a provider.

CDK's focus: Deep AWS integration with no multi-cloud support. You cannot deploy Azure or GCP resources with CDK.

AWS feature availability: CDK gets new AWS features when CloudFormation supports them (usually within a week). Terraform's AWS provider may lag by weeks or months for new features.

Abstraction Levels: Constructs vs Modules

This is where CDK's advantage is most visible with real code. Let me show you what an ECS Fargate service with an Application Load Balancer looks like in each tool.

CDK (L3 construct, roughly 8 lines of meaningful code):

import { ApplicationLoadBalancedFargateService } from 'aws-cdk-lib/aws-ecs-patterns';

const service = new ApplicationLoadBalancedFargateService(this, 'MyService', {
  cluster,
  taskImageOptions: {
    image: ecs.ContainerImage.fromRegistry('my-app:latest'),
  },
  memoryLimitMiB: 512,
  cpu: 256,
  desiredCount: 2,
  publicLoadBalancer: true,
});

Terraform (equivalent HCL, 80+ lines):

resource "aws_ecs_cluster" "main" {
  name = "my-cluster"
}

resource "aws_ecs_task_definition" "app" {
  family                   = "my-app"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = 256
  memory                   = 512
  execution_role_arn       = aws_iam_role.ecs_execution.arn

  container_definitions = jsonencode([{
    name  = "app"
    image = "my-app:latest"
    portMappings = [{
      containerPort = 80
      protocol      = "tcp"
    }]
  }])
}

resource "aws_lb" "main" {
  name               = "my-alb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb.id]
  subnets            = var.public_subnet_ids
}

resource "aws_lb_target_group" "app" {
  name        = "my-app-tg"
  port        = 80
  protocol    = "HTTP"
  vpc_id      = var.vpc_id
  target_type = "ip"
}

resource "aws_lb_listener" "http" {
  load_balancer_arn = aws_lb.main.arn
  port              = 80
  protocol          = "HTTP"

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.app.arn
  }
}

resource "aws_ecs_service" "app" {
  name            = "my-app"
  cluster         = aws_ecs_cluster.main.id
  task_definition = aws_ecs_task_definition.app.arn
  desired_count   = 2
  launch_type     = "FARGATE"

  network_configuration {
    subnets          = var.private_subnet_ids
    security_groups  = [aws_security_group.ecs_tasks.id]
    assign_public_ip = false
  }

  load_balancer {
    target_group_arn = aws_lb_target_group.app.arn
    container_name   = "app"
    container_port   = 80
  }
}
# Plus: security groups, IAM roles, ECS task execution role...

The CDK L3 construct creates the ECS cluster, task definition, service, ALB, target group, listener, security groups, and IAM roles, all with production-ready defaults. Terraform gives you more explicit control over each resource, but you're writing and maintaining that explicitness.

Terraform's advantage: You see exactly what's being created. No hidden resources, no magic defaults.

CDK advantage: L2/L3 constructs dramatically reduce boilerplate and bake in AWS best practices. When CDK Mixins adds a fourth composition pattern, you can apply compliance settings to L1 constructs without the full L2 rewrite.

For deep dives, see my posts on understanding CDK constructs and CDK stacks.

Learning Curve Reality Check

For developers: CDK is easier. You use familiar programming languages, existing tooling, and patterns you already know. Learning HCL is an extra cognitive load.

For ops teams: Terraform is easier. Declarative configuration is more approachable than learning to program. The mental model (describe what you want) is simpler than imperative code (describe how to get it).

For mixed teams: Terraform's HCL serves as a neutral middle ground that both developers and ops can work with.

CDK hidden complexity: You still need to understand CloudFormation behavior underneath. When debugging fails, you'll be reading CloudFormation error messages and understanding its state model.

Terraform hidden complexity: State management and HCL limitations take time to master. You'll learn the quirks of the state file the hard way.

Neither is "easy." They're different skill investments with different learning paths.

State Management Philosophy

State management is where these tools diverge most fundamentally.

CDK's CloudFormation-Managed State

CDK doesn't manage state directly; CloudFormation does it automatically.

When you deploy a CDK stack, CloudFormation tracks the state of all resources in that stack internally. There's no state file you manage or secure. CloudFormation operates under three modes per resource: Create, Update, Delete, and determines the current mode automatically based on the last deployment.

You can also run cdk drift to call CloudFormation's drift detection API, which compares actual resource state in AWS against expected configuration in CloudFormation. This is distinct from cdk diff, which compares your local CDK code against the deployed template. Both commands exist but serve different purposes: cdk diff is a pre-deployment check, cdk drift is a post-deployment health check.

Advantages:

  • Zero state management overhead
  • Automatic locking (no concurrent deployment conflicts)
  • No security configuration needed for state storage
  • No state file that could expose sensitive values
  • State visible via CloudFormation console or API

Limitations:

  • Tied to CloudFormation quotas and constraints (e.g., 500 resources per stack)
  • Less control over state operations
  • Can't easily inspect or manipulate state directly

Terraform's Manual State Configuration

Terraform stores state in JSON files (typically terraform.tfstate). By default this is local, but for team environments you configure a remote backend, almost always S3 for AWS users.

Updated recommended AWS setup (note: DynamoDB locking is now deprecated):

terraform {
  backend "s3" {
    bucket       = "myorg-terraform-states"
    key          = "myapp/production/tfstate"
    region       = "us-east-1"
    use_lockfile = true
  }
}

The use_lockfile = true parameter uses native S3 state locking, available since Terraform 1.10.0. The old approach (using a DynamoDB table for locking via dynamodb_table) is deprecated and will be removed in a future Terraform version. AWS Prescriptive Guidance explicitly recommends migrating to S3 native locking if you're on the old pattern.

Advantages:

  • Full control over state storage, versioning, and backup
  • Works with any cloud provider
  • Can inspect and manipulate state when needed

Challenges:

  • Must configure encryption, access controls, and locking manually
  • State file contains sensitive data (passwords, connection strings) in plain text, a real security risk if mishandled
  • Team coordination around state requires discipline

Security bottom line: Terraform's state file exposure is higher risk. You need to be deliberate about state security: encrypt at rest with SSE-KMS, restrict IAM access to the state bucket, enable S3 versioning for rollback capability. CDK's automatic CloudFormation-managed state sidesteps this entire problem.

Setup and Prerequisites

What do you need before deploying anything?

CDK Bootstrap Requirements

CDK requires a one-time bootstrapping process per AWS account/region combination. As of March 2025, bootstrap templates v26 and v27 are current.

What bootstrapping creates (the CDKToolkit CloudFormation stack):

  • S3 bucket for storing Lambda code and assets (naming: cdk-hnb659fds-assets-ACCOUNT-REGION)
  • ECR repository for Docker images
  • IAM roles: CloudFormationExecutionRole, DeploymentActionRole, FilePublishingRole, ImagePublishingRole, LookupRole

Command: cdk bootstrap

Node.js requirement: Node.js 18.x support ended November 30, 2025. Node.js 22.x is now the recommended minimum.

The bootstrap stack is safe to run multiple times; it upgrades the template version if necessary. Re-bootstrapping is how you pick up new IAM role configurations or S3 bucket policies when AWS updates the bootstrap template.

For a detailed walkthrough, see my guide on AWS CDK bootstrapping.

If you want to skip the manual setup and start with a production-ready foundation, check out the AWS CDK Starter Kit. It includes secure OIDC authentication, automated CI/CD with GitHub Actions, and branch-based deployments: all the boilerplate I found myself copy-pasting across client projects, refined into a single starting point.

See the documentation for setup instructions.

Terraform Backend Setup

Terraform has no automatic bootstrapping; you configure the backend yourself.

Recommended setup for AWS:

  • Create S3 bucket with versioning enabled
  • Enable S3 server-side encryption (SSE-KMS)
  • Create IAM policies restricting access
  • Use use_lockfile = true for S3 native state locking (Terraform 1.10.0+)

Key difference: You must create these resources manually or with separate Terraform/CloudFormation before you can use them as your backend. It's a chicken-and-egg problem that teams typically solve with a small bootstrap script.

Comparison:

  • CDK: One command, automatic resource creation, required per account/region
  • Terraform: Manual resource setup, flexible configuration, per-project choice

If you want to skip the manual backend configuration and start with a production-ready setup, check out the AWS Terraform Starter Kit. It includes secure OIDC authentication, automated CI/CD with GitHub Actions, and security scanning built in.

See the documentation for setup instructions.

Deployment Workflow Comparison

What does your daily deployment experience look like?

CDK: Synth to Deploy to Rollback

The CDK deployment workflow:

  1. Synthesis: cdk synth converts your CDK code to CloudFormation templates (the "cloud assembly")
  2. Preview: cdk diff shows what will change compared to the deployed stack
  3. Deploy: cdk deploy synthesizes fresh templates, uploads assets to S3/ECR, creates/updates CloudFormation stack
  4. Rollback: CloudFormation automatically rolls back on deployment failure

Asset handling:

  • Lambda code is zipped and uploaded to the S3 staging bucket
  • Docker images are built locally and pushed to the ECR repository
  • CloudFormation references these assets during deployment
# Preview changes
cdk diff

# Deploy a specific stack
cdk deploy MyStack

# Deploy all stacks
cdk deploy "*"

Key benefit: Automatic rollback means failed deployments don't leave your infrastructure in a broken state.

Terraform: Init to Plan to Apply

The Terraform workflow is explicit about each step:

  1. Initialize: terraform init downloads providers and initializes the backend
  2. Plan: terraform plan shows what changes will be made (compares state with desired config)
  3. Apply: terraform apply provisions or updates infrastructure, updates state file
  4. Destroy: terraform destroy removes all managed infrastructure
# Initialize (first time or when providers change)
terraform init

# Preview changes
terraform plan

# Apply changes
terraform apply

# Tear down everything
terraform destroy

No automatic rollback: If a deployment fails partway through, you must manually fix or destroy/recreate. This is a significant operational difference from CDK.

Previewing Changes: cdk diff vs terraform plan

Both tools let you preview changes before deployment; this is critical for production safety.

cdk diff:

  • Compares current code with deployed CloudFormation stack
  • Shows resource additions, modifications, deletions
  • Displays property-level before/after values

terraform plan:

  • Compares desired state (code) with current state (state file)
  • Shows proposed changes with +/- indicators
  • More detailed output showing attribute-level changes

Key difference: CDK diff queries the live CloudFormation stack; Terraform plan uses the state file. If someone modified infrastructure outside Terraform, the state file might be stale.

Always run preview before deploying to production. Make it a mandatory CI/CD gate.

Testing Your Infrastructure Code

Quality assurance for infrastructure is just as important as application code.

CDK's Built-in Testing Framework

CDK includes native testing support through the aws-cdk-lib/assertions module.

Two testing approaches:

Fine-grained assertions (recommended for most cases):

  • Test specific resource properties
  • Clear test failures indicating exact problems
  • Stable across CDK version upgrades
import { Template } from 'aws-cdk-lib/assertions';

const template = Template.fromStack(stack);

// Verify Lambda function configuration
template.hasResourceProperties('AWS::Lambda::Function', {
  Runtime: 'nodejs22.x',
  Handler: 'index.handler',
  MemorySize: 256
});

Snapshot tests:

  • Compare entire synthesized template against a baseline
  • Useful for catching unintended changes during refactoring
  • Can break due to CDK upgrades or metadata changes

Compatible test frameworks: Jest (TypeScript/JavaScript), Pytest (Python), JUnit (Java), NUnit/xUnit (.NET)

Terraform's Third-Party Testing Ecosystem

Terraform has no native testing framework; you must use third-party tools.

Popular options:

  • Terratest (Go-based): Deploys real infrastructure, runs tests, tears down
  • terraform-compliance: Policy-as-code testing using BDD syntax
  • tflint: Linter for Terraform code
  • terraform validate: Built-in syntax validation (not comprehensive testing)

Trade-off: More flexible but requires additional tool learning and integration.

Testing complexity: Often requires actually deploying infrastructure to test (unlike CDK's template assertions). This makes tests slower and more expensive.

Which Approach Fits Your CI/CD Pipeline?

CDK advantage: Testing integrates with existing application test frameworks. Add infrastructure tests to the same pipeline as application code.

CDK workflow: Run npm test or pytest, fail the build on test failures, same as application code.

Terraform challenge: Requires separate testing tools and potentially separate CI stages.

Terraform benefit: Can test against actual deployed infrastructure (more realistic but slower and more expensive).

If your team already has a strong testing culture, CDK's built-in support reduces friction significantly.

Security and Compliance Comparison

Will your security team approve this tool, and how do you enforce compliance?

Security Scanning Tools

CDK security tools:

  • cdk-nag: Checks CDK constructs against AWS best practices and compliance standards (HIPAA, PCI-DSS, etc.)
  • CloudFormation Guard: Policy-as-code validation for synthesized templates
  • CDK Aspects: Custom validation logic that runs during synthesis

Terraform security tools:

  • tfsec: Static analysis for Terraform code
  • Checkov: Multi-cloud policy-as-code scanner (works with both CDK and Terraform)
  • Terraform Sentinel: HashiCorp's policy-as-code framework (requires Terraform Enterprise/Cloud)
  • OPA (Open Policy Agent): Flexible policy engine

Terraform's security tooling ecosystem is more mature with more options.

Policy-as-Code Capabilities

CDK Aspects: TypeScript/Python code that validates constructs during synthesis.

Example use case: enforce S3 bucket encryption across all buckets. The Aspect runs before CloudFormation deployment and fails the build on violations.

CDK Mixins (new in March 2026): complement Aspects by allowing compliance settings to be applied directly at the construct level via .with(), which is less invasive than an Aspect traversal for specific resource configurations.

Terraform Sentinel/OPA: Policy languages for validating Terraform plans. Can prevent non-compliant infrastructure from being deployed.

Terraform's policy-as-code ecosystem is more established for enterprise compliance. If you need sophisticated policy enforcement with governance workflows, Terraform has more mature options.

Secret Management Approaches

Best practice for both tools: Use AWS Secrets Manager or Parameter Store, never hardcode secrets.

CDK approach:

// Reference existing secret at runtime
const secret = secretsmanager.Secret.fromSecretNameV2(
  this, 'MySecret', 'my-secret-name'
);

Secrets are retrieved at runtime, not synthesis time. Never hardcode in CDK code.

Terraform approach:

data "aws_secretsmanager_secret_version" "my_secret" {
  secret_id = "my-secret-name"
}

Critical: Secrets accessed via Terraform data sources can appear in the state file. You must protect the state file with encryption and access controls. AWS recommends rotating secrets immediately after Terraform ingestion.

Security concern: Terraform's state file exposure is higher risk than CDK for sensitive data.

Drift Detection and Remediation

CDK drift detection:

  • cdk drift: Calls CloudFormation's drift detection API to compare actual resource state against CloudFormation's expected configuration
  • cdk diff: Compares your local CDK code against the deployed CloudFormation stack template (different from cdk drift)
  • Available via console or API
  • Shows which properties drifted and their actual vs expected values
  • Cannot auto-remediate; must redeploy stack

Terraform drift detection:

  • terraform plan compares state file with actual infrastructure
  • Shows what would change to bring infrastructure back to desired state
  • terraform apply remediates drift
  • More integrated into normal workflow

Advantage Terraform: Drift detection is core to the plan/apply workflow, making it natural to catch and fix.

Honest Pros and Cons

Let's be direct about the real trade-offs you're accepting.

AWS CDK Advantages and Limitations

Advantages:

  1. Programming language support: TypeScript, Python, Java, C#, Go, JavaScript with full IDE features
  2. High-level abstractions: L2/L3 constructs reduce boilerplate dramatically (see the ECS Fargate example above)
  3. Automatic state management: CloudFormation handles state with zero configuration
  4. Built-in testing: Native unit and snapshot testing support
  5. AWS integration: First-class support, gets new features when CloudFormation does
  6. Best practices built-in: L2/L3 constructs encode AWS recommended configurations
  7. Rapid development: Create complex architectures with minimal code
  8. CloudFormation benefits: Automatic rollback, change sets, drift detection
  9. Reusable constructs: Share components via npm, PyPI, Maven, NuGet
  10. Type safety: Compile-time checking prevents configuration errors
  11. CDK Toolkit Library: Programmatic CI/CD integration without CLI subprocess calls
  12. CDK Mixins: Cross-cutting compliance features applied via .with() on any construct
  13. CDK Refactor: Safe code restructuring without triggering resource replacement

Limitations:

  1. Bootstrapping required: One-time setup per AWS account/region with multiple resources
  2. AWS-only: Cannot deploy to other cloud providers or SaaS platforms
  3. CloudFormation dependency: Limited by CloudFormation capabilities, quotas (500 resources/stack), and behaviors
  4. Learning curve for ops: Requires programming knowledge, steeper for traditional ops teams
  5. Provisioning speed: Deploys via CloudFormation, which is slower than Terraform's direct API approach
  6. Construct availability: Not all AWS features have L2/L3 constructs yet (fall back to L1/CFN)

Terraform Advantages and Limitations

Advantages:

  1. Multi-cloud support: 3,000+ providers for AWS, Azure, GCP, and diverse services
  2. Platform agnostic: No cloud vendor lock-in, consistent tooling across providers
  3. Mature ecosystem: Extensive modules, tools, and community (9+ years)
  4. No bootstrapping: No upfront environment setup required
  5. Declarative syntax: HCL easier for teams with diverse skillsets to learn
  6. Drift detection: Built-in drift detection core to the workflow
  7. Code reusability: Terraform Registry with thousands of vetted modules
  8. Broad adoption: Industry standard with extensive documentation and resources
  9. Agentless: No software installation on managed infrastructure
  10. Faster provisioning: Direct AWS API calls without CloudFormation intermediary

Limitations:

  1. Manual state management: Requires S3 bucket, encryption, IAM, and locking configuration
  2. State file security risk: Contains sensitive data in plain text requiring careful protection
  3. No automatic rollback: Manual intervention required for failed deployments
  4. Feature lag: New AWS features may take time to appear in AWS provider
  5. Limited testing: No native testing framework, requires third-party tools
  6. Licensing change: Business Source License instead of open source
  7. Business model risk: HashiCorp must monetize the tool itself, creating potential for future restrictive changes
  8. State locking migration: DynamoDB-based locking is deprecated; must migrate to S3 native locking (use_lockfile = true)
  9. HCL learning curve: New language to learn for developers used to general-purpose languages

Real-World Experiences

What problems will you actually face?

Common Gotchas and Pain Points

CDK Gotchas:

  1. CloudFormation stack updates can fail mid-deployment: manual intervention required via console
  2. Construct configuration sometimes synthesizes to unexpected CloudFormation: use cdk synth to verify
  3. Cross-stack references require stacks in same environment: common error for beginners
  4. Bootstrap version mismatches cause cryptic errors; keep bootstrap updated with cdk bootstrap
  5. L1 constructs require CloudFormation property knowledge: no abstraction benefit
  6. CDK Refactor constraint: if you need to add/delete/modify resources AND refactor simultaneously, you must split these into separate deployments

For help with cross-stack errors, see my guide on cross-stack references in CDK.

Terraform Gotchas:

  1. State file corruption from concurrent modifications: locking is critical
  2. Sensitive data in state file: must secure state bucket rigorously
  3. Provider version changes can break existing code; pin versions
  4. terraform plan doesn't catch all errors: some only appear during apply
  5. New AWS features lag: may wait weeks or months for provider updates

When the Pros Become Cons

CDK Examples:

  • "Programming flexibility" becomes "too much magic" when debugging synthesized CloudFormation
  • "High-level abstractions" become "I can't customize this" when L2/L3 constructs don't match your exact needs
  • "Automatic state management" becomes "I can't debug state issues" when CloudFormation behaves unexpectedly
  • "AWS-native integration" becomes "I'm locked into AWS" when business strategy shifts to multi-cloud

Terraform Examples:

  • "Declarative simplicity" becomes "I can't express this logic" when you need complex conditional resource creation
  • "Multi-cloud support" becomes "lowest common denominator" when AWS-specific features aren't available
  • "Mature ecosystem" becomes "outdated modules" when community modules don't keep up with best practices
  • "Manual state control" becomes "state file nightmare" when team grows and state conflicts increase

Decision Framework

Based on your specific situation, which tool should you choose?

The Role-Based Decision

Here's the organizing principle that most comparison posts bury in a footnote: who writes and maintains the infrastructure code daily?

The answer matters more than any feature comparison.

Software developers and application engineers: CDK is the natural fit. You use the language you already know, integrate infrastructure tests into your existing test suite, and leverage L2/L3 constructs to move fast on AWS. The programming model is familiar. The tooling is familiar.

DevOps/SRE and platform engineers: Terraform typically wins. Declarative configuration is more readable for infrastructure-focused roles. State management gives operational control that platform engineers are used to. The Terraform ecosystem maps to platform engineering workflows.

Mixed teams or teams with no clear IaC owner: Terraform's HCL is the neutral middle ground. HCL doesn't require programming expertise. CDK requires someone who can program, and that person has to be available when things break.

Engineering managers choosing for a team: ask "who will write and maintain this code daily?" before looking at any feature matrix. Training 10 ops engineers to program is a different investment than training 10 developers to learn HCL.

Choose CDK If

Definitive scenarios:

  1. Your infrastructure is 100% AWS with no multi-cloud requirements (now or in foreseeable future)
  2. Your team are developers who prefer TypeScript, Python, Java, C#, or Go over learning HCL
  3. You want to leverage L2/L3 constructs to reduce boilerplate and encode AWS best practices
  4. Testing is critical and you want native framework integration
  5. You're building AWS-native applications (serverless, containers) where CDK constructs shine
  6. You have governance requirements and want to distribute custom constructs with enforced standards
  7. You want CDK's fast-moving 2026 feature set (Mixins, Refactor, Toolkit Library)

Choose Terraform If

Definitive scenarios:

  1. You need multi-cloud or hybrid-cloud (AWS + Azure/GCP/on-premises)
  2. You manage non-AWS resources (GitHub, Datadog, Kubernetes, etc.) alongside AWS
  3. You want to avoid vendor lock-in as a strategic principle
  4. Your team are ops-focused and prefer declarative configuration over programming
  5. You have existing Terraform expertise and modules already built
  6. Drift detection is critical to your operational workflow
  7. Provisioning speed is a primary concern and you want direct API calls

Use Case Decision Matrix

Use CaseCDKTerraformPulumiWhy
Serverless applicationsBestGoodGoodCDK L3 constructs for Lambda, API Gateway reduce boilerplate significantly
Container orchestration (ECS/EKS)BestGoodGoodCDK patterns like ApplicationLoadBalancedFargateService simplify complex setups
Multi-account AWS OrganizationsGoodGoodGoodBoth work well; CDK if developers manage, Terraform if centralized ops team
Network infrastructure (VPC, Transit Gateway)GoodBestGoodTerraform's mature modules and declarative nature fit network configs well
Multi-cloud deploymentsNoBestGoodCDK doesn't support multi-cloud; Terraform for mature ecosystem, Pulumi for code
Data engineering pipelinesGoodGoodGoodCDK if Python-heavy team, Terraform if diverse data sources across clouds
Kubernetes infrastructureNoBestGoodTerraform has Kubernetes provider; CDK doesn't (use CDK8s for K8s resources)
Third-party SaaS (GitHub, Datadog)NoBestGoodOnly Terraform/Pulumi support non-AWS providers
AWS-only with compliance requirementsBestGoodNoCDK Aspects + Mixins provide compile-time policy enforcement

For multi-account architectures, see my guide on AWS multi-account best practices.

Hybrid CDK + Terraform: When to Use Both

This section is for teams that have decided they need both tools. If you're still deciding, pick one; hybrid approaches add complexity that rarely pays off until you have a good reason.

The question for hybrid setups isn't whether to use both; it's where to draw the boundary.

The Layered Architecture Pattern

The pattern that works well in practice is clean vertical separation by resource lifespan and ownership:

Terraform manages the foundation layer: resources that change infrequently, serve multiple teams, and need to exist before any CDK stack can deploy:

  • VPC and networking (subnets, route tables, NAT gateways)
  • Shared IAM roles and permission boundaries
  • Transit Gateway for multi-VPC routing
  • Route 53 hosted zones
  • Shared security groups

CDK manages the application layer: resources owned by a single application or service, deployed frequently by the team that owns the service:

  • Lambda functions and API Gateway
  • ECS services and task definitions
  • Application-specific S3 buckets and queues
  • DynamoDB tables for specific services

The boundary criterion: if a resource is shared across multiple CDK stacks or multiple teams, it belongs in Terraform. If a resource is owned by a single application or service, it belongs in CDK.

Sharing State Between CDK and Terraform

The standard integration pattern uses SSM Parameter Store as the bridge:

Terraform side: export outputs to SSM:

resource "aws_ssm_parameter" "vpc_id" {
  name  = "/platform/vpc/id"
  type  = "String"
  value = aws_vpc.main.id
}

resource "aws_ssm_parameter" "private_subnet_ids" {
  name  = "/platform/vpc/private-subnet-ids"
  type  = "StringList"
  value = join(",", aws_subnet.private[*].id)
}

CDK side: read at synthesis time:

import * as ssm from 'aws-cdk-lib/aws-ssm';

const vpcId = ssm.StringParameter.valueFromLookup(this, '/platform/vpc/id');
const subnetIds = ssm.StringParameter.valueFromLookup(this, '/platform/vpc/private-subnet-ids');

The CDK stack reads SSM parameters during cdk synth; if the Terraform foundation hasn't run yet, synthesis fails. That's intentional: it enforces the deployment order and makes the dependency explicit.

When Hybrid Adds More Complexity Than Value

Solo developers and small startups: don't do this. The overhead of two tool chains, two CI/CD patterns, and two sets of state management isn't worth it.

When hybrid makes sense:

  • Large organizations with a platform team (Terraform) and application teams (CDK)
  • Gradual migration from one tool to another
  • Existing Terraform foundation that isn't worth rewriting, combined with CDK for new applications
  • Multi-cloud foundation (Terraform) with AWS-native application layer (CDK)

Operational discipline required: two tool chains to install and maintain in CI/CD, two different PR review patterns, and a real risk of resource boundary overlap if teams aren't disciplined about the rules.

If the hybrid approach sounds like it fits, you'll want a migration plan. Here's how to move infrastructure between tools when your requirements change.

Migration Strategies

What if you choose wrong, or your requirements change?

Migrating from Terraform to CDK

The tool for this is cdk migrate, currently in preview (first documented February 2024). The important thing to know upfront: cdk migrate generates L1 constructs only. Getting to L2/L3 is manual work after migration.

Three migration sources:

# Migrate from a deployed CloudFormation stack
cdk migrate --from-stack --stack-name "myCloudFormationStack"

# Migrate from a local CloudFormation template
cdk migrate --from-path "./template.json" --stack-name "myCloudFormationStack"

# Migrate from deployed resources (uses IaC generator scan)
cdk migrate --from-scan --stack-name "myStack"

Key constraints you need to know:

  • All migrated resources become L1 constructs; upgrading to L2/L3 is manual post-migration work
  • Single stack per migration; nested templates are not supported
  • Lambda code and project assets are not migrated automatically
  • The Terraform-to-CDK path is indirect: cdk migrate works from CloudFormation stacks or raw AWS resources, not Terraform state directly

The Terraform-specific migration path:

  1. Identify Terraform-managed resources you want to migrate
  2. Use cdk import to absorb individual resources into a CDK app (use cdk import for a few resources into an existing app, cdk migrate for a whole stack into a new CDK app)
  3. Verify logical IDs match between the CDK app and deployed resources
  4. Run cdk diff to confirm no changes are detected
  5. Refactor L1 constructs to L2/L3 where appropriate
  6. Remove Terraform configuration after CDK fully manages the resources

Stateful resources (databases, S3 buckets): verify the resource supports CloudFormation import before migrating. Deploy to the same account and region as the existing resource.

Migrating from CDK to Terraform

No automated migration tool: this must be done manually.

Migration strategy:

  1. Use terraform import to bring existing CloudFormation-managed resources into Terraform state
  2. Write Terraform configuration matching existing resources
  3. Run terraform plan to verify no changes detected
  4. Test in non-production environment first
  5. Delete CDK stacks after Terraform manages resources

Challenges:

  • Must write Terraform config from scratch
  • CloudFormation-specific resource configurations may not translate directly
  • Import process is resource-by-resource (tedious for large infrastructures)

Alternative: Recreate infrastructure with Terraform (blue/green migration).

This asymmetry matters: migrating Terraform to CDK has a tool (cdk migrate); migrating CDK to Terraform is entirely manual. That's one concrete reason to think carefully before starting with Terraform if you expect to want CDK later.

Risk Mitigation for Lock-In Concerns

Both tools create lock-in: switching is non-trivial in either direction.

Mitigation strategies:

  • Keep infrastructure simple and modular (easier to rewrite)
  • Document architectural decisions
  • Invest in testing to enable confident refactoring
  • Consider gradual migration over big-bang approach
  • Maintain infrastructure diagrams independent of IaC tool

CDK lock-in is twofold: tool lock-in (CDK itself) + vendor lock-in (AWS via CloudFormation).

Terraform lock-in: tool lock-in but platform flexibility via providers.

If you're genuinely worried about lock-in, Terraform's multi-cloud support provides more architectural flexibility by design.

How CDK and Terraform Fit in the IaC Landscape

What about CloudFormation, SAM, Pulumi, and other IaC tools?

Quick Comparison with CloudFormation, SAM, and Pulumi

AWS Prescriptive Guidance provides a useful framework for choosing between IaC tools. Here's the decision matrix:

ScenarioRecommended Tool
Serverless AWS with minimal dependenciesAWS SAM
Infrastructure entirely on AWS, state management importantAWS CloudFormation or AWS CDK
Multi-cloud or hybrid-cloud, multi-providerTerraform
Top-down construct reuse, governance, compliance distributionAWS CDK
Multi-cloud with high risk tolerancePulumi
Multi-cloud with existing Terraform investmentTerraform

The full tool comparison:

ToolBest ForLanguageScopeRelationship
CloudFormationAWS-native declarative IaCYAML/JSONAWS onlyCDK synthesizes to CloudFormation
AWS SAMServerless applicationsYAML (simplified)Serverless-focusedSubset of CloudFormation
AWS CDKAWS infrastructure with programmingTypeScript, Python, Java, C#, Go, JSAWS onlyLayer on top of CloudFormation
TerraformMulti-cloud infrastructureHCLMulti-cloudAlternative deployment engine
PulumiMulti-cloud with programmingTypeScript, Python, Go, C#, JavaMulti-cloudCDK-like experience for non-AWS clouds

Key differentiators:

  • CloudFormation = AWS-native, declarative, YAML/JSON (CDK's underlying deployment engine)
  • CDK = CloudFormation with programming languages and high-level abstractions
  • Terraform = multi-cloud, HCL, mature ecosystem, direct API calls
  • Pulumi = multi-cloud with programming languages (CDKTF's spiritual successor, actively maintained)

When to consider alternatives:

  • CloudFormation directly: If you prefer YAML/JSON and don't need programming abstractions
  • AWS SAM: If you're building purely serverless applications and want simplified syntax
  • Pulumi: If you want CDK-like programming experience with multi-cloud support

For a deeper look at AWS-native IaC options, see my comparison of Control Tower alternatives from console to code.

Conclusion: Making Your Decision with Confidence

Let me give you the specific, non-vague version of "here's what to do."

Key Takeaways

  1. AWS CDK wins for AWS-native teams: programming languages, automatic CloudFormation-managed state, rich L2/L3 abstractions, and a fast-moving 2025-2026 feature set (Toolkit Library, Refactor, Mixins)

  2. Terraform wins for multi-cloud and platform teams: declarative HCL, mature ecosystem, direct API calls (faster provisioning), but manual state management and BSL licensing

  3. CDKTF is dead: don't plan around it. If you were on CDKTF for programming languages + AWS, migrate to CDK. If you needed multi-cloud, evaluate Pulumi.

  4. CDK deploys via CloudFormation: roughly 2-3x slower than Terraform for the same resources; cdk watch narrows the gap in development workflows

  5. Hybrid is valid with clear boundaries: Terraform for foundation (VPC, shared IAM, networking), CDK for application layer, SSM Parameter Store as the state bridge

Your Next Action Step

The best way to validate this decision for your specific team is to build the same infrastructure (VPC + Lambda + S3) in both tools in a scratch AWS account, time it, and see which development experience feels natural. Commit to one tool within two weeks; indecision is more costly than picking either.

If you're choosing CDK:

  1. Install AWS CDK: npm install -g aws-cdk
  2. Read my comprehensive beginner's guide to AWS CDK
  3. Review CDK best practices for production-ready patterns
  4. Learn about bootstrapping: AWS CDK Bootstrap: The Why and the How
  5. Explore project structure: Optimize your AWS CDK Project Structure for Growth

If you're choosing Terraform:

  1. Install Terraform: follow HashiCorp's installation guide
  2. Read AWS Prescriptive Guidance for Terraform
  3. Set up S3 backend with use_lockfile = true (not DynamoDB; that's deprecated)
  4. Explore Terraform Registry for AWS modules
  5. Review Terraform AWS Provider Best Practices

If you're still uncertain: build the same simple infrastructure in both tools, evaluate which feels more natural for your team, and make a decision. Both are good tools; the right one depends on your context.

What's your biggest hesitation in making this choice? Drop a comment below and let's discuss.

Need Help With Your AWS CDK Implementation?

I review your CDK infrastructure code for best practices, security compliance, and maintainability. Get actionable recommendations to improve your Infrastructure as Code quality and reduce technical debt.

Is AWS CDK better than Terraform?
Neither is universally better. CDK is better for AWS-only teams who prefer programming languages and want high-level abstractions. Terraform is better for multi-cloud setups, platform engineering teams, and organizations that prefer declarative configuration. The right choice depends on your cloud scope, team composition, and existing expertise.
What is the difference between AWS CDK and Terraform?
CDK uses general-purpose programming languages (TypeScript, Python, Java, C#, Go) to define AWS infrastructure, synthesizes to CloudFormation, and has automatic state management. Terraform uses HCL (HashiCorp Configuration Language), supports 3,000+ cloud providers, and requires manual state configuration using S3 or another backend. CDK is AWS-only; Terraform is multi-cloud.
What happened to CDKTF?
HashiCorp deprecated CDKTF in December 2025. Existing CDKTF projects continue to work, but no new features or updates will be delivered. If you're AWS-only, migrate to native AWS CDK. If you need multi-cloud with programming languages, evaluate Pulumi as the closest alternative.
Is Terraform still free to use in 2026?
Terraform is free for infrastructure management purposes. In August 2023, HashiCorp changed the license from Mozilla Public License to Business Source License (BSL). The BSL restricts use in products that compete directly with HashiCorp's commercial offerings. For most teams using Terraform to manage infrastructure, there is no practical change.
Can you use AWS CDK and Terraform together?
Yes. The recommended pattern is a layered architecture: Terraform manages foundational infrastructure (VPC, shared IAM roles, networking) while CDK manages application-layer infrastructure (Lambda, ECS, API Gateway). SSM Parameter Store serves as the bridge for sharing outputs between the two tools. This approach adds operational complexity and only makes sense at a certain organizational scale.
Why is CDK slower than Terraform for provisioning?
CDK deploys through CloudFormation as an intermediary, which adds service overhead. Terraform calls AWS APIs directly without an intermediate template service. For a typical stack of 10-20 resources, CDK/CloudFormation takes roughly 3-5 minutes compared to 1-3 minutes for Terraform. CDK's 'cdk watch' command partially closes this gap in development by using hot-swap deployments for Lambda and ECS updates.
What is the latest version of AWS CDK in 2026?
As of March 2026, the CDK Construct Library is at version 2.243.0 and the CDK CLI is at 2.1000+. Since February 2025, the CLI and Construct Library follow independent release cadences, which is why the version numbers look mismatched. The compatibility rule is now based on release date rather than version number.
Should I use OpenTofu instead of Terraform?
OpenTofu is a viable option if open-source licensing is a hard requirement for your organization. It maintains the Mozilla Public License and aims for Terraform compatibility. Trade-offs include a smaller ecosystem and less mature tooling. AWS doesn't explicitly endorse OpenTofu, but updated its Service Catalog to use an 'External' product type that accommodates OpenTofu alongside other Terraform-compatible tools.

Share this article on ↓

Subscribe to our Newsletter

Join ---- other subscribers!