Skip to main content
AWSDevOps & IaCintermediate

CloudFormation vs CDK

Compare AWS CloudFormation and CDK for infrastructure as code with real-world examples.

CloudToolStack Team24 min readPublished Feb 22, 2026

Prerequisites

  • Basic understanding of infrastructure as code concepts
  • AWS account with CloudFormation permissions
  • Familiarity with at least one programming language (for CDK)

Infrastructure as Code on AWS

AWS offers two first-party Infrastructure as Code (IaC) tools: CloudFormation and the Cloud Development Kit (CDK). CloudFormation is a declarative template-based system using JSON or YAML, while CDK lets you define infrastructure using general-purpose programming languages like TypeScript, Python, Java, Go, and C#. Understanding the strengths and trade-offs of each helps you choose the right tool for your team and project.

Importantly, CDK is not a replacement for CloudFormation. CDK synthesizes CloudFormation templates under the hood. Every CDK deployment ultimately creates or updates a CloudFormation stack. CDK is a higher-level abstraction that generates CloudFormation, so understanding CloudFormation fundamentals is valuable regardless of which tool you use. When something goes wrong with a CDK deployment, you will be troubleshooting CloudFormation stack events.

This guide provides a comprehensive comparison of both tools, including real-world code examples, testing strategies, migration paths, and a decision framework to help you choose the right approach for your organization.

Third-Party Alternatives

While this guide focuses on AWS first-party tools, Terraform (by HashiCorp) and Pulumi are popular multi-cloud alternatives. Terraform uses HCL (a declarative DSL) and manages state externally. Pulumi, like CDK, uses general-purpose programming languages but supports multiple cloud providers. If multi-cloud IaC is a requirement, these tools deserve evaluation alongside CDK and CloudFormation.

CloudFormation: The Foundation

CloudFormation has been available since 2011 and is the most battle-tested IaC tool on AWS. It provides declarative infrastructure definitions where you describe the desired end state and AWS handles the provisioning order, dependency resolution, rollbacks, and resource lifecycle. Every AWS service launches with CloudFormation support on day one, making it the most comprehensive IaC tool for AWS resources.

CloudFormation Architecture

When you deploy a CloudFormation template, the service creates a stack, a collection of AWS resources managed as a single unit. CloudFormation determines the dependency order, creates resources in parallel where possible, and tracks the state of every resource. If any resource fails to create, CloudFormation automatically rolls back the entire stack to its previous state, ensuring you never end up with a partially deployed environment.

cloudformation-example.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: Serverless API with Lambda, API Gateway, and DynamoDB

Parameters:
  Environment:
    Type: String
    AllowedValues: [dev, staging, prod]
    Default: dev
  LogRetentionDays:
    Type: Number
    Default: 30
    AllowedValues: [7, 14, 30, 60, 90, 365]

Conditions:
  IsProd: !Equals [!Ref Environment, prod]

Resources:
  ApiFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: !Sub "${Environment}-api-handler"
      Runtime: python3.12
      Handler: index.handler
      MemorySize: !If [IsProd, 512, 256]
      Timeout: 30
      Code:
        S3Bucket: !Ref DeploymentBucket
        S3Key: !Ref CodePackageKey
      Environment:
        Variables:
          TABLE_NAME: !Ref DataTable
          ENVIRONMENT: !Ref Environment
      Tracing:
        Mode: Active
      Tags:
        - Key: Environment
          Value: !Ref Environment

  DataTable:
    Type: AWS::DynamoDB::Table
    DeletionPolicy: !If [IsProd, Retain, Delete]
    Properties:
      TableName: !Sub "${Environment}-data"
      BillingMode: PAY_PER_REQUEST
      PointInTimeRecoverySpecification:
        PointInTimeRecoveryEnabled: !If [IsProd, true, false]
      AttributeDefinitions:
        - AttributeName: pk
          AttributeType: S
        - AttributeName: sk
          AttributeType: S
      KeySchema:
        - AttributeName: pk
          KeyType: HASH
        - AttributeName: sk
          KeyType: RANGE
      SSESpecification:
        SSEEnabled: true

  FunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "/aws/lambda/${Environment}-api-handler"
      RetentionInDays: !Ref LogRetentionDays

Outputs:
  FunctionArn:
    Value: !GetAtt ApiFunction.Arn
    Export:
      Name: !Sub "${Environment}-api-function-arn"
  TableName:
    Value: !Ref DataTable

CloudFormation Strengths

  • Mature and stable: Over a decade of production use across millions of stacks worldwide. Edge cases are well-documented.
  • No additional tooling: Templates are plain YAML/JSON files. No build step, no compiler, no package manager required.
  • Direct AWS support: CloudFormation is a first-class AWS service with premium support coverage and same-day resource type support for new services.
  • Automatic rollback: Failed deployments automatically roll back to the previous known-good state, ensuring consistency.
  • StackSets: Deploy identical stacks across multiple accounts and regions from a single template. Essential for organizational governance.
  • Drift detection: Identifies resources that have been modified outside CloudFormation, helping maintain configuration consistency.
  • Change sets: Preview exactly what resources will be created, modified, or deleted before applying changes.

CloudFormation Weaknesses

  • Verbose templates: YAML templates grow unwieldy quickly. A moderately complex stack can reach 1,000+ lines.
  • Limited logic: No real loops. Fn::If is the only conditional, and it cannot be nested easily. Complex logic requires creative workarounds.
  • Poor code reuse: Nested stacks are cumbersome. Modules exist but have limitations. Copy-pasting between templates is common.
  • Slow feedback: You must deploy to validate many things. Template validation catches syntax errors but not logical ones.
  • 200-resource limit per stack: Large applications must be split across multiple stacks with cross-stack references.

CDK: Infrastructure with Programming Languages

CDK brings the full power of programming languages to infrastructure definition. You get loops, conditionals, type safety, IDE autocomplete, unit testing, and the ability to create reusable abstractions. CDK introduces the concept of “constructs,” cloud components at three levels of abstraction.

CDK Construct Levels

LevelNameDescriptionExample
L1Cfn ResourcesDirect 1:1 mapping to CloudFormation resources. Auto-generated from the CloudFormation spec.CfnBucket, CfnFunction
L2Curated ConstructsOpinionated defaults with helper methods. Written by the CDK team with best practices built in.Bucket, Function with grantRead()
L3PatternsHigh-level patterns that wire multiple resources together into common architectures.LambdaRestApi, ApplicationLoadBalancedFargateService
cdk-example.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as logs from 'aws-cdk-lib/aws-logs';

interface ApiStackProps extends cdk.StackProps {
  environment: 'dev' | 'staging' | 'prod';
}

export class ApiStack extends cdk.Stack {
  public readonly apiUrl: string;

  constructor(scope: Construct, id: string, props: ApiStackProps) {
    super(scope, id, props);

    const isProd = props.environment === 'prod';

    // DynamoDB table with environment-specific settings
    const table = new dynamodb.Table(this, 'DataTable', {
      partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING },
      sortKey: { name: 'sk', type: dynamodb.AttributeType.STRING },
      billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
      pointInTimeRecovery: isProd,
      encryption: dynamodb.TableEncryption.AWS_MANAGED,
      removalPolicy: isProd
        ? cdk.RemovalPolicy.RETAIN
        : cdk.RemovalPolicy.DESTROY,
    });

    // Lambda function with best practices baked in
    const fn = new lambda.Function(this, 'ApiHandler', {
      runtime: lambda.Runtime.PYTHON_3_12,
      handler: 'index.handler',
      code: lambda.Code.fromAsset('lambda/'),
      memorySize: isProd ? 512 : 256,
      timeout: cdk.Duration.seconds(30),
      tracing: lambda.Tracing.ACTIVE,
      environment: {
        TABLE_NAME: table.tableName,
        ENVIRONMENT: props.environment,
        POWERTOOLS_SERVICE_NAME: 'api',
      },
      logRetention: isProd
        ? logs.RetentionDays.ONE_YEAR
        : logs.RetentionDays.ONE_WEEK,
    });

    // One line grants least-privilege read/write access
    // CDK automatically generates the precise IAM policy
    table.grantReadWriteData(fn);

    // API Gateway wired to Lambda
    const api = new apigateway.LambdaRestApi(this, 'Api', {
      handler: fn,
      proxy: true,
      deployOptions: {
        stageName: props.environment,
        tracingEnabled: true,
        metricsEnabled: true,
      },
    });

    this.apiUrl = api.url;
  }
}

The Power of grant* Methods

CDK L2 constructs include grant*() methods that automatically generate least-privilege IAM policies. table.grantReadWriteData(fn) creates a policy allowing only GetItem, PutItem, UpdateItem, DeleteItem, BatchGetItem, BatchWriteItem, and Query on that specific table ARN. This is much safer and easier than hand-writing IAM policies in CloudFormation, where getting the exact action list and resource ARN format correct is error-prone.

CDK Strengths

  • Real programming languages: Loops, conditionals, abstractions, and composition using TypeScript, Python, Java, Go, or C#
  • Type safety and IDE support: Autocomplete, inline documentation, and compile-time error detection catch mistakes before deployment
  • Reusable constructs: Create custom construct libraries and publish them to npm, PyPI, or Maven for organization-wide reuse
  • Built-in best practices: L2 constructs include sensible defaults (encryption enabled, removal policies, security settings)
  • Unit testing: Test infrastructure configuration with assertion libraries before deploying
  • CDK Pipelines: Self-mutating CI/CD pipelines that deploy infrastructure changes through stages with approvals

CDK Weaknesses

  • Programming knowledge required: Teams without software engineering experience face a steeper learning curve
  • Build step required: CDK must synthesize templates before deployment, adding complexity to the toolchain
  • Non-determinism risk: CDK can make API calls at synthesis time (VPC lookups, AZ lookups), introducing potential drift
  • Abstraction leakage: When CDK abstractions don't cover your use case, you drop to L1 constructs and lose the ergonomic benefits
  • Breaking changes: CDK library updates occasionally include breaking changes that require code modifications
  • Debugging complexity: Errors in synthesized CloudFormation templates can be difficult to trace back to CDK code
AWS IAM Best Practices: Infrastructure as Code for IAM Governance

Side-by-Side Comparison

DimensionCloudFormationCDK
LanguageJSON / YAMLTypeScript, Python, Java, Go, C#
Learning curveLow (declarative syntax)Medium (requires programming knowledge)
Code reuseNested stacks, modules, macrosClasses, packages, npm/PyPI/Maven
Testingcfn-lint, TaskCat, cfn-guardUnit tests, snapshot tests, fine-grained assertions
Abstraction levelResource-level onlyL1 (CFN), L2 (curated), L3 (patterns)
RollbackAutomaticAutomatic (uses CloudFormation under the hood)
State managementCloudFormation service (no external state)CloudFormation service + cdk.context.json
Drift detectionBuilt-inVia CloudFormation (same mechanism)
Multi-account deployStackSets (native, battle-tested)CDK Pipelines, cdk-nag, custom constructs
IDE experienceBasic YAML/JSON editingFull IDE support (autocomplete, go-to-definition, refactoring)
Community constructsAWS resource types, macrosConstruct Hub (1,400+ constructs)

Testing Infrastructure Code

One of CDK's strongest advantages is testability. You can write unit tests that verify your infrastructure configuration before deploying, catching issues like missing encryption, overly permissive security groups, or incorrect resource configurations. CloudFormation also has testing tools, but they are less integrated into the development workflow.

CDK Testing with Assertions

cdk-test.ts
import { Template, Match, Capture } from 'aws-cdk-lib/assertions';
import * as cdk from 'aws-cdk-lib';
import { ApiStack } from '../lib/api-stack';

describe('ApiStack', () => {
  let template: Template;

  beforeAll(() => {
    const app = new cdk.App();
    const stack = new ApiStack(app, 'TestStack', {
      environment: 'prod',
    });
    template = Template.fromStack(stack);
  });

  test('DynamoDB table has point-in-time recovery in prod', () => {
    template.hasResourceProperties('AWS::DynamoDB::Table', {
      PointInTimeRecoverySpecification: {
        PointInTimeRecoveryEnabled: true,
      },
    });
  });

  test('DynamoDB table uses PAY_PER_REQUEST billing', () => {
    template.hasResourceProperties('AWS::DynamoDB::Table', {
      BillingMode: 'PAY_PER_REQUEST',
    });
  });

  test('Lambda function has X-Ray tracing enabled', () => {
    template.hasResourceProperties('AWS::Lambda::Function', {
      TracingConfig: { Mode: 'Active' },
    });
  });

  test('Lambda has correct memory for prod', () => {
    template.hasResourceProperties('AWS::Lambda::Function', {
      MemorySize: 512,
    });
  });

  test('No S3 buckets are publicly accessible', () => {
    const buckets = template.findResources('AWS::S3::Bucket');
    for (const [id, bucket] of Object.entries(buckets)) {
      expect((bucket as any).Properties?.PublicAccessBlockConfiguration).toEqual(
        expect.objectContaining({
          BlockPublicAcls: true,
          BlockPublicPolicy: true,
        })
      );
    }
  });

  test('IAM roles have least-privilege policies', () => {
    const policyCapture = new Capture();
    template.hasResourceProperties('AWS::IAM::Policy', {
      PolicyDocument: policyCapture,
    });
    // Verify no wildcard actions
    const policy = policyCapture.asObject();
    for (const statement of policy.Statement) {
      expect(statement.Action).not.toContain('*');
    }
  });
});

CDK Nag for Compliance

CDK Nag is a validation tool that checks your CDK constructs against sets of rules (called Nag Packs). It catches security misconfigurations, compliance violations, and best practice deviations at synthesis time, before any resources are deployed.

cdk-nag-setup.ts
import { App, Aspects } from 'aws-cdk-lib';
import { AwsSolutionsChecks, NagSuppressions } from 'cdk-nag';
import { ApiStack } from '../lib/api-stack';

const app = new App();
const stack = new ApiStack(app, 'ProdStack', {
  environment: 'prod',
});

// Apply AWS Solutions checks to all constructs
Aspects.of(app).add(new AwsSolutionsChecks({ verbose: true }));

// Suppress known-acceptable findings with justification
NagSuppressions.addStackSuppressions(stack, [
  {
    id: 'AwsSolutions-APIG1',
    reason: 'Access logging configured at the account level via centralized logging',
  },
]);

CloudFormation Testing Tools

ToolPurposeWhen to Use
cfn-lintTemplate linting and validationPre-commit hook, CI/CD pipeline
cfn-guardPolicy-as-code validation rulesCompliance checks before deployment
TaskCatDeploy templates in test accountsIntegration testing CloudFormation templates
Change SetsPreview resource changesBefore every production update
cfn-guard-rule.guard
# CloudFormation Guard rules for security compliance
# Run: cfn-guard validate -d template.yaml -r rules.guard

# All S3 buckets must have encryption enabled
AWS::S3::Bucket {
  Properties.BucketEncryption.ServerSideEncryptionConfiguration[*] {
    ServerSideEncryptionByDefault.SSEAlgorithm in ["aws:kms", "AES256"]
  }
}

# All Lambda functions must have tracing enabled
AWS::Lambda::Function {
  Properties.TracingConfig.Mode == "Active"
}

# No security groups should allow SSH from 0.0.0.0/0
AWS::EC2::SecurityGroup {
  Properties.SecurityGroupIngress[*] {
    when IpProtocol == "tcp" and FromPort == 22 {
      CidrIp != "0.0.0.0/0"
    }
  }
}

CDK Context and Non-Determinism

CDK can make API calls at synthesis time to look up VPCs, AMIs, and Availability Zones. These values are cached in cdk.context.json. Always commit this file to version control. Without it, your templates may change unexpectedly when context values are re-fetched (for example, if a new AZ becomes available), leading to unintended infrastructure changes. Run cdk diff before every deployment to catch unexpected changes.

Creating Reusable Constructs

One of CDK's most powerful capabilities is the ability to create reusable construct libraries that encode your organization's best practices. Instead of documenting standards in a wiki and hoping teams follow them, you encode them directly in shared constructs that teams import and use.

secure-bucket-construct.ts
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as kms from 'aws-cdk-lib/aws-kms';
import * as cdk from 'aws-cdk-lib';

export interface SecureBucketProps {
  /** Bucket name (optional - CDK will generate if omitted) */
  bucketName?: string;
  /** Enable versioning (default: true) */
  versioned?: boolean;
  /** Days before transitioning to IA (default: 30) */
  iaTransitionDays?: number;
  /** Days before transitioning to Glacier (default: 90) */
  glacierTransitionDays?: number;
  /** Days before expiring objects (default: no expiration) */
  expirationDays?: number;
}

/**
 * A secure S3 bucket with encryption, access logging, lifecycle policies,
 * and public access blocking baked in.
 *
 * Usage:
 *   const bucket = new SecureBucket(this, 'DataBucket', {
 *     versioned: true,
 *     iaTransitionDays: 30,
 *   });
 */
export class SecureBucket extends Construct {
  public readonly bucket: s3.Bucket;
  public readonly key: kms.Key;

  constructor(scope: Construct, id: string, props: SecureBucketProps = {}) {
    super(scope, id);

    this.key = new kms.Key(this, 'Key', {
      enableKeyRotation: true,
      description: `Encryption key for ${id}`,
    });

    this.bucket = new s3.Bucket(this, 'Bucket', {
      bucketName: props.bucketName,
      encryption: s3.BucketEncryption.KMS,
      encryptionKey: this.key,
      bucketKeyEnabled: true,
      versioned: props.versioned ?? true,
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      enforceSSL: true,
      minimumTLSVersion: 1.2,
      objectOwnership: s3.ObjectOwnership.BUCKET_OWNER_ENFORCED,
      removalPolicy: cdk.RemovalPolicy.RETAIN,
      lifecycleRules: [
        {
          transitions: [
            {
              storageClass: s3.StorageClass.INFREQUENT_ACCESS,
              transitionAfter: cdk.Duration.days(props.iaTransitionDays ?? 30),
            },
            {
              storageClass: s3.StorageClass.GLACIER,
              transitionAfter: cdk.Duration.days(props.glacierTransitionDays ?? 90),
            },
          ],
          ...(props.expirationDays && {
            expiration: cdk.Duration.days(props.expirationDays),
          }),
          abortIncompleteMultipartUploadAfter: cdk.Duration.days(7),
        },
      ],
    });
  }
}
S3 Storage Classes: Lifecycle Policy Configuration

CDK Pipelines for CI/CD

CDK Pipelines is a high-level construct that creates a self-mutating CI/CD pipeline for deploying CDK applications. The pipeline deploys your infrastructure changes through stages (dev, staging, prod) with optional manual approvals. The “self-mutating” aspect means that when you modify the pipeline definition in code, the pipeline updates itself on the next run.

cdk-pipeline.ts
import * as cdk from 'aws-cdk-lib';
import { CodePipeline, CodePipelineSource, ShellStep } from 'aws-cdk-lib/pipelines';
import { ApiStage } from './api-stage';

export class PipelineStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const pipeline = new CodePipeline(this, 'Pipeline', {
      pipelineName: 'api-pipeline',
      synth: new ShellStep('Synth', {
        input: CodePipelineSource.gitHub('my-org/my-repo', 'main'),
        commands: [
          'npm ci',
          'npm run build',
          'npm run test',
          'npx cdk synth',
        ],
      }),
    });

    // Deploy to dev (no approval)
    pipeline.addStage(new ApiStage(this, 'Dev', {
      environment: 'dev',
      env: { account: '111111111111', region: 'us-east-1' },
    }));

    // Deploy to staging with integration tests
    const staging = pipeline.addStage(new ApiStage(this, 'Staging', {
      environment: 'staging',
      env: { account: '222222222222', region: 'us-east-1' },
    }));
    staging.addPost(new ShellStep('IntegrationTests', {
      commands: ['npm run test:integration'],
    }));

    // Deploy to prod with manual approval
    const prod = pipeline.addStage(new ApiStage(this, 'Prod', {
      environment: 'prod',
      env: { account: '333333333333', region: 'us-east-1' },
    }));
    prod.addPre(new cdk.pipelines.ManualApprovalStep('PromoteToProd'));
  }
}

StackSets for Governance, CDK Pipelines for Applications

Use CloudFormation StackSets for deploying organizational governance resources (IAM roles, Config rules, GuardDuty enablement) across all accounts. Use CDK Pipelines for deploying application infrastructure through dev/staging/prod stages. These tools serve different purposes and complement each other well in a well-architected multi-account environment.

Migration Strategies

Many organizations have existing CloudFormation templates and want to adopt CDK for new projects. CDK provides several migration paths.

Importing Existing CloudFormation Stacks

CDK can import existing CloudFormation resources into a CDK-managed stack using the cdk import command. This allows you to gradually migrate resources from manually managed CloudFormation stacks to CDK without recreating them.

Using L1 Constructs as a Bridge

L1 constructs (prefixed with Cfn) provide a 1:1 mapping to CloudFormation resources. You can translate CloudFormation YAML directly to L1 CDK code as a first migration step, then gradually refactor to L2 constructs to gain the ergonomic benefits.

Migration Decision Table

ScenarioRecommended Approach
New project, no existing templatesStart with CDK (TypeScript recommended)
Existing CFN templates, team knows programmingNew features in CDK, gradually migrate existing stacks
Existing CFN templates, team prefers declarativeStay with CloudFormation, adopt cfn-guard and modules
Multi-cloud requirementConsider Terraform or Pulumi instead
Organization-wide governanceCloudFormation StackSets for governance, CDK for apps

When to Choose Which

The right choice depends on your team, not the technology. Both tools are production-ready and can handle any scale of AWS deployment.

Choose CloudFormation When

  • Your team is not comfortable with programming languages and prefers declarative configuration
  • You need StackSets for multi-account/multi-region governance deployments
  • You are working with existing CloudFormation templates and do not want to migrate
  • You want the simplest possible toolchain with no build step, compiler, or package manager
  • You need to support teams across your organization with varying technical skills
  • You are deploying simple, well-defined infrastructure that does not need complex logic

Choose CDK When

  • Your team has software engineering experience and wants to leverage it for infrastructure
  • You want to create reusable infrastructure libraries shared across teams via package managers
  • You need complex logic: loops for creating multiple similar resources, environment-specific configuration, conditional resource creation
  • You want to unit test your infrastructure configuration as part of your CI/CD pipeline
  • You value IDE support with autocomplete, type checking, and inline documentation
  • You want built-in best practices from L2 constructs (encryption defaults, grant methods, security settings)

Consider Both

Many organizations use CDK for new projects while maintaining existing CloudFormation templates. CDK can import existing CloudFormation stacks, and you can use L1 constructs to work with CloudFormation resources directly when needed. There is no requirement to standardize on a single tool across your entire organization.

Key Takeaways

CDK and CloudFormation are complementary, not competing tools. CDK generates CloudFormation under the hood and adds the power of general-purpose programming languages. Start with CDK for new projects if your team has programming experience ; the testing, reuse, and IDE support significantly improve productivity. Use CloudFormation directly for simpler stacks, governance resources deployed via StackSets, or when your team prefers declarative configuration. Regardless of which tool you choose, invest in testing and validation before deploying to production. Always commit cdk.context.json for CDK projects and run cdk diffbefore every deployment.

AWS Well-Architected Framework: Operational Excellence PillarVPC Architecture Patterns: IaC for Network InfrastructureLambda Performance Tuning: Deploying Optimized Functions with CDK

Key Takeaways

  1. 1CloudFormation uses declarative YAML/JSON; CDK uses imperative code in TypeScript, Python, etc.
  2. 2CDK synthesizes to CloudFormation templates; it is an abstraction layer, not a replacement.
  3. 3CDK constructs (L1, L2, L3) provide increasing levels of abstraction and best-practice defaults.
  4. 4CloudFormation is better for teams with YAML expertise and simpler infrastructure.
  5. 5CDK excels when infrastructure needs loops, conditionals, or shared patterns across teams.
  6. 6Both support drift detection, change sets, and stack policies for safe deployments.

Frequently Asked Questions

Is AWS CDK replacing CloudFormation?
No. CDK generates CloudFormation templates under the hood. CDK is an abstraction layer that makes it easier to define CloudFormation resources using programming languages. CloudFormation remains the deployment engine.
Which should I choose: CloudFormation or CDK?
Choose CloudFormation if your team prefers declarative YAML and has simple infrastructure. Choose CDK if you need programmatic logic (loops, conditions), want type safety, or have complex reusable patterns. CDK has a steeper learning curve but more expressiveness.
Can I mix CloudFormation and CDK in the same project?
Yes. CDK can import existing CloudFormation templates using CfnInclude. You can also use CDK L1 constructs (CfnResource) to access any CloudFormation resource. Gradual migration from CloudFormation to CDK is common.
What are CDK constructs?
Constructs are the building blocks of CDK apps. L1 constructs map 1:1 to CloudFormation resources. L2 constructs add sensible defaults and helper methods. L3 constructs (patterns) combine multiple resources into opinionated architectures.
How does Terraform compare to CloudFormation and CDK?
Terraform is cloud-agnostic and uses HCL. CloudFormation/CDK are AWS-specific but have deeper AWS integration. Terraform has a larger provider ecosystem for multi-cloud. CDK for Terraform (CDKTF) brings CDK constructs to Terraform.

Written by CloudToolStack Team

Cloud engineers and architects with hands-on experience across AWS, Azure, and GCP. We write guides based on real-world production patterns, not just documentation rewrites.

Disclaimer: This guide is for educational purposes. Cloud services change frequently; always refer to official documentation for the latest information. AWS, Azure, and GCP are trademarks of their respective owners.