CDK Workshop - The workshop used for these notes

AWS CDK Construct Library - This lists the constructs used in creating your resources

AWS CDK Documentation - AWS CDK Documentation

These notes are taken from the above link which i have worked through twice now. Once from the AWS CDK documentation and the second time using the link above. I am hoping that the link above will give me a better understanding of CDK and TS.

Prerequisites

export AWS_PROFILE={profile_name}

for the purposes of this i created a new IAM user and new profile

brew install node

install node for a mac

node --version

should be higher than v10.x

npm install -g aws-cdk 

installs the AWS CDK

Commands

cdk init sample-app --language typescript

This installs the “sample-app” and chooses typescript as the language

cdk synth
cdk bootstrap
cdk diff
cdk deploy
cdk destroy

File Structure

CDK App

The CDK app is defined in the lib/ directory while the app is built from the bin/ directory. The library contains the *.ts files that are used in the creation of the resources. CDK relies on CloudFormation and the CF template is created based on the resources defined in the lib/ TS files.

The lib/cdk-workshop-stack.ts is the file that defines the resources. The constructs are first imported and can then be used in the code to create the resource.

When you deploy the stack, you are calling bin/cdk-workshop.ts which imports the stack from the lib/ directory. A new “app” definition is created and then deployed.

To update the cloudformation stacks, you can edit the lib/*.ts files and remove resources from them. Once you run a cdk diff it will show the changes needed to update the infrastructure.

Once you are happy with the changes run a cdk deploy and any resources will be removed.

Adding to the Infrastructure

I am adding a Lambda function to the infrastructure which requires the node module to be installed

npm install @aws-cdk/aws-lambda
npm install @aws-cdk/aws-apigateway

This needs to be done each time you use a new construct.

Outputs

To declare an output, create a new cdk.CfnOutput function block. Define the name of the output function which contains the following properties:

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

        // defines an AWS Lambda resource
        const hello = new lambda.Function(this, 'HelloHandler', {
          runtime: lambda.Runtime.NODEJS_14_X,    // execution environment
          code: lambda.Code.fromAsset('lambda'),  // code loaded from "lambda" directory
          handler: 'hello.handler'                // file is "hello", function is "handler"
        });

           // 👇 create an Output
    new cdk.CfnOutput(this, 'lambdaArn', {
      value: hello.functionArn,
      description: 'The ARN of the Lambda Function',
      exportName: 'helloLambdaArn',
    });

Tests

I was running some tests on my code after destroying everything. This still works as it compares the code to be deployed and tests it before you cdk deploy. After making a change to a test, it failed. I was attempting to add a test for DynamoDB encryption and updated the .ts file. This was correct, but the .js file hadn’t been updated.

This was the test i was running:

test('DynamoDB Table Created With Encryption', () => {
    const stack = new cdk.Stack();
    // WHEN
    new HitCounter(stack, 'MyTestConstruct', {
      downstream:  new lambda.Function(stack, 'TestFunction', {
        runtime: lambda.Runtime.NODEJS_14_X,
        handler: 'hello.handler',
        code: lambda.Code.fromAsset('lambda')
      })
    });
    // THEN
    const template = Template.fromStack(stack);
    //
    // check the template has SSEEnabled = true
    //
    template.hasResourceProperties('AWS::DynamoDB::Table', {
      SSESpecification: {
        SSEEnabled: true
      }
    });
  });

And this was the .ts file that configmed i had enabled the encryption.

    const table = new dynamodb.Table(this, 'Hits', {
        partitionKey: { name: 'path', type: dynamodb.AttributeType.STRING },
        //
        // i was trying a few variations on encryption
        //
        // serverSideEncryption: true
        // encryption: dynamodb.TableEncryption.CUSTOMER_MANAGED
        encryption: dynamodb.TableEncryption.AWS_MANAGED,
      });

I then ran a cdk synth and confirmed that the template had been updated with the encryption:

"HelloHitCounterHits7AAEBF80": {
      "Type": "AWS::DynamoDB::Table",
      "Properties": {
        "KeySchema": [
          {
            "AttributeName": "path",
            "KeyType": "HASH"
          }
        ],
        "AttributeDefinitions": [
          {
            "AttributeName": "path",
            "AttributeType": "S"
          }
        ],
        "ProvisionedThroughput": {
          "ReadCapacityUnits": 5,
          "WriteCapacityUnits": 5
        },
        //
        // the encryption had been updated in the CFN template
        //
        "SSESpecification": {
          "SSEEnabled": true
        }
      },
      "UpdateReplacePolicy": "Retain",
      "DeletionPolicy": "Retain",
      "Metadata": {
        "aws:cdk:path": "CdkWorkshopStack/HelloHitCounter/Hits/Resource"
      }
    },

But the test was failing

➜  cdk-workshop git:(master) ✗ npm run test

> cdk-workshop@0.1.0 test
> jest

 FAIL  test/hitcounter.test.ts
  ✓ DynamoDB Table Created (121 ms)
  ✕ DynamoDB Table Created With Encryption (41 ms)
  ✓ Lambda Has Environment Variables (33 ms)
  .....
  lots of lines of code
  .....
with the following mismatches:
        Missing key at /Properties/SSESpecification (using objectLike matcher)

      32 |     // THEN
      33 |     const template = Template.fromStack(stack);
    > 34 |     template.hasResourceProperties('AWS::DynamoDB::Table', {
         |              ^
      35 |       SSESpecification: {
      36 |         SSEEnabled: true
      37 |       }

      at Template.hasResourceProperties (node_modules/@aws-cdk/assertions/lib/template.ts:52:13)
      at Object.<anonymous> (test/hitcounter.test.ts:34:14)

It seemed that the test was looking at an outdated JavaScript file as this didn’t contain the encryption. After running this command, i was able to refresh the deployed the JavaScript from my updated TypeScript.

https://docs.aws.amazon.com/cdk/latest/guide/work-with-cdk-typescript.html#typescript-running

npm run build

This manually updated the JavaScript file and after running the tests again, they succeeded.

npm run test

> cdk-workshop@0.1.0 test
> jest

 PASS  test/hitcounter.test.ts
  ✓ DynamoDB Table Created (147 ms)
  ✓ DynamoDB Table Created With Encryption (54 ms)
  ✓ Lambda Has Environment Variables (42 ms)

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        2.933 s, estimated 4 s
Ran all test suites.

Versions

The versions of the files stored in the package.json are critical to the smooth running of the CDK commands.

Ensure they are all set at the same version and without the “^” as this will ensure the version is upgraded. This is in the package.json file at the root of your application.

  "dependencies": {
    "@aws-cdk/aws-apigateway": "1.133.0",
    "@aws-cdk/aws-codecommit": "1.133.0",
    "@aws-cdk/aws-dynamodb": "1.133.0",
    "@aws-cdk/aws-lambda": "1.133.0",
    "@aws-cdk/aws-sns": "1.133.0",
    "@aws-cdk/aws-sns-subscriptions": "1.133.0",
    "@aws-cdk/aws-sqs": "1.133.0",
    "@aws-cdk/core": "1.133.0",
    "@aws-cdk/pipelines": "1.133.0",

Once the versions are hardcoded, run these commands.

npm upgrade -g
npm audit fix

Also ensure you aren’t using the latest version of node (v17.x). On a mac, run these commands.

brew install node@16
brew remove node@17
node --version
  v16.13.0

Node Version Manager

I have since discovered the Node Version Manager. This controls multiple versions of node on your machine.

brew install nvm
  - follow the directions to add a ~/.nvm directory and add some bits to your ~/.zshrc

# source your environment 
. ~/.zshrc

# confirm the binary is on your $PATH
nvm version

# download and install the latest version of Node (currently 17.1.0). node --version will show you 17.1.0
nvm install --lts

# list the versions of node available
nvm ls

# use the system integrated version of node (currently 16.13.0)
nvm use system

# confirm the version of node in use. This should show 16.13.0
node --version