Logo

dev-resources.site

for different kinds of informations.

Config AWS Cloudwatch Application Signals for NodeJs Lambda with CDK

Published at
1/4/2025
Categories
aws
awsapplicationsignals
cdk
cloudwatch
Author
johanneskonings
Author
15 person written this
johanneskonings
open
Config AWS Cloudwatch Application Signals for NodeJs Lambda with CDK

Use case

You want to use AWS Cloudwatch Application Signals for your NodeJs Lambda functions. For IaC you use CDK.

Setup

In a new AWS account the Management Console show the two steps to set up the Cloudwatch Application Signals. Step 1 is an Account wide setup, Step 2 is necessary for each Lambda function.

application signals config steps

Account wide setup

For starting the discovery the service linked role application-signals.cloudwatch.amazonaws.com must be created: https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/using-service-linked-roles.html#service-linked-role-signals. CDK itself has no direct functionality for that. The SDK can help here via a custom resource.

 const serviceLinkeRoleArnApplicationSignals = `arn:aws:iam::${Stack.of(this).account}:role/aws-service-role/application-signals.cloudwatch.amazonaws.com/AWSServiceRoleForCloudWatchApplicationSignals`;
    const applicationSignalsStartDiscovery = new AwsCustomResource(
      this,
      "ApplicationSignalsStartDiscovery",
      {
        onCreate: {
          service: "@aws-sdk/client-application-signals",
          action: "StartDiscovery",
          physicalResourceId: PhysicalResourceId.of(
            "ApplicationSignalsStartDiscovery",
          ),
        },
        // fromSdkCalls didn't work, that's why the policy is set manually
        // policy: AwsCustomResourcePolicy.fromSdkCalls({ resources: AwsCustomResourcePolicy.ANY_RESOURCE }),
        policy: AwsCustomResourcePolicy.fromStatements([
          new PolicyStatement({
            effect: Effect.ALLOW,
            actions: ["iam:CreateServiceLinkedRole"],
            resources: [serviceLinkeRoleArnApplicationSignals],
          }),
          new PolicyStatement({
            effect: Effect.ALLOW,
            actions: ["application-signals:StartDiscovery"],
            resources: ["*"],
          }),
        ]),
      },
    );

    const customResourceId = `AWS${AwsCustomResource.PROVIDER_FUNCTION_UUID.replaceAll("-", "")}`;
    NagSuppressions.addResourceSuppressionsByPath(
      Stack.of(this),
      [
        `/${Stack.of(this).stackName}/${customResourceId}/ServiceRole/Resource`,
        `/${Stack.of(this).stackName}/${customResourceId}/Resource`,
      ],
      [
        {
          id: "AwsSolutions-L1",
          reason: "CDK managed lambda function",
        },
        {
          id: "AwsSolutions-IAM4",
          reason: "CDK managed policy",
        },
        {
          id: "AwsSolutions-IAM5",
          reason: "CDK managed policy",
        },
      ],
      true,
    );

    NagSuppressions.addResourceSuppressions(
      applicationSignalsStartDiscovery,
      [
        {
          id: "AwsSolutions-IAM5",
          reason: "CDK managed policy",
        },
      ],
      true,
    );

Enter fullscreen mode Exit fullscreen mode

Each Lambda function

As described here each lambda need the environment variable AWS_LAMBDA_EXEC_WRAPPER with the value /opt/otel-instrument and the layer AWSOpenTelemetryDistroJs with the respective ARN.

  const LAMBDA_APPLICATION_SIGNALS_LAYER_ARN =
    "arn:aws:lambda:us-east-1:615299751070:layer:AWSOpenTelemetryDistroJs:5";
  const LAMBDA_APPLICATION_SIGNALS_ENV = {
    AWS_LAMBDA_EXEC_WRAPPER: "/opt/otel-instrument",
  };

    const lambda = new NodejsFunction(this, id, {
      runtime: Runtime.NODEJS_22_X,
      timeout: Duration.seconds(10),
      environment: props.enableApplicationSignals
        ? LAMBDA_APPLICATION_SIGNALS_ENV
        : {},
    });
    lambda.role?.addManagedPolicy(
      ManagedPolicy.fromAwsManagedPolicyName(
        "CloudWatchLambdaApplicationSignalsExecutionRolePolicy",
      ),
    );
    NagSuppressions.addResourceSuppressions(
      lambda,
      [
        {
          id: "AwsSolutions-IAM4",
          reason: "CDK managed policy",
        },
      ],
      true,
    );
    const layerApplicationSignals = LayerVersion.fromLayerVersionArn(
      this,
      "LambdaApplicationSignalsLayer",
      LAMBDA_APPLICATION_SIGNALS_LAYER_ARN,
    );

    lambda.addLayers(layerApplicationSignals);

Enter fullscreen mode Exit fullscreen mode

The lambda function implementation can than look like this:

const handler = async (event: undefined, context: undefined) => {
  console.log("lambda was called...");
  return {
    statusCode: 200,
    body: JSON.stringify({
      message: "Hello from Lambda!",
    }),
  };
};
module.exports = { handler };

Enter fullscreen mode Exit fullscreen mode

⚠️ The documentation recommend to use currently CommonJS (CJS) instead of ECMAScript Modules (ESM).

Also for CommonJs some "details" are to consider like the export of the handler: https://github.com/aws-observability/aws-otel-lambda/issues/284#issuecomment-1465465790

Result

After the setup and some runs of the Lambda function the Cloudwatch Application Signals are visible in the Management Console (It takes some minutes).

application signals result

cdk Article's
30 articles in total
Favicon
Invoking Private API Gateway Endpoints From Step Functions
Favicon
Automating Cross-Account CDK Bootstrapping Using AWS Lambda
Favicon
Deploy de NextJS utilizando CDK
Favicon
Config AWS Cloudwatch Application Signals Transaction Search with CDK
Favicon
[Solved] AWS Resource limit exceeded
Favicon
Create a cross-account glue Job using AWS CDK
Favicon
Enabling Decorators for Lambda Functions with CDK in an Nx Monorepo
Favicon
Run vs code on a private AWS ec2 instance without ssh (with AWS CDK examples)
Favicon
Config AWS Cloudwatch Application Signals for NodeJs Lambda with CDK
Favicon
Deploying a Simple Static Website on AWS with CDK and TypeScript
Favicon
AWS Architectural Diagrams on a Commit Base: Using AWS PDK Diagram Plugin with Python
Favicon
AWS CDK Aspects specifications have changed
Favicon
Relative Python imports in a Dockerized lambda function
Favicon
Building Scalable Infrastructure with AWS CDK: A Developer’s Guide to Best Practices
Favicon
API Gateway integration with AWS Services.
Favicon
DevSecOps with AWS- IaC at scale - Building your own platform – Part 3 - Pipeline as a Service
Favicon
AWS Cloud Development Kit (CDK) vs. Terraform
Favicon
Query your EventBridge Scheduled Events in DynamoDB
Favicon
AWS CDK context validation
Favicon
How to combine SQS and SNS to implement multiple Consumers (Part 2)
Favicon
Serverless Code-Signing (EV) with KMS and Fargate
Favicon
How to build an API with Lambdas, API Gateway and deploy with AWS CDK
Favicon
The Journey of CDK.dev: From Static Site to Bluesky
Favicon
Create an Asset Store with a Custom Domain using AWS CDK, Route53, S3 and CloudFront
Favicon
Crafting a Scalable Node.js API: Insights from My RealWorld Project with Express, Knex, and AWS CDK
Favicon
Techniques to Save Costs Using AWS Lambda Functions with CDK
Favicon
Deploy a Docker Image to ECS with Auto Scaling Using AWS CDK in Minutes
Favicon
AWS CDK + Localstack (API Gateway, Lambda, SQS, DynamoDB,TypeScript)
Favicon
Simplifying Cloud Infrastructure with AWS CDK
Favicon
Effortless Debugging: AWS CDK TypeScript Projects in VSCode

Featured ones: