How to use Cfn Resources in AWS CDK - Complete Guide

avatar

Borislav Hadzhiev

Fri Apr 23 20216 min read

banner

Photo by Austin Neill

Updated on Fri Apr 23 2021

In order to use CfnResources in AWS CDK we have to refer to 2 types of documentation - the CDK and CloudFormation docs.

Table of Contents #

  1. What are Cfn Resources
  2. Using Cfn Resources
  3. How to read docs for CFN Resources
  4. Another example of using Cfn Resources
  5. Defining Cfn Resources manually

What are Cfn Resources #

Cfn Resources are a type of Construct in AWS CDK.

You can think of Constructs as Cloud components. We use them to encapsulate logic, which we can reuse throughout our infrastructure code.

There are 3 levels of Constructs in the AWS CDK constructs library and Cfn Resources are Level 1 constructs.

They are 1x1 mappings to the resource in CloudFormation, which means they provide the lowest level of abstraction on top of CloudFormation.

Since our CDK code compiles down to CloudFormation we generally look for higher level of constructs that would enable us to hide some of the complexity that comes along with using CloudFormation.

However, we often have to use Cfn Resources, just because the CDK constructs library generates and exposes them automatically, immediately after a new resource is introduced in CloudFormation.

When we use Cfn Resources, we're basically writing CloudFormation using a programming language, instead of a configuration language (yaml or json).

They don't provide any of the opinionated defaults implemented in level 2 and level 3 constructs, nor do they provide any of the glue logic for service to service interactions, i.e. permission grants.

Using Cfn Resources #

When we need to use Cfn Resources, we often have to refer to the cloudformation docs.

The naming convention for Cfn Resources is Cfn + the resource's name, i.e.:

  • CfnBucket
  • CfnBucketPolicy
  • CfnFunction
  • CfnTable

Let's start with an example of a CfnBucket.

Like every other type of constructs, Cfn Resources take in 3 parameters:

  • the scope - specifies the parent construct within which the child construct is initialized, i.e. in JavaScript - the this keyword, in Python - self, etc.
  • the identifier - must be unique within the scope. The combination of CDK identifiers build the CloudFormation logical ID of the resource.
  • props - A map of key-value pairs where we pass in configuration options. This parameter is optional.

Before we start with the examples, it's very important to note that when using Cfn Resources, we are really just writing CloudFormation using a programming language.

If we look at the properties sections of the CfnBucket construct and the AWS::S3::Bucket CloudFormation resource we can see that the only difference is the casing.

CloudFormation properties use PascalCase, whereas CDK configuration properties use camelCase (in TypeScript).

All of the properties in the CDK and CloudFormation documentations are the same, however the CloudFormation documentation offers us way more information about what the properties actually do.

Let's look at an example of a CfnBucket first:

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

    // ๐Ÿ‘‡ using the CfnBucket construct
    const bucket = new s3.CfnBucket(this, 'uploads-bucket', {
      corsConfiguration: {
        corsRules: [
          {
            allowedMethods: [
              s3.HttpMethods.GET,
              s3.HttpMethods.POST,
              s3.HttpMethods.PUT,
            ],
            allowedOrigins: ['http://localhost:3000'],
            allowedHeaders: ['*'],
          },
        ],
      },
      tags: [{key: 'environment', value: 'development'}],
    });

    bucket.applyRemovalPolicy(cdk.RemovalPolicy.DESTROY);
  }
}

In the snippet above, we use the CfnBucket construct to create an s3 bucket, passing in cors configuration options and a tag.

We then call the applyRemovalPolicy method on the CfnBucket to set the deletion policy of the bucket to DESTROY.

The hard part is finding out what properties we need to use and the types and structure of said properties.

How to read docs for CFN Resources #

It can be quite confusing to understand where to look for the information we need, because most of the time we have to refer to documentation in 2 places:

A formula I use is:

  • refer to CloudFormation docs for properties, whose behavior I don't understand. In general the CDK docs only specify types for construct properties of Cfn Resources, whereas the CloudFormation docs explain the behavior of properties.
  • After I know what the property does, I use the CDK docs to understand the structure and type of the property
  • refer to the CDK docs for helper methods
  • try to use my IDE's auto completion as much as possible. If you use a modern IDE with a typed language you should be able to stay in the IDE most of the time.

In summary I only use CloudFormation documentation to understand the behavior of configuration properties.

Another example of using Cfn Resources #

Another example of using CfnResources that you'll use in almost every CDK application is the CfnOutput construct.

Outputs in CloudFormation and CDK enable us to output values from a stack, so:

  • we can reference them from another stack
  • we can redirect the output values to a file and import the values from our frontend code. A good example is outputting the value of an API Gateway url so our frontend can use it.

In our case we are going to output the name of the S3 bucket we created using the CfnOutput construct:

const bucketNameOutput = new cdk.CfnOutput(this, 'bucketName', {
  value: bucket.bucketName || '',
  description: 'The name of the s3 bucket for avatar uploads',
  exportName: 'avatarsBucket',
});

bucketNameOutput.overrideLogicalId('MyBucket');

In the example above we define our CfnOutput resource passing in the name of the S3 bucket as a value.

We then use the overrideLogicalId method on the Cfn Resource to override the CloudFormation logical ID of the Output.

The equivalent resource in CloudFormation would look like:

template.yaml
Outputs:
  MyBucket:
    Value: !Ref bucket
    Description: 'The name of the s3 bucket for avatar uploads'
    Export:
      Name: avatarsBucket

Let's take a look at the documentation of outputs in both CDK and CloudFormation:

In this case the properties in the CDK docs have a short explanation that has been copied from the CloudFormation docs.

Ideally in the future we'll be able to get all the information we need for CfnResources just by using the CDK documentation, because there are some small inconsistencies between the names of some properties.

Defining Cfn Resources manually #

In some very rare cases we have to manually define a Cfn Resource using the CfnResource class.

If, for example, the CfnTable resource wasn't available, we would have to use the CfnResource class and define a table ourselves.

It is very unlikely that you'll have to do that often, however it's good to know how to do it, just in case.

Let's create a Dynamodb table using the CfnResource class.

What we'll notice is that we're basically using CloudFormation, the property casing as well as the structure of properties is the same as in CloudFormation.

Here are the CloudFormation Dynamodb table docs.

new cdk.CfnResource(this, 'MyTable', {
  type: 'AWS::DynamoDB::Table',
  properties: {
    KeySchema: [
      {
        AttributeName: 'todoId',
        KeyType: 'HASH',
      },
      {
        AttributeName: 'date',
        KeyType: 'RANGE',
      },
    ],
    AttributeDefinitions: [
      {AttributeName: 'todoId', AttributeType: 'S'},
      {AttributeName: 'date', AttributeType: 'S'},
    ],
    BillingMode: 'PAY_PER_REQUEST',
  },
});

If you open the CloudFormation docs for the Dynamodb tables, you'll se that the configuration properties are the exact same.

As a comparison the CloudFormation template for the dynamodb table would look like:

template.yaml
Resources:
  MyTable:
    Type: AWS::DynamoDB::Table
    Properties:
      KeySchema:
        - AttributeName: todoId
          KeyType: HASH
        - AttributeName: date
          KeyType: RANGE
      AttributeDefinitions:
        - AttributeName: todoId
          AttributeType: S
        - AttributeName: date
          AttributeType: S
      BillingMode: PAY_PER_REQUEST

We should avoid using the CfnResource class when we can, because we get no autocompletion in our IDE.

If we were defining a more complicated resource it would be a very error prone process.

Even when using the Level 1 Cfn constructs, provided by the CDK team, there's a lot of work that's already been done for us behind the scenes.

Conclusion #

In order to use CfnResources in AWS CDK we have to refer to 2 types of documentation - the CDK and CloudFormation docs.

It's best to avoid using CfnResources if we can, however it's not always an option because Cfn Resources get generated automatically and are exposed almost immediately after release.

It takes time for the AWS CDK team to provide a higher level of abstraction for some resources in the form of Level 2 or Level 3 constructs.

For now the best way to work with Cfn Resources is to:

  • Read about the properties of the resources in the CloudFormation documentation
  • Get everything else from the CDK docs - i.e. types / structure of properties, available methods on the resources, etc.

Further Reading #

Join my newsletter

I'll send you 1 email a week with links to all of the articles I've written that week

Buy Me A Coffee