AWS CDK v2 vs Terraform: Which IaC Tool to Choose?
CI/CD & GitOps

AWS CDK v2 vs Terraform: Which IaC Tool to Choose?

February 21, 202610 min readAWS CDKTerraformIaC

AWS CDK lets you write infrastructure in TypeScript or Python, while Terraform remains the multi-cloud standard. Which tool should you choose in 2026?

Two Different Philosophies

AWS CDK (Cloud Development Kit) and Terraform represent two fundamentally different approaches to Infrastructure as Code. Terraform takes a declarative approach: you describe the desired final state in HCL, and Terraform calculates the plan to get there. AWS CDK takes an imperative approach: you write TypeScript or Python code that generates CloudFormation templates, with the full power of a real programming language.

In 2026, both tools have reached remarkable maturity and often coexist in the same organisations. Choosing between them depends on your context: cloud strategy, team skills, and infrastructure complexity.

Key CDK Concepts

CDK is organised around three hierarchical concepts:

  • App: the entry point of your CDK programme, contains one or more stacks
  • Stack: CloudFormation deployment unit, corresponds to a CF template
  • Construct: reusable building block representing one or more AWS resources

Constructs exist at three levels:

  • L1 (CfnXxx): direct wrapper of CloudFormation resources, 1:1 with CF
  • L2: high-level abstractions with sensible defaults (opinionated)
  • L3 (Patterns): ready-to-use complete architectures (e.g. ApplicationLoadBalancedFargateService)

Side-by-Side Comparison: ECS Fargate Service

Here is the same ECS Fargate service defined in CDK TypeScript and Terraform HCL:

CDK TypeScript

import { Stack, StackProps } from 'aws-cdk-lib';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ecs_patterns from 'aws-cdk-lib/aws-ecs-patterns';
import { Construct } from 'constructs';

export class PaymentServiceStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const cluster = new ecs.Cluster(this, 'Cluster', {
      clusterName: 'payment-cluster',
      containerInsights: true,
    });

    const service = new ecs_patterns.ApplicationLoadBalancedFargateService(
      this, 'PaymentService', {
        cluster,
        cpu: 512,
        memoryLimitMiB: 1024,
        desiredCount: 2,
        taskImageOptions: {
          image: ecs.ContainerImage.fromRegistry('nginx:latest'),
          containerPort: 8080,
          environment: { NODE_ENV: 'production' },
        },
        publicLoadBalancer: true,
      }
    );

    // Auto-scaling in 3 lines
    const scaling = service.service.autoScaleTaskCount({ maxCapacity: 10 });
    scaling.scaleOnCpuUtilization('CpuScaling', { targetUtilizationPercent: 70 });
  }
}

Terraform HCL

resource "aws_ecs_cluster" "main" {
  name = "payment-cluster"
  setting {
    name  = "containerInsights"
    value = "enabled"
  }
}

resource "aws_ecs_task_definition" "payment" {
  family                   = "payment-service"
  cpu                      = "512"
  memory                   = "1024"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  execution_role_arn       = aws_iam_role.ecs_exec.arn
  container_definitions = jsonencode([{
    name      = "app"
    image     = "nginx:latest"
    portMappings = [{ containerPort = 8080 }]
    environment = [{ name = "NODE_ENV", value = "production" }]
  }])
}

resource "aws_ecs_service" "payment" {
  name            = "payment-service"
  cluster         = aws_ecs_cluster.main.id
  task_definition = aws_ecs_task_definition.payment.arn
  desired_count   = 2
  launch_type     = "FARGATE"
  network_configuration {
    subnets         = var.private_subnets
    security_groups = [aws_security_group.app.id]
  }
  load_balancer {
    target_group_arn = aws_lb_target_group.payment.arn
    container_name   = "app"
    container_port   = 8080
  }
}
# ... 50+ additional lines for ALB, SG, IAM, Auto Scaling

CDK drastically reduces code volume for AWS thanks to L3 constructs, but Terraform gives granular control over every parameter.

CDK Aspects: Automated Compliance

CDK Aspects allow you to apply compliance rules across all resources in a stack. This is a feature with no direct equivalent in Terraform:

class SecurityAspect implements cdk.IAspect {
  visit(node: IConstruct): void {
    if (node instanceof s3.Bucket) {
      if (!node.encryptionKey) {
        cdk.Annotations.of(node).addError(
          'All S3 buckets must use customer-managed KMS keys'
        );
      }
    }
    if (node instanceof rds.DatabaseInstance) {
      if (!node.storageEncrypted) {
        cdk.Annotations.of(node).addError('RDS must have encryption enabled');
      }
    }
  }
}

// Apply the aspect to the entire app
cdk.Aspects.of(app).add(new SecurityAspect());

CDK Testing with Jest

CDK allows you to test infrastructure like application code, with assertions on generated templates:

import { Template } from 'aws-cdk-lib/assertions';

test('ECS service has correct CPU allocation', () => {
  const app = new cdk.App();
  const stack = new PaymentServiceStack(app, 'TestStack');
  const template = Template.fromStack(stack);

  template.hasResourceProperties('AWS::ECS::TaskDefinition', {
    Cpu: '512',
    Memory: '1024',
    RequiresCompatibilities: ['FARGATE'],
  });
});

test('S3 buckets have versioning enabled', () => {
  template.allResourcesProperties('AWS::S3::Bucket', {
    VersioningConfiguration: { Status: 'Enabled' },
  });
});

Terraform Advantages

  • Native multi-cloud: same workflow for AWS, GCP, Azure, OVH, Kubernetes
  • Massive community: 3000+ providers, Terraform Registry modules
  • OpenTofu: open-source fork without commercial dependency
  • Stability: HCL is less susceptible to breaking changes than an SDK
  • Dev/ops separation: ops can contribute without mastering TypeScript

CDK Advantages

  • Type safety: the TypeScript compiler catches errors before deployment
  • IDE autocomplete: discover APIs without consulting documentation
  • Reusability: constructs are npm packages shareable between teams
  • Testing: native unit tests for infrastructure
  • Powerful abstractions: L3 patterns radically reduce code volume

Decision Matrix

ContextRecommendation
100 % AWS team with strong TypeScript/Python cultureCDK
Multi-cloud (AWS + GCP, or AWS + OVH)Terraform / OpenTofu
Mixed dev + ops team without programming cultureTerraform
AWS-only startup scaling fastCDK (maximum productivity)
Complex infrastructure shared across multiple teamsTerraform (modules) or CDK (npm constructs)
Compliance and security policies to apply everywhereCDK Aspects or Sentinel (Terraform)

Conclusion

In 2026, CDK and Terraform are no longer really in competition — they address different needs. If your organisation is AWS-centric and your developers already write TypeScript, CDK offers unmatched productivity. If you manage multiple clouds, want simple governance, or your ops teams lack a programming culture, Terraform (or OpenTofu) remains the wisest choice. Many organisations use both: CDK for AWS application services, Terraform for the network foundation and multi-cloud layer.

← Back to blog