Happy Dussehra! May good always win. Happy Diwali! Wishing you a bright year ahead. Merry Christmas! Warm wishes from Vizag. Happy New Year! Here’s to a great year ahead.

Archive ยท DevOps

CloudFormation and the AWS CDK

Use CloudFormation stacks, change sets and drift detection, then write the same infrastructure in TypeScript with CDK v2, including bootstrapping, diffs and safe deletion policies.

3 min read

  • AWS CloudFormation
  • AWS CDK v2
  • TypeScript
  • Node.js 16

CloudFormation is the AWS-native service that creates resources from a template as a stack, tracks what it created, and rolls back if something fails. The AWS Cloud Development Kit (CDK) lets you write the same infrastructure in a programming language such as TypeScript or Python, then generates CloudFormation templates for you. CDK version 2, which is a single package called aws-cdk-lib, has been the current line since December 2021.

From code to resources

flowchart TB
  CODE[CDK app in TypeScript] -->|cdk synth| TPL[CloudFormation template JSON]
  YAML[Handwritten template YAML] --> TPL
  TPL -->|cdk diff or change set| REVIEW{Review changes}
  REVIEW -->|approve| STACK
  subgraph STACK[CloudFormation stack]
    R1[Resources] --> ST[Stack state and events]
  end
  STACK -->|create update delete| AWS[(AWS services)]
  STACK -->|failure| RB[Automatic rollback]
  BOOT[CDKToolkit bootstrap stack<br/>S3 bucket, ECR, IAM roles] -.-> STACK
  STACK --> DRIFT[Drift detection<br/>finds manual console changes]

CloudFormation by hand

AWSTemplateFormatVersion: "2010-09-09"
Parameters:
  Env: { Type: String, AllowedValues: [dev, prod] }
Resources:
  Assets:
    Type: AWS::S3::Bucket
    DeletionPolicy: Retain
    UpdateReplacePolicy: Retain
    Properties:
      BucketName: !Sub "acme-assets-${Env}-${AWS::AccountId}"
      VersioningConfiguration: { Status: Enabled }
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault: { SSEAlgorithm: AES256 }
Outputs:
  BucketArn: { Value: !GetAtt Assets.Arn }
aws cloudformation validate-template --template-body file://assets.yaml
aws cloudformation deploy --template-file assets.yaml --stack-name assets-dev \
  --parameter-overrides Env=dev --no-execute-changeset          # create a change set only
aws cloudformation describe-change-set --stack-name assets-dev --change-set-name <name-from-output>
aws cloudformation deploy --template-file assets.yaml --stack-name assets-dev --parameter-overrides Env=dev
aws cloudformation describe-stack-events --stack-name assets-dev --max-items 10
aws cloudformation detect-stack-drift --stack-name assets-dev

A change set shows what will be added, modified or replaced before anything happens. Replacement of a stateful resource (database, bucket) usually means data loss, so DeletionPolicy: Retain (or Snapshot for databases) and UpdateReplacePolicy are your safety net.

The same thing with CDK

node --version                        # use an active LTS version, 16 at the moment
npm install -g aws-cdk
mkdir assets && cd assets
cdk init app --language typescript
cdk bootstrap aws://111122223333/ap-south-1      # once per account and region

lib/assets-stack.ts:

import { Stack, StackProps, RemovalPolicy, CfnOutput } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';

export class AssetsStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
    const bucket = new s3.Bucket(this, 'Assets', {
      versioned: true,
      encryption: s3.BucketEncryption.S3_MANAGED,
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      enforceSSL: true,
      removalPolicy: RemovalPolicy.RETAIN,
    });
    new CfnOutput(this, 'BucketArn', { value: bucket.bucketArn });
  }
}
npm run build
cdk synth            # print the template
cdk diff             # what would change, including IAM statement changes
cdk deploy
cdk destroy          # respects the retain policy

CDK constructs come in three levels: L1 (CfnBucket, a direct template mapping), L2 (s3.Bucket, sensible defaults and helper methods like bucket.grantRead(fn)) and L3 (patterns that wire several services). L2 helpers generate least-privilege IAM policies for you, a big reason teams like CDK.

Choosing between tools

Question CloudFormation CDK Terraform
Only AWS Good Good Good
Several clouds or SaaS No No Yes
Loops, conditions, reuse Awkward Natural (a real language) Good (HCL)
State to manage yourself No (service tracks it) No Yes (remote state)
New AWS features Often first Follows CloudFormation Provider release lag

Operations

  • Stack policies and termination protection (aws cloudformation update-termination-protection --enable-termination-protection) for production stacks.
  • Roll back safely: failed updates roll back automatically; a stack stuck in UPDATE_ROLLBACK_FAILED can be continued with continue-update-rollback, skipping the stuck resource only when you understand why.
  • Split stacks by lifecycle (network, data, application). Avoid one giant stack: the limit is 500 resources per stack, and big stacks deploy slowly.
  • Cross-stack references with exports create tight coupling; pass values through parameters or SSM parameters where you can.
  • Deploy from CI with a role limited to CloudFormation and what the stack manages; commit cdk.context.json and the lock file, and pin the CDK version in package.json.
  • Run cfn-lint (templates) and unit-test CDK with the assertions module in the pull request.
  • Never edit CloudFormation-managed resources in the console; run drift detection regularly and fix drift in code.