CDK Workshop, with Graph Visualizations

AWS CDK is excellent, and might be my favorite AWS product ever. I am using it to better understand other AWS services and how they are supposed to be set up, with the help of fancy graphics.

A sample graphic is this Lambda + Role setup:

Lambda

with the interactive version here.

Starting with Cloudformation

AWS CDK replaces/upgrades Cloudformation (Cfn), which always felt very clunky to me. Extremely configurable, Cfn makes anything possible, but almost all the configurations are wrong: maybe they have too-broad permissions, or are forgetting a key resource, etc. Expressiveness is not Cfn’s forte. Instead of “I want a lambda behind an APIGateway endpoint”, you need to know about (and create) every resource each of those services requires to integrate. I hope you know what the best-practice setup of all those resources are, because nothing is going to tell you.

Enter CDK

CDK actually lets you write “I want a lambda behind an APIGateway endpoint”, and it just works. Under the hood, CDK generates Cloudformation, so you are not technically locked in while CDK is under active development.

Try CDK out

The CDK Workshop is an excellent intro to get your hands dirty. When you first start the sample app, your directory will look like

.
├── README.md
├── app.py
├── cdk.json
├── cdkwork2
│   ├── __init__.py
│   ├── cdkwork2.egg-info
│   │   ├── PKG-INFO
│   │   ├── SOURCES.txt
│   │   ├── dependency_links.txt
│   │   ├── requires.txt
│   │   └── top_level.txt
│   └── cdkwork2_stack.py
├── requirements.txt
├── setup.py
├── source.bat
└── tests
    ├── __init__.py
    └── unit
        ├── __init__.py
        └── test_cdkwork2_stack.py

and you get a Queue subscribed to a Topic. The relevant Python is

# app.py

class Cdkwork2Stack(core.Stack):

    def __init__(self, scope: core.Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        queue = sqs.Queue(
            self, "Cdkwork2Queue",
            visibility_timeout=core.Duration.seconds(300),
        )

        topic = sns.Topic(
            self, "Cdkwork2Topic"
        )

        topic.add_subscription(subs.SqsSubscription(queue))

It looks like this:

Sample app

Nodes are Resources in the generated Cloudformation, and arrows point to dependencies of those Resources. The interactive version of this is here.

You can see the generated YAML with cdk synth.

Evolving a setup

To really drive home how much work CDK is doing for us, watch what happens to go from “a Lambda” to “a Lambda behind APIGateway”

Just a Lambda

For the first step in the workshop, we change our stack to have a single Lambda with a handler. Our project adds a lambda directory with a file:

.
...
├── lambda
│   └── hello.py
...

And the stack becomes

class Cdkwork2Stack(core.Stack):
    def __init__(self, scope: core.Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        my_lambda = _lambda.Function(
            self,
            "HelloHandler",
            runtime=_lambda.Runtime.PYTHON_3_7,
            code=_lambda.Code.asset("lambda"),
            handler="hello.handler",
        )

And the generated Cloudformation only has two resources:

Lambda

The interactive version of this chart is here.

Add APIGateway

Exposing the Lambda to the internet means putting it behind APIGateway. In CDK, that is easy:

class Cdkwork2Stack(core.Stack):
    def __init__(self, scope: core.Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        my_lambda = _lambda.Function(
            self,
            "HelloHandler",
            runtime=_lambda.Runtime.PYTHON_3_7,
            code=_lambda.Code.asset("lambda"),
            handler="hello.handler",
        )

        apigw.LambdaRestApi(
            self,
            "Endpoint",
            handler=my_lambda,
        )

But in Cloudformation, it’s much more complex.

APIGateway

Our original Lambda setup is in the top right; you can see that the hashes match. Everything else came from those three lines of LambdaRestApi in the cdk app. You can see the interactive version of this graph here.

Learning from the graph

“API with Lambda backing” is a clear and comprehensible idea; implementing it from first principles in Cfn is harder because you need to know what all the AWS nouns are. CDK + graphics let us reason backwards from known good setups, and ask what all the pieces are for.

I did not know what an APIGateway Account was before doing this research. From what I read, it appears to be a magic resource expected by APIGateway to reference an ARN of an IAM Role to authorize APIGW to write logs to Cloudwatch. It’s weird that there is an implicit ordering; if you have never created an APIGW resource before, you need to make an Account depend on the first one you make via template.

Based on the generated Cloudformation from CDK (and the CDK Examples repo), I expect CDK to make it to be much easier to learn how to build the things I want. Even if I don’t stick with CDK long term, getting decipherable best-practice examples is a huge win.