DEV Community

Masafumi Saito
Masafumi Saito

Posted on

AWS CLI and CDK Setup Journey - Part 2

AWS CLI and CDK Setup Journey - Part 2

Continuing from yesterday, I'm diving deeper into AWS CLI and CDK setup today!
Yesterday's post πŸ‘‡
https://dev.to/tofu1216/setting-up-aws-cli-and-cdk-2onp

Still following this awesome AWS official documentation:
https://docs.aws.amazon.com/cdk/v2/guide/hello-world.html

Here's my real experience going through Steps 5-12, including the bumps I hit along the way.


Step 5: Check Your CDK Stack List

Yesterday I got AWS CDK and local bootstrap all set up, so we're ready to deploy!
Let's check what Stacks we have (Stacks are basically how CDK groups and manages AWS resources together).

Run this command:

cdk list
Enter fullscreen mode Exit fullscreen mode

You should see just one Stack:

HelloCdkStack
Enter fullscreen mode Exit fullscreen mode

Step 6: Define Your Lambda Function

Time to update lib/hello-cdk-stack.ts with this code:

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
// Import the Lambda module
import * as lambda from 'aws-cdk-lib/aws-lambda';

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

    // Define the Lambda function resource
    const myFunction = new lambda.Function(this, "HelloWorldFunction", {
      runtime: lambda.Runtime.NODEJS_20_X, // Provide any supported Node.js runtime
      handler: "index.handler",
      code: lambda.Code.fromInline(`
        exports.handler = async function(event) {
          return {
            statusCode: 200,
            body: JSON.stringify('Hello World!'),
          };
        };
      `),
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

As Claude explained to me:

Constructs are basically AWS resources (like Lambda or S3) represented as TypeScript classes.
Think of them as blueprints that include settings and dependencies.
Enter fullscreen mode Exit fullscreen mode

This Function construct needs three key arguments:

  • scope: Where to place this function in the parent hierarchy (I'm still wrapping my head around this one - someone please help! 🀲)
  • id: Unique identifier for this function
  • props: The actual function settings
    • runtime: The language and version Lambda runs on
    • handler: Which function to execute
    • code: The actual function code

Step 7: Define the Lambda Function URL

We'll use the Function construct's addFunctionUrl helper method to create a Lambda function URL. To output this URL value when we deploy, we'll create an AWS CloudFormation output using the CfnOutput construct. (Still figuring this part out too, but apparently Lambda needs a URL to run!)

Add this code to lib/hello-cdk-stack.ts:

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

    // Define the Lambda function resource
    // ...

    // Define the Lambda function URL resource
    const myFunctionUrl = myFunction.addFunctionUrl({
      authType: lambda.FunctionUrlAuthType.NONE,
    });

    // Define a CloudFormation output for your URL
    new cdk.CfnOutput(this, "myFunctionUrlOutput", {
      value: myFunctionUrl.url,
    })

  }
}
Enter fullscreen mode Exit fullscreen mode

Step 8: Synthesize the CloudFormation Template

Now let's run this command to convert our TypeScript code into a CloudFormation template:

cdk synth
Enter fullscreen mode Exit fullscreen mode

If successful, you'll see the CloudFormation template output like this:

Resources:
  HelloWorldFunctionServiceRole<unique-identifier>:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
        Version: "2012-10-17"
      ManagedPolicyArns:
        - Fn::Join:
            - ""
            - - "arn:"
              - Ref: AWS::Partition
              - :iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
    Metadata:
      aws:cdk:path: HelloCdkStack/HelloWorldFunction/ServiceRole/Resource
  HelloWorldFunction<unique-identifier>:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        ZipFile: "

          \        exports.handler = async function(event) {

          \          return {

          \            statusCode: 200,

          \            body: JSON.stringify('Hello World!'),

          \          };

          \        };

          \      "
      Handler: index.handler
      Role:
        Fn::GetAtt:
          - HelloWorldFunctionServiceRole<unique-identifier>
          - Arn
      Runtime: nodejs20.x
    DependsOn:
      - HelloWorldFunctionServiceRole<unique-identifier>
    Metadata:
      aws:cdk:path: HelloCdkStack/HelloWorldFunction/Resource
  HelloWorldFunctionFunctionUrl<unique-identifier>:
    Type: AWS::Lambda::Url
    Properties:
      AuthType: NONE
      TargetFunctionArn:
        Fn::GetAtt:
          - HelloWorldFunction<unique-identifier>
          - Arn
    Metadata:
      aws:cdk:path: HelloCdkStack/HelloWorldFunction/FunctionUrl/Resource
  HelloWorldFunctioninvokefunctionurl<unique-identifier>:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunctionUrl
      FunctionName:
        Fn::GetAtt:
          - HelloWorldFunction<unique-identifier>
          - Arn
      FunctionUrlAuthType: NONE
      Principal: "*"
    Metadata:
      aws:cdk:path: HelloCdkStack/HelloWorldFunction/invoke-function-url
  CDKMetadata:
    Type: AWS::CDK::Metadata
    Properties:
      Analytics: v2:deflate64:<unique-identifier>
    Metadata:
      aws:cdk:path: HelloCdkStack/CDKMetadata/Default
    Condition: CDKMetadataAvailable
Outputs:
  myFunctionUrlOutput:
    Value:
      Fn::GetAtt:
        - HelloWorldFunctionFunctionUrl<unique-identifier>
        - FunctionUrl
Parameters:
  BootstrapVersion:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /cdk-bootstrap/<unique-identifier>/version
    Description: Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]
Rules:
  CheckBootstrapVersion:
    Assertions:
      - Assert:
          Fn::Not:
            - Fn::Contains:
                - - "1"
                  - "2"
                  - "3"
                  - "4"
                  - "5"
                - Ref: BootstrapVersion
        AssertDescription: CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI.
Enter fullscreen mode Exit fullscreen mode

Now we're ready to deploy!


Step 9: Deploy the CDK Stack

Let's deploy our CDK stack! This takes the CloudFormation template we just generated and deploys it through AWS CloudFormation.

cdk deploy --profile your-profile-name
Enter fullscreen mode Exit fullscreen mode

⚠️ Don't forget to specify your --profile!

Once it's done, head over to the AWS Console and check out your resources in both Lambda and CloudFormation.

Lambda


CloudFormation


How CloudFormation Templates and CDK Work Together πŸ”

Let me break down what's happening behind the scenes:

What is a CloudFormation Template?

  • A file written in JSON/YAML format that describes AWS resource configurations
  • Think of it as a blueprint that tells AWS "how to build what"
  • AWS CloudFormation service reads this and creates the actual resources

What is CloudFormation?

  • A service that reads JSON/YAML files and provisions actual AWS resources
  • It's like a "converter/execution engine"

How CDK Works:

  1. CDK is a tool that includes CloudFormation service
  2. Write code in TypeScript
  3. cdk synth converts your program code into CloudFormation template (JSON/YAML format)
  4. cdk deploy uses CloudFormation to deploy
Your CDK Code (TypeScript)
        ↓ cdk synth
CloudFormation Template (JSON)
        ↓ cdk deploy
CloudFormation Service (AWS)
        ↓ executes
Actual AWS Resources (Lambda, etc.)
Enter fullscreen mode Exit fullscreen mode

Step 10: Test Your Application

Use the URL that was displayed when you ran the cdk deploy command to test your function:

curl https://<api-id>.lambda-url.<Region>.on.aws/
Enter fullscreen mode Exit fullscreen mode

Here's my actual execution - it worked perfectly! πŸ‘Œ


Step 11: Make Changes to Your Application

Let's modify our Lambda code a bit. I changed myFunction like this:

// Define the Lambda function resource
const myFunction = new lambda.Function(this, "HelloWorldFunction", {
  runtime: lambda.Runtime.NODEJS_20_X, // Provide any supported Node.js
  handler: "index.handler",
  code: lambda.Code.fromInline(`
    exports.handler = async function(event) {
      return {
        statusCode: 200,
        body: JSON.stringify('Hello CDK I'm AWS Lambda!!'),
      };
    };
  `),
});
Enter fullscreen mode Exit fullscreen mode

To preview your changes, run:

cdk diff --profile your-profile-name
Enter fullscreen mode Exit fullscreen mode

Then deploy again:

cdk deploy --profile your-profile-name
Enter fullscreen mode Exit fullscreen mode

And test the URL again:

curl https://<api-id>.lambda-url.<Region>.on.aws/
Enter fullscreen mode Exit fullscreen mode

But here's where I hit a snag:

Internal Server Error%
Enter fullscreen mode Exit fullscreen mode

I checked the Lambda function logs in CloudWatch and found a syntax error!

Turns out the culprit was the apostrophe in I'm! Since I was using single quotes to wrap the string, the ' in I'm was being interpreted as the end of the string, causing a syntax error. Changed it to I am and everything worked perfectly! ⭕️


Step 12: Clean Up Your Application

Finally, let's clean up. This command will delete all the resources we created:

cdk destroy --profile your-profile-name
Enter fullscreen mode Exit fullscreen mode

Run the command and check the AWS Console. The Lambda function should be gone.

In CloudFormation, you'll notice the stack is still there. Apparently this is normal behavior. If you're not using CDK anymore, you might want to delete it manually.

If you do delete it, you can always bring it back with:

cdk bootstrap --profile your-profile-name
Enter fullscreen mode Exit fullscreen mode

Wrap Up

That's it! AWS CDK setup is complete. Now if I set up CDK in other environments within IAM Identity Center, I can easily switch between environments using command line profiles.

This is incredibly convenient!!

See you tomorrow! πŸ‘‹

Top comments (0)