101: Solving AWS Lambda LogGroup Persistence Issue with CDK Constructs

Alexander Smirnoff
3 min readJul 15, 2023

--

Introduction of the Problem

AWS Lambda is a powerful serverless computing service that allows developers to run their code without provisioning or managing servers. However, one persistent issue with Lambda functions is the handling of their corresponding LogGroups. When a Lambda function is deleted, the associated LogGroup often remains, causing deployment failures when attempting to redeploy a Lambda function with the same name. In this article, we will explore this problem and provide a solution using AWS CDK Constructs. We’ll delve into the code example for a Node.js Lambda function Construct that implements the necessary logic to create and remove the LogGroup, ensuring a smoother deployment process.

Introducing CDK Constructs

AWS Cloud Development Kit (CDK) is an open-source software development framework that enables developers to define cloud infrastructure resources using familiar programming languages. CDK Constructs are reusable cloud components encapsulating AWS resources and their associated configurations. In this case, we will create a CDK Construct to handle the creation and deletion of the LogGroup automatically.

Implementing the CDK Construct

Full code example:

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';

export interface NodejsLambdaConstructProps extends Omit<cdk.aws_lambda_nodejs.NodejsFunctionProps, "runtime"> {}

export class NodejsLambdaConstruct extends Construct {
public lambda: cdk.aws_lambda_nodejs.NodejsFunction;
public logGroup: cdk.aws_logs.LogGroup;

constructor(scope: Construct, id: string, props: NodejsLambdaConstructProps = {}) {
super(scope, id);

const { logRetention, ...lambdaProps } = props;

this.lambda = new cdk.aws_lambda_nodejs.NodejsFunction(this, id, {
runtime: cdk.aws_lambda.Runtime.NODEJS_18_X,
maxEventAge: cdk.Duration.seconds(3),
memorySize: 1024,
retryAttempts: 2,
bundling: { minify: true },
...lambdaProps,
});

this.logGroup = new cdk.aws_logs.LogGroup(this, "LambdaLogGroup", {
logGroupName: `/aws/lambda/${this.lambda.functionName}`,
removalPolicy: cdk.RemovalPolicy.DESTROY,
retention: logRetention ?? cdk.aws_logs.RetentionDays.TWO_WEEKS,
});
}
}

The class defines the NodejsLambdaConstructProps interface, which extends the NodejsFunctionProps. This interface allows developers to customize the Lambda function’s properties.

Please note that you should update the runtime property inside the this.lambda initialization to the appropriate version for your specific use case. The best practice is to avoid using different runtimes for functions written in the same language. Therefore, in this case, we will exclude the runtime property from the NodejsLambdaConstructProps interface by omitting it from the original interface.:

export interface NodejsLambdaConstructProps extends Omit<cdk.aws_lambda_nodejs.NodejsFunctionProps, "runtime"> {}

Within the constructor we create a new instance of the NodejsFunction class to define the Lambda function. Except runtime various properties such as memory size, bundling options, etc., can be customized.

this.lambda = new cdk.aws_lambda_nodejs.NodejsFunction(this, id, {
runtime: cdk.aws_lambda.Runtime.NODEJS_18_X,
maxEventAge: cdk.Duration.seconds(3),
memorySize: 1024,
retryAttempts: 2,
bundling: { minify: true },
...lambdaProps,
});

Next, we need to create a new instance of the LogGroup class to define the LogGroup associated with the Lambda function. To associate the LogGroup with the Lambda function we need to set the logGroupName property to /aws/lambda/${this.lambda.functionName}, ensuring a consistent naming convention.

To enable automatic deletion of the LogGroup when the Lambda function is deleted we need to set removalPolicy to DESTROY. Additionally, the retention property can be customized, specifying the number of days the logs should be retained. For example, it could be useful to specify a different retention policy for dev, stage and prod environments.

this.logGroup = new cdk.aws_logs.LogGroup(this, "LambdaLogGroup", {
logGroupName: `/aws/lambda/${this.lambda.functionName}`,
removalPolicy: cdk.RemovalPolicy.DESTROY,
retention: logRetention ?? cdk.aws_logs.RetentionDays.TWO_WEEKS,
});

Usage

The code snippet below showcases the usage of the NodejsLambdaConstruct within the context of an MyAWSomeStack class:

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

const { lambda, logGroup } = new NodejsLambdaConstruct(this, "NodejsLambdaConstruct");

new cdk.CfnOutput(this, "LambdaArn", {
value: lambda.functionArn,
description: "Lambda ARN",
exportName: "LambdaArn"
});

new cdk.CfnOutput(this, "LambdaLogGroup", {
value: logGroup.logGroupName,
description: "Lambda Log Group",
exportName: "LambdaLogGroup"
});
}
}

Conclusion

The persistence of LogGroups after deleting Lambda functions can cause deployment issues in AWS. By leveraging the power of CDK Constructs, developers can automate the creation and removal of LogGroups for Lambda functions, mitigating the problem. The provided code example demonstrates a Node.js Lambda Construct that encapsulates the logic to create and remove the LogGroup, making deployments more seamless and efficient. By incorporating this solution into your AWS Lambda workflows, you can save time and avoid unnecessary deployment failures due to lingering LogGroups.

--

--