When you start building a cloud-based back-end system for your application, you can manually build it using the graphical user interface (GUI) or command-line interface (CLI) provided by the cloud provider to provision cloud resources. Though, this manual approach is prone to human error and cannot be performed in a repeatable, reliable, or consistent manner due to the need for manual intervention. A popular solution is using an open-source software development framework like AWS CDK.

This comes with AWS Lamda which allows you to simply focus on your code while it manages and maintains your server resources. You can design your code into AWS CDK Lambda functions that process the events that you pass into the function. You can invoke your AWS CDK Lambda function via the Lambda API, or configure an AWS service or resource to run your function.

In this article, you will learn how to effectively create and deploy your AWS CDK Lambda function in 4 easy steps.

Steps to create a Lambda Function in AWS CDK

To create and deploy an AWS CDK Lambda function. You can follow the easy steps given below:

Step 1: Instantiate Function Class

To create the AWS CDK Lambda Function, you have first instantiate the Function class.

import * as iam from 'aws-cdk-lib/aws-iam';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as cdk from 'aws-cdk-lib';
import * as path from 'path';

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

    //  lambda function definition
    const lambdaFunction = new lambda.Function(this, 'lambda-function', {
      runtime: lambda.Runtime.NODEJS_14_X,
      memorySize: 1024,
      timeout: cdk.Duration.seconds(5),
      handler: 'index.main',
      code: lambda.Code.fromAsset(path.join(__dirname, '/../src/new-lambda')),
      environment: {
        REGION: cdk.Stack.of(this).region,
        AVAILABILITY_ZONES: JSON.stringify(
          cdk.Stack.of(this).availabilityZones,
        ),
      },
    });
 }
}

The above function configuration required the following properties:

  • runtime: Represents the programming language and version used for the AWS CDK Lambda function.
  • memorySize: 128 MB being the default value, this represents the amount of memory you want to allocate to the function. 
  • timeout: The time your function has to run before it is terminated.
  • handler: This denotes the file storing the lambda function named index.js and the handler function that is named main.
  • code: The source code of the lambda function.
  • environment: A complete map of your environment variables.

Step 2: Add the code for the Lambda Function

After instantiating the function class, now you can add the code for the AWS CDK Lambda function at src/new-lambda/index.js:

async function main(event) {
  console.log('region 👉', process.env.REGION);
  console.log('availability zones 👉', process.env.AVAILABILITY_ZONES);

  return {
    body: JSON.stringify({message: 'SUCCESS 🎉'}),
    statusCode: 200,
  };
}

module.exports = {main};

Step 3: Adding IAM Permissions for Lambda Function

As discussed in the above section, IAM roles are automatically assigned for your AWS CDK Lambda functions. You can also view the already added CloudWatch logging permissions for your autogenerated role:

AWS CDK Lambda - CloudWatch logging permissions
Image Source

You can simply add permissions to an AWS CDK Lambda function by attaching policies to the auto-generated role of the function. For instance, you can add a permission to list all of the S3 buckets in the account:

import * as iam from 'aws-cdk-lib/aws-iam';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as cdk from 'aws-cdk-lib';
import * as path from 'path';

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

    // ... rest

    // 👇 create a policy statement
    const listBucketsPolicy = new iam.PolicyStatement({
      effect: iam.Effect.ALLOW,
      actions: ['s3:ListAllMyBuckets'],
      resources: ['arn:aws:s3:::*'],
    });

    // 👇 attach the policy to the function's role
    lambdaFunction.role?.attachInlinePolicy(
      new iam.Policy(this, 'list-buckets', {
        statements: [listBucketsPolicy],
      }),
    );
  }
}

With this code, you can create a policy statement that allows the s3:ListAllMyBuckets action on all S3 resources. It also successfully attaches the policy to the IAM role of the AWS CDK Lambda function.

If you are looking to enhance your S3 resources further, you can consider migrating your data resources to Amazon’s fully managed NoSQL database, DynamoDB. To establish a connection between the two, use AWS Glue. The whole process is explained in detail here.

Step 4: Deploy the Function

Finally, you can deploy the AWS CDK Lambda function using the following command: 

npx aws-cdk deploy

To check if the desired permissions are added, you can navigate to the IAM management console and verify the role of the AWS CDK Lambda function. You will find that 2 permission policies are attached now, including the one that grants the s3:ListAllMyBuckets action.

AWS CDK Lambda - new permissions
Image Source

Challenges with the AWS CDK Lambda Functions

  • Long-drawn Operations: AWS CDK Lambda functions are best suited for short-term operations. As the function’s duration increases, it may exceed the Lambda timeout and throw an error.
  • Memory Limits: Since you create AWS CDK Lambda functions for shorter workloads, the memory limits are also scalable to a smaller extent. Extensive workload cases will lead to timeout issues as well as affect computational resource performance in the long run.
  • Synchronous Architectures: If you design a Lambda function that is responsible for executing a series of tasks one after the other, it will eventually lead to longer runtimes. You also may incur high costs in implementing the AWS CDK Lambda function.

AWS Lambda is a powerful compute service that allows you to run your code for virtually any type of application or backend service without worrying about configuring or managing servers. You can simply organize your application code in AWS CDK Lambda functions. To create or invoke your AWS CDK Lambda functions, you can use the AWS Lambda API or console. After creating your AWS CDK Lambda function, you can set multiple function capabilities and options such as permissions, environment variables, tags, and layers.

Use Cases of the AWS CDK Lambda Functions with Examples

  • Custom Applications for Mobile: Lambda functions can be useful while building custom applications for mobile devices. A good example of putting the AWS CDK Lambda function to use can be when you want to create photo-sharing applications. Users can upload their pictures on your app, and the application uses S3 buckets to store them. You can invoke a Lambda function to read the new image added to S3, create a thumbnail version of it, and save it to another S3 bucket.
  • Website Functions: While creating and developing a website, you may wish to host the backend logic on Lambda. You can build a custom code that allows you to place a Lambda function over the HTTP and integrate it with Amazon API Gateway. Through this setup, the requests that come through the API will be directed to the Lambda function to perform necessary computations.
  • Data Analysis: Since data analysis is vital for the growth of your business, you can lighten some of your load by writing Lambda functions. You can use it to generate custom business metrics that will be automatically applied to specific datasets. You can further streamline your analysis process by integrating AWS CDK Lambda functions with other AWS tools and services like Redshift or DynamoDB.

To know how you can load data to Amazon Redshift using AWS Lambda Function in just four easy steps, refer to Amazon Redshift Lambda Function: 4 Easy Steps to Load Data.

Understanding the Salient Aspects of AWS Lambda Construct Library

The AWS construct library contains modules for each AWS service with constructs that provide an API that encapsulates the details of AWS usage. The AWS Construct Library reduces the complexity and connection logic required when integrating various AWS services to achieve your goals on AWS. You can check out the following aspects of the AWS Lambda construct library: 

Docker Images

You specify the Lambda function’s handlers within docker images. The docker image can be an image from ECR or a local asset that the CDK will package and load into ECR. 

  • To understand this, observe the DockerImageFunction construct that uses a local folder with a Dockerfile as the asset that will be used as the AWS CDK Lambda function handler.
new lambda.DockerImageFunction(this, 'AssetFunction', {
  code: lambda.DockerImageCode.fromImageAsset(path.join(__dirname, 'docker-handler')),
});
  • Apart from the local asset, you can follow the example given below to specify an image that already exists in ECR as the AWS CDK Lambda Function handler.
import * as ecr from '@aws-cdk/aws-ecr';
const repo = new ecr.Repository(this, 'Repository');

new lambda.DockerImageFunction(this, 'ECRFunction', {
  code: lambda.DockerImageCode.fromEcr(repo),
});

It is important to note that these docker image resources allow overriding the image’s CMD, ENTRYPOINT, and WORKDIR configurations and choosing a specific tag or digest.

If you are looking to leverage Docker as a containerization platform with robust relational databases like PostgreSQL, you can set up a Docker PostgreSQL environment. Read about it here: Docker PostgreSQL: Efficient Database Management with Containers.

Handler Code

You can deploy your code using lambda.Code class. This class has the following methods for different types of runtime code:

  • lambda.Code.fromBucket(bucket, key[, objectVersion]): Denotes the S3 object that has the archive of your runtime code.
  • lambda.Code.fromInline(code): You can use this to inline the handle code as a string. 
  • lambda.Code.fromAsset(path): This allows you to mention a directory or a .zip file in your local filesystem which will be zipped and uploaded to S3 before deployment. 
  • lambda.Code.fromDockerBuild(path, options): You can also use the result of a Docker build as code. Generally, the code is present at /asset in the image and will be zipped and uploaded to S3 as an asset.

For instance, you can go through the following python AWS CDK Lambda function example that deploys the code from the local directory new-aws-lambda-handler to it. Note that during synthesis, the CDK expects to find a directory on disk at the asset directory specified. 

new lambda.Function(this, 'NewLambda', {
  code: lambda.Code.fromAsset(path.join(__dirname, 'new-aws-lambda-handler')),
  handler: 'index.main',
  runtime: lambda.Runtime.PYTHON_3_6,
});

Function Timeout

With a default timeout of 3 seconds, AWS CDK Lambda functions allow you to increase it up to 15 minutes. You can reference this property elsewhere in your stack, for example, to create a CloudWatch alarm to report when your function timed out:

import * as cdk from '@aws-cdk/core';
import * as cloudwatch from '@aws-cdk/aws-cloudwatch';

const fn = new lambda.Function(this, 'NewFunction', {
   runtime: lambda.Runtime.NODEJS_16_X,
   handler: 'index.handler',
   code: lambda.Code.fromAsset(path.join(__dirname, 'new-aws-lambda-handler')),
   timeout: cdk.Duration.minutes(5),
});

if (fn.timeout) {
   new cloudwatch.Alarm(this, `NewAlarm`, {
      metric: fn.metricDuration().with({
         statistic: 'Maximum',
      }),
      evaluationPeriods: 1,
      datapointsToAlarm: 1,
      threshold: fn.timeout.toMilliseconds(),
      treatMissingData: cloudwatch.TreatMissingData.IGNORE,
      alarmName: 'New Timeout',
   });
}

Execution Role

AWS CDK Lambda functions assume an IAM(Identity and Access Management) role during runtime. By Default, AWS CDK Lambda functions will assume an autogenerated Role if you have not assigned it. Moreover, the autogenerated Role is automatically given permissions to run the Lambda function. You can use the following commands to reference the autogenerated Role:

const fn = new lambda.Function(this, 'NewFunction', {
  runtime: lambda.Runtime.NODEJS_16_X,
  handler: 'index.handler',
  code: lambda.Code.fromAsset(path.join(__dirname, 'new-aws-lambda-handler')),
});
 
const role = fn.role; // the Role

For better control, you can also provide your own IAM role, though permission will not automatically be granted to run the AWS CDK Lambda function. You can provide a role and grant it appropriate permissions by following the example given below: 

const myRole = new iam.Role(this, 'My Role', {
  assumedBy: new iam.ServicePrincipal('sns.amazonaws.com'),
});
 
const fn = new lambda.Function(this, 'NewFunction', {
  runtime: lambda.Runtime.NODEJS_16_X,
  handler: 'index.handler',
  code: lambda.Code.fromAsset(path.join(__dirname, 'new-aws-lambda-handler')),
  role: myRole, // user-provided role
});
 
myRole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName("service-role/AWSLambdaBasicExecutionRole"));
myRole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName("service-role/AWSLambdaVPCAccessExecutionRole")); // only required if your function lives in a VPC

Resource-based Policies

AWS Lambda supports resource-based policies to control access to AWS CDK Lambda functions and tiers on a per-resource basis. Moreover, it gives AWS services and other AWS accounts the right to change and call functions. You can also limit AWS service permissions by specifying a source account or ARN(Amazon Resource Name).

declare const fn: lambda.Function;
const principal = new iam.ServicePrincipal('my-service');

fn.grantInvoke(principal);

// Equivalent to:
fn.addPermission('my-service Invocation', {
  principal: principal,
});

Architecture

You will observe that by default AWS CDK Lambda functions run on compute systems that have the 64-bit x86 architecture. They also run on the ARM architecture, which can reduce costs for some workloads. Depending on your requirement, you can configure an AWS CDK Lambda function can be configured to be run on one of these platforms:

new lambda.Function(this, 'NewFunction', {
  runtime: lambda.Runtime.NODEJS_16_X,
  handler: 'index.handler',
  code: lambda.Code.fromAsset(path.join(__dirname, 'new-aws-lambda-handler')),
  architecture: lambda.Architecture.ARM_64,
});

Conclusion

In this article, you have learned how to create and deploy an AWS CDK Lambda function effectively. AWS Lambda allows you to run your code by managing the compute fleet. The fleet offers memory, CPU, network, and other resources responsible for executing your code. It also takes care of the operational and administrative activities on your behalf.

Using the AWS CDK Lambda API or console, you can easily create functions, configure additional properties, and deploy them using programming languages such as JavaScript, TypeScript, Python, Java & .NET. To understand more about JavaScript Lambda functions, take a look at the JavaScript Lambda Function 2024: Key Features & Best Practices.

As you collect and manage data across several applications in your business, it is important to consolidate them for a complete performance analysis. However, it may be a time-consuming and resource-intensive task to monitor your databases and connectors continuously. To achieve this efficiently, you can turn to cloud-based ETL tools such as Hevo Data.

Visit our Website to Explore Hevo

If you are using AWS products such as Amazon S3 & Amazon Redshift and searching for a no-fuss alternative to Manual Data Integration, then Hevo can effortlessly automate this for you. Hevo allows you to not only export & load data but also transform & enrich your data & make it analysis-ready in a jiffy.

Want to take Hevo for a ride? Sign Up for a 14-day free trial and simplify your Data Integration process. Do check out the Hevo Pricing details to understand which plan fulfills all your business needs.

Share your experience of creating and deploying AWS CDK Lambda functions! Let us know in the comments section below!

Sanchit Agarwal
Former Research Analyst, Hevo Data

Sanchit Agarwal is a data analyst at heart with a passion for data, software architecture, and writing technical content. He has experience writing more than 200 articles on data integration and infrastructure. He finds joy in breaking down complex concepts in simple and easy language, especially related to data base migration techniques and challenges in data replication.

No-code Data Pipeline For Your Data Warehouse