Go fast and reduce risk: using CDK to deploy your serverless applications on AWS
Tags: aws, devops, cdk, iac
Published: 2022-12-28 19:19:22

There are a truly absurd number of ways to deploy applications on AWS. Corey Quinn wrote two [separate](https://www.lastweekinaws.com/blog/the-17-ways-to-run-containers-on-aws/) [articles](https://www.lastweekinaws.com/blog/17-more-ways-to-run-containers-on-aws/) wherein he described 17 ways to deploy containers on AWS -- that's **34 different ways just to deploy containers** (granted, some of the listed ways to deploy are farcical).

All of these options inevitably spawn fights about the "right" way to deploy on AWS. I'm now going to explain my preferred way to deploy a serverless API, and then you can fight me.

## The best way to deploy your serverless API on AWS:

1. Write your API business logic code in a way that can be run on AWS Lambda.
2. Write a CDK template in the **same language** as your Lambda function code **_and store along with your Lambda function code in source control_** (I'm going to assume you're using git here, but use whatever source control you want).
3. Deploy your serverless API from your git commit as a self-contained unit.

There's a lot to unpack here, but the main thing I want convey here is that _it is awesome to write your executable code and your IaC templates in the same language_.

**Your CDK template**

So what is CDK? It's the AWS 'Cloud Development Kit', and it basically lets you transpile your language of choice (as long as that language is either JavaScript, TypeScript, Python, Java, C#, or Go) into CloudFormation. This is awesome, because you don't have to deal with a weird domain specific language for your IaC or manage state files for your IaC (looking at you, Terraform).

In the case of a serverless API, these are the main things you'll want in your CDK code:

1. IAM policies that limit your lambda permissions using the principle of least privilege.
2. A Lambda resource that you'll deploy your code to.
3. An API Gateway resource (RestApi) that you'll point at your Lambda.

There are a bunch of other services you could include here (RDS database, dynamoDB, etc etc) if you need them, but that's the beauty of this system -- you're not limited by a specific serverless framework, you can just add arbitrary services to your CDK code as you need them.

Here's a bare-bones version of a serverless CDK stack file in Python (you can also write these files in JavaScript, TypeScript, Java, C#, or Go), so you can see what I mean (everything has the generic name "somestack", which you can replace with a more meaningful name):

```
from aws_cdk import (
aws_lambda as lambda_,
aws_apigateway as apigw,
aws_iam as iam,
aws_ecr as ecr,
Stack)
from constructs import Construct
import os

class SomeStackName(Stack):
    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)
        
        # Create the Lambda execution role
        lambda_role = iam.Role(self, id="somestack-lambda",
            role_name='SomestackManagementRole',
            assumed_by=iam.ServicePrincipal("lambda.amazonaws.com"),
            managed_policies= [
                        iam.ManagedPolicy.from_aws_managed_policy_name("service-role/AWSLambdaVPCAccessExecutionRole"),
                        iam.ManagedPolicy.from_aws_managed_policy_name("service-role/AWSLambdaBasicExecutionRole")
                    ]
        )
        # Get an existing ECR repo that has a docker image you want to deploy to Lambda
        repo = ecr.Repository.from_repository_name(self, "SomestackRepo", "somestack_ecr_repo")
        # Create the Lambda function
        somestack_lambda = lambda_.DockerImageFunction(self,
            "SomestackLambda",
            code=lambda_.DockerImageCode.from_ecr(
                repository=repo,
                tag=some_image_tag_name
                ),
            role=lambda_role
        )
        
        # Create the API Gateway
        api = apigw.LambdaRestApi(self,
            "somestack-endpoint",
            handler=somestack_lambda,
            default_cors_preflight_options=apigw.CorsOptions(allow_origins=["*"])
        )
```

That's all the Infrastructure as Code that you need to create a serverless API! You can learn more from the [official CDK developer guide](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html).

## The actual development workflow

So now you have your CDK IaC written and you're ready to start incorporating it into a real devops pipeline. What does the day-to-day development process look like?

1. Make a branch.
2. Make some Lambda code changes, some CDK changes, whatever, and push your branch.
4. A git server webhook should trigger a CDK build to test your Infrastructure as Code (IaC). This webhook also runs some tests on your temporarily built environment to make sure you have no regressions.
5. Merge your branch into main (master, whatever you call it).
6. Another git server webhook that only triggers on main branch commits deploys your code to your dev environment (which can then be promoted to stage/prod as necessary)

Now you're ready to build serverless, production-grade APIs with incredible speed and very little risk! You don't have to have a team of engineers doing server maintenance, and you can spin up as many independent microservices as you need to without worrying about autoscaling policies or cluster management. It's an incredibly powerful pattern for certain types of software.