CDK and SAM are both powerful Infrastructure as Code (IaC) tools for serverless development, but they approach the problem from fundamentally different angles.

Here’s a look at a typical serverless workflow using AWS Lambda and API Gateway, and how each tool handles it.

SAM in Action: A Simple API

Imagine you want to create a basic Lambda function that returns "Hello, World!" when an HTTP GET request hits a specific API Gateway endpoint.

With SAM, your template.yaml might look like this:

AWSTemplateFormatVersion: '2020-07-15'
Transform: AWS::Serverless-2016-10-31
Description: A simple Hello World API

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: my-hello-world-function
      Handler: app.lambda_handler
      Runtime: python3.9
      MemorySize: 128
      Timeout: 30
      Events:
        HelloWorldApi:
          Type: Api
          Properties:
            Path: /hello
            Method: get

Outputs:
  HelloWorldApiEndpoint:
    Description: "API Gateway endpoint URL for Prod stage for Hello World API"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello"

And your app.py:

import json

def lambda_handler(event, context):
    return {
        "statusCode": 200,
        "body": json.dumps({
            "message": "Hello, World!",
        }),
    }

To deploy this, you’d typically run:

sam build
sam deploy --guided

SAM takes this template, packages your code, and deploys it as CloudFormation. The AWS::Serverless::Function and AWS::Serverless::Api (implied by Events: Api) are SAM-specific resource types that abstract away many of the underlying AWS resources.

CDK in Action: The Same API

Now, let’s do the same with the AWS CDK. You’d define this in your preferred programming language, for example, TypeScript:

import * as cdk from '@aws-cdk/core';
import * as lambda from '@aws-cdk/aws-lambda';
import * as apigateway from '@aws-cdk/aws-apigateway';

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

    const helloWorldFunction = new lambda.Function(this, 'HelloWorldFunction', {
      functionName: 'my-hello-world-function',
      handler: 'app.lambda_handler',
      runtime: lambda.Runtime.PYTHON_3_9,
      memorySize: 128,
      timeout: cdk.Duration.seconds(30),
      code: lambda.Code.fromAsset('lambda'), // Assumes your code is in a 'lambda' directory
    });

    const api = new apigateway.LambdaRestApi(this, 'HelloWorldApi', {
      handler: helloWorldFunction,
      proxy: false, // We'll define specific routes
    });

    const helloResource = api.root.addResource('hello');
    helloResource.addMethod('GET'); // Lambda is the default integration

    new cdk.CfnOutput(this, 'HelloWorldApiEndpoint', {
      value: api.url + 'hello',
    });
  }
}

And lambda/app.py would be identical to the SAM example.

To deploy this, you’d run:

cdk bootstrap # (if you haven't already)
cdk synth    # (to see the CloudFormation output)
cdk deploy

The CDK synthesizes this into a CloudFormation template, but the key difference is that you’re using the AWS construct library, which maps directly to native CloudFormation resources. lambda.Function maps to AWS::Lambda::Function, and apigateway.LambdaRestApi maps to AWS::ApiGateway::RestApi, AWS::ApiGateway::Resource, and AWS::ApiGateway::Method.

The Core Difference: Abstraction vs. Direct Mapping

SAM is a framework built on top of CloudFormation. Its AWS::Serverless-* resources are abstractions that simplify common serverless patterns. This means SAM can often get you up and running faster with less code, and its local development tools (sam local) are generally more mature and integrated. SAM also has opinionated defaults and conventions that can be beneficial.

CDK, on the other hand, is a framework for defining CloudFormation (or other IaC backends) using general-purpose programming languages. While it has higher-level constructs (like apigateway.LambdaRestApi), it ultimately translates your code into standard CloudFormation resources. This gives you immense flexibility and allows you to define any AWS resource, not just those explicitly supported by a serverless-specific framework. You’re working with the native AWS resource model, just expressed in code.

When to Choose Which

  • Choose SAM if:

    • You’re primarily focused on serverless applications and want the fastest path to deployment for common patterns.
    • You value integrated local development and testing experiences provided by sam local.
    • Your team is comfortable with YAML-based templates and a more opinionated framework.
    • You’re building a greenfield serverless project and want to leverage SAM’s conventions.
  • Choose CDK if:

    • You need to define a wide range of AWS resources beyond just serverless, or want a unified IaC approach across your AWS footprint.
    • Your team prefers to write infrastructure code in familiar programming languages (TypeScript, Python, Java, C#, Go).
    • You require maximum flexibility and fine-grained control over every aspect of your infrastructure, mapping directly to CloudFormation resource properties.
    • You want to leverage the power of programming language features like classes, inheritance, and complex logic for infrastructure definition.
    • You’re already using CDK for other parts of your infrastructure.

The "One Thing" Most People Don’t Realize

While SAM’s AWS::Serverless::Function is convenient, it’s important to know that it is ultimately a CloudFormation resource. You can inspect the CloudFormation generated by sam build (often in .aws-sam/build/template.yaml) and see that it expands into standard AWS::Lambda::Function, AWS::IAM::Role, and AWS::ApiGateway::* resources. This means that when SAM’s abstractions don’t quite fit, you can drop down to defining native CloudFormation resources directly within your SAM template, giving you escape hatches. The CDK, by its nature, always works with these native resources, so you don’t have the same "abstraction layer" to think about dropping through.

The next step is to understand how each tool handles state management and drift detection.

Want structured learning?

Take the full Cdk course →