From 4fab44d03bfd5313a05c756ac3089265878ce0de Mon Sep 17 00:00:00 2001 From: Eugene Cheung <81188333+echeung-amzn@users.noreply.github.com> Date: Tue, 29 Aug 2023 16:10:45 -0400 Subject: [PATCH] feat: upgrade secrets metric publisher Lambda to Node.js 18 (#417) Closes #393 Upgrades the second of the two Lambda handlers we have. Manually verified in a test account by manually invoking the Lambda with a valid event (e.g. `{"secretId": "test-secret"}`) and verifying that the metrics were emitted to CloudWatch. --- _By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license_ --- .../SecretsManagerMetricsPublisher/index.js | 89 +++++++++---------- .../SecretsManagerMetricsPublisher.ts | 2 +- .../MonitoringAspect.test.ts.snap | 4 +- ...ecretsManagerSecretMonitoring.test.ts.snap | 4 +- 4 files changed, 46 insertions(+), 53 deletions(-) diff --git a/assets/SecretsManagerMetricsPublisher/index.js b/assets/SecretsManagerMetricsPublisher/index.js index 51202fa2..8386a530 100644 --- a/assets/SecretsManagerMetricsPublisher/index.js +++ b/assets/SecretsManagerMetricsPublisher/index.js @@ -1,19 +1,16 @@ -const aws = require("aws-sdk"); +const { CloudWatchClient, PutMetricDataCommand } = require('@aws-sdk/client-cloudwatch'); +const { SecretsManagerClient, DescribeSecretCommand } = require('@aws-sdk/client-secrets-manager'); const region = process.env.AWS_REGION; const millisPerDay = 1000 * 60 * 60 * 24; const clientOptions = { - region, - maxRetries: 5, - httpOptions: { - connectTimeout: 3 * 1000, - timeout: 3 * 1000, - }, + signingRegion: region, + retryMode: 'standard', }; -const cloudwatch = new aws.CloudWatch(clientOptions); -const sm = new aws.SecretsManager(clientOptions); +const cloudwatchClient = new CloudWatchClient(clientOptions); +const secretsManagerClient = new SecretsManagerClient(clientOptions); function daysSince(date, now = Date.now()) { const millis = now - date.getTime(); @@ -22,19 +19,18 @@ function daysSince(date, now = Date.now()) { } exports.handler = async (event, context) => { - console.debug("event:", JSON.stringify(event)); - console.debug("context:", JSON.stringify(context)); + console.info(`Retrieving secret for event ${JSON.stringify(event)}`); + console.debug(`context: ${JSON.stringify(context)}`); - console.info(`retrieving secret: ${event.secretId}`); - const secret = await sm - .describeSecret({ + const secret = await secretsManagerClient.send( + new DescribeSecretCommand({ SecretId: event.secretId, }) - .promise(); + ); - console.debug("found secret: ", JSON.stringify(secret)); + console.debug(`Found secret: ${JSON.stringify(secret)}`); if (!secret.Name || !secret.CreatedDate) { - throw new Error("invalid secret response"); + throw new Error("Invalid secret response"); } // use retrieved secret name in case secretId was an arn @@ -43,40 +39,37 @@ exports.handler = async (event, context) => { const lastRotatedDate = secret.LastRotatedDate ?? secret.CreatedDate; const now = Date.now(); - const metricData = [ - { - MetricName: "DaysSinceLastChange", - Dimensions: [ - { - Name: "SecretName", - Value: secretName, - }, - ], - Unit: "Count", - Value: daysSince(lastChangedDate, now), - }, - { - MetricName: "DaysSinceLastRotation", - Dimensions: [ - { - Name: "SecretName", - Value: secretName, - }, - ], - Unit: "Count", - Value: daysSince(lastRotatedDate, now), - }, - ]; - const params = { Namespace: "SecretsManager", - MetricData: metricData, + MetricData: [ + { + MetricName: "DaysSinceLastChange", + Dimensions: [ + { + Name: "SecretName", + Value: secretName, + }, + ], + Unit: "Count", + Value: daysSince(lastChangedDate, now), + }, + { + MetricName: "DaysSinceLastRotation", + Dimensions: [ + { + Name: "SecretName", + Value: secretName, + }, + ], + Unit: "Count", + Value: daysSince(lastRotatedDate, now), + }, + ], }; - - console.debug("putMetricData params: ", JSON.stringify(params)); - - console.info(`publishing metrics for secret: ${event.secretId}`); - await cloudwatch.putMetricData(params).promise(); + console.debug(`putMetricData params: ${JSON.stringify(params)}`); + console.info(`Publishing metrics for secret "${event.secretId}"`); + const command = new PutMetricDataCommand(params); + await cloudwatchClient.send(command); return Promise.resolve(); }; diff --git a/lib/monitoring/aws-secretsmanager/SecretsManagerMetricsPublisher.ts b/lib/monitoring/aws-secretsmanager/SecretsManagerMetricsPublisher.ts index 065217c9..b7038f1a 100644 --- a/lib/monitoring/aws-secretsmanager/SecretsManagerMetricsPublisher.ts +++ b/lib/monitoring/aws-secretsmanager/SecretsManagerMetricsPublisher.ts @@ -34,7 +34,7 @@ export class SecretsManagerMetricsPublisher extends Construct { "Custom metrics publisher for SecretsManager Secrets (MonitoringCDKConstructs)", handler: "index.handler", memorySize: 128, - runtime: Runtime.NODEJS_14_X, + runtime: Runtime.NODEJS_18_X, timeout: Duration.seconds(60), logRetention: RetentionDays.ONE_DAY, }); diff --git a/test/facade/__snapshots__/MonitoringAspect.test.ts.snap b/test/facade/__snapshots__/MonitoringAspect.test.ts.snap index 8acfb1d6..f1753a93 100644 --- a/test/facade/__snapshots__/MonitoringAspect.test.ts.snap +++ b/test/facade/__snapshots__/MonitoringAspect.test.ts.snap @@ -3449,7 +3449,7 @@ Object { "S3Bucket": Object { "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}", }, - "S3Key": "370b9814d54928ec69d6005dd678b0f594a594948aa3322141bf10679ac71ee3.zip", + "S3Key": "d06eb8bb6deb3dba48c714538958f2ea17e2418a079ef471cb5b7b9de6a29d18.zip", }, "Description": "Custom metrics publisher for SecretsManager Secrets (MonitoringCDKConstructs)", "Handler": "index.handler", @@ -3460,7 +3460,7 @@ Object { "Arn", ], }, - "Runtime": "nodejs14.x", + "Runtime": "nodejs18.x", "Timeout": 60, }, "Type": "AWS::Lambda::Function", diff --git a/test/monitoring/aws-secretsmanager/__snapshots__/SecretsManagerSecretMonitoring.test.ts.snap b/test/monitoring/aws-secretsmanager/__snapshots__/SecretsManagerSecretMonitoring.test.ts.snap index 775f1dab..40411d4a 100644 --- a/test/monitoring/aws-secretsmanager/__snapshots__/SecretsManagerSecretMonitoring.test.ts.snap +++ b/test/monitoring/aws-secretsmanager/__snapshots__/SecretsManagerSecretMonitoring.test.ts.snap @@ -109,7 +109,7 @@ Object { "Arn", ], }, - "Runtime": "nodejs14.x", + "Runtime": "nodejs18.x", "Timeout": 60, }, "Type": "AWS::Lambda::Function", @@ -435,7 +435,7 @@ Object { "Arn", ], }, - "Runtime": "nodejs14.x", + "Runtime": "nodejs18.x", "Timeout": 60, }, "Type": "AWS::Lambda::Function",