Infrastructure as Code: Looking at SAM + CDK as a Replacement for Serverless Framework and Terraform

Mario Bittencourt
SSENSE-TECH
Published in
10 min readMar 11, 2022

A good practice in any cloud-first approach is having a way to define your infrastructure as code. This helps you establish a reproducible environment and take advantage of all the benefits associated with source code versioning. At SSENSE we leverage two tools to do this: Hashicorp’s Terraform and Serverless Framework.

As the landscape, requirements from developers, and the maturity of the ecosystem evolve, new alternatives emerge that claim to provide similar or enhanced features, as well as a better developer experience. AWS Serverless Application Model (SAM) and Cloud Development Kit (CDK) present themselves as compelling solutions to replace our existing setup.

In this article, I will share my findings while evaluating SAM and CDK, how far they have evolved, where they excel and where there is still room for improvement.

Serverless Application Model

The concept of defining your infrastructure as code is not new. Even before cloud computing became popular we had tools that allowed us to define and automate the process of creating your application environment.

AWS has a service called CloudFormation, which allows you to specify the resources used by your application as config files using YAML or JSON. Those config files are known as templates and have sections that you fill in with the details of the resources you need.

CloudFormation specifies various sections: AWSTemplateFormatVersion, Description, Metadata, Parameters, Mappings, Conditions, Resources and Outputs. Out of these only two (AWSTemplateFormatVersion, Resources) are mandatory. A complete description of each section can be found here.

In the previous example you can see it specifies a DynamoDB table and provides the values to be used as parameters that can be overridden.

Anyone that has attempted to use CloudFormation can attest to the fact that the templates can quickly grow and become overwhelming to fill due to their size and the number of details.

Enter SAM, which is an extension of CloudFormation, focused on Function as a Service (FaaS) applications. It defines resources commonly used by FaaS applications by reducing the effort needed to create and maintain those templates.

If we compare a SAM template with a pure CloudFormation we will see it adds only one additional section named Globals which allows you to define common configuration options used by all resources.

SAM has 7 specific resources, all with the format AWS::Serverless::<Resource>:

  1. Api
  2. Application
  3. Function
  4. HttpApi
  5. LayerVersion
  6. SimpleTable
  7. StateMachine

If you need to define an API gateway backed lambda that interacts with a DynamoDB table it is as simple as this:

Besides helping with the definition of the resources, SAM’s ecosystem provides a CLI tool that helps you with packaging and deploying your infrastructure + code as well as running it locally for development purposes.

Running it Locally

SAM CLI comes with a number of features to help you to run your serverless project locally.

Let’s explore a scenario where your lambda is connected to an AWS API Gateway.

Starting your server

If we were talking about a non-serverless environment, you would have your server listening to a local port. SAM CLI local start-api option does that for you:

Figure 1. Running an API Gateway locally.

Sending requests via HTTP

We can see it exposes the defined endpoint(s) locally allowing you to use whatever HTTP-based tool to send requests such as the one illustrated in Figure 2:

Figure 2. Sending a local request to the API endpoint.

Invoking your lambda directly

On the other hand, if you want to invoke your lambda directly you can do so with sam local invoke:

Figure 3. Direct Lambda invocation.

Generate payloads specific to event sources

Here SAM CLI includes a helpful solution.

API Gateway is just one of the many event sources that can invoke a Lambda. SAM CLI allows you to generate sample payloads based on the different event sources.

For example, if you set SQS as the event source for your Lambda you can generate a sample event with the command sam local generate-event.

Figure 4. Generating events for Lambda.

Note that the generated event follows the format of an SQS message delivered to a Lambda function. Certain attributes, such as the md5OfBody, are even calculated for you as it would be in a real message sent via SQS.

If you save this event, you can use it when invoking locally with the option -e:

Figure 5. Local invocation using pre-recorded events.

Deploying

Deploying with SAM is as simple as having the AWS credentials set and running the command sam deploy. It will then take the template you defined and perform the creation of the resources, including setting the permissions you defined as IAM roles.

The first time you execute, you should use the –guided option, which will ask you for several standard parameters such as the name of the stack, the region to deploy, etc. If you have created a “Parameters” section in your template then it will also ask for the information specified there.

SAM CLI will save what you entered and if you need to deploy again it will use the same values.

This is nice, but can I get a side of fries with that?

So far SAM offers nice features that take care of executing your serverless application locally, defining and managing the lifecycle of the AWS resources used, and the associated permissions.

While this works fine, there are situations where you need to interface with other AWS services. For example, if your application needs to publish or consume messages to SQS, save information to DynamoDB, etc. Running locally becomes an issue as you can’t expect to have local versions of all AWS services on your machine.

One option is to use replacement services like localstack. While perfectly valid, you do have to deal with a few of complications:

  1. Setting up those services yourself to mimic what your application needs. This is not only tedious but error prone as you have to essentially keep them in sync with the resources used by your application.
  2. Not being able to rely on the replacement services to behave or support all the functionalities of the real ones.
  3. The resources needed by those services can become impractical to run locally.

Due to these challenges, it is preferable to run your applications in the cloud itself as part of the development cycle. This eliminates developers having the unnecessarily long feedback cycle of edit → save → deploy → test.

SAM has made progress on this with the beta support of its accelerate mode. With it, you essentially reduce the time it takes to deploy your application.

There is no magic solution. And, on the first deployment when you change the resources used, you still incur a higher cost as AWS needs to make those resources available in the cloud. For example, when creating new queues or DynamoDB tables.

But if you are just making changes to the code of your lambdas then the time it takes to deploy the changes becomes minimal.

Figure 6. Accelerate detecting changes to the application and automatically pushing changes to the cloud.

The previous image illustrates that after the first initial stack creation, SAM watches for changes in the code and synchronizes as changes are made.

Cloud Development Kit (CDK)

This is interesting but you can argue that if I go for the combo Serverless Framework + localstack I have almost everything SAM offers and therefore there is no real reason to change.

In a way, SAM and Serverless Framework use the same principle of specifying your infrastructure by creating definition files or templates where the resources are listed. While there is no intrinsic problem with that, what if there was a different and potentially better way? That is what Cloud Development Kit (CDK) proposes.

Different from SAM, or Serverless Framework, CDK allows you to define your resources using a programming language model. This means your developers can use the language they already know and leverage features such as IDE autocompletion to speed up the process.

Developing with CDK

Start by defining an Application Stack and inside this Stack start referencing one or more of the CDK building blocks known as Constructs. The code below illustrates an example of a DynamoDB table being defined for our application:

To elaborate on our initial example we can include the Lambda having write access to the DynamoDB table.

Because CDK relies on a programming language, you can create your custom Constructs by extending or combining multiple pre-existing Constructs that you can then re-use.

Let’s imagine your company decided that all event stores that use DynamoDB should have a specific data structure. You can build a stream and allow it to customize the name of the table used.

This approach makes it easier to define a library of such constructs internally to speed up the development and ensure everyone is following best practices and company standards.

To assist your development, AWS provides a Construct Hub where you can host public constructs with more than 900 available options. If you want to have a private version you can even use the project in a self-hosted environment.

Deploying with CDK

CDK handles the deployment of your application for you, creating the resources and permissions you specify. All you need to do is execute the cdk deploy and it will perform just the changes made after the previous deployment.

Figure 7. CDK deployment in action.

As you build the application and execute cdk deploy it will perform only the changes made from the previous deployment.

SAM & CDK, Better Together

After evaluating SAM and CDK, my first impulse was to consider how we could combine them to leverage the best bits of both:

  • SAM: the ability to run locally and have fast deployment with the accelerate option
  • CDK: define the resources using the same language as the developer is comfortable programming in and leverage the concept of Constructs

The good news is that you’re able to do just that!

First use the option cdk synth –no-stage to generate locally the necessary artifacts, such as the CloudFormation template. Then run sam local invoke as shown below:

Figure 8. SAM local invocation of a CDK-defined Lambda.

Are We There Yet?

No :(

Although you can use CDK and SAM together the experience is far from what could be considered developer-friendly. Let me highlight the 3 biggest limitations for me at the time of writing this article:

  • No watch/hot reload

You noticed that I had to perform a cdk build before calling the sam local command. This is because the invoke will look for the assets inside the generated cdk.out directory. If you make constant changes, as you invariably do when writing code, you have to run cdk build every time.

While you can automate this yourself, the problem is that even if you did not change any resource — no new functions, no updated permissions, etc. — CDK will still take a noticeable amount of time to re-generate the artifacts.

  • No accelerate option

If you do not want to run your application locally, the accelerate helps to speed up the cycle edit → deploy → test. Unfortunately, there is no such capability at the moment so you are stuck with cdk deploy which is not as snappy as you would need.

  • No support for runtime-specific CDK constructs

CDK offers a construct called NodeJsFunction that allows you to bundle the resources needed by your application. This allows you, for example, to write Typescript applications that get transpiled to Javascript before being deployed or executed.

If you happen to use typescript with your application this means you are out of luck, at least for now.

The Future Looks Promising

CDK is an interesting approach to define and maintain your infrastructure. It allows you to create reusable constructs using the language your developers are familiar with. SAM, on the other hand, provides a more standard config-based approach but with capabilities to allow not only local execution but also to reduce the time associated with the develop → deploy → test cycle via the accelerate features. .

Together they promise to be a combination that, if fully integrated, can reduce the barrier to making developers responsible for their infrastructure and adopting a serverless model, all without the need to depend on third-party tooling.

Unfortunately, due to the limitations previously described, this is not yet the case. SAM’s public roadmap is promising as it shows work already in progress to resolve some of the problems and speed up this integration.

If you are starting your project I would encourage you to evaluate CDK and keep an eye out for SAM to complement your local development.

Editorial reviews by Catherine Heim & Nicole Tempas.

Want to work with us? Click here to see all open positions at SSENSE!

--

--