diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/LambdaAlarmActionIntegrationTestStack.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/LambdaAlarmActionIntegrationTestStack.assets.json index 885823e5c7cdf..565a447c421aa 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/LambdaAlarmActionIntegrationTestStack.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/LambdaAlarmActionIntegrationTestStack.assets.json @@ -1,7 +1,7 @@ { "version": "36.0.0", "files": { - "443cd498fca620868cfc5e66c6d03b3dcd2cc9d88b6eba24927a4e8d06ae7984": { + "db34b72b17af5b7d83e311e6552b5f6cc3c82caf16943655fcbabc28a5c9b826": { "source": { "path": "LambdaAlarmActionIntegrationTestStack.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "443cd498fca620868cfc5e66c6d03b3dcd2cc9d88b6eba24927a4e8d06ae7984.json", + "objectKey": "db34b72b17af5b7d83e311e6552b5f6cc3c82caf16943655fcbabc28a5c9b826.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/LambdaAlarmActionIntegrationTestStack.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/LambdaAlarmActionIntegrationTestStack.template.json index c2fe3bad39eb1..d9b3e13120a6d 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/LambdaAlarmActionIntegrationTestStack.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/LambdaAlarmActionIntegrationTestStack.template.json @@ -79,8 +79,36 @@ } ], "EvaluationPeriods": 1, + "InsufficientDataActions": [ + { + "Ref": "alarmLambdaFeatureCurrentVersionCF39751979501d2f67eaf906b2ef0c378303873b" + }, + { + "Ref": "alarmLambdaFeatureAliasaliasName16F91D34" + }, + { + "Fn::GetAtt": [ + "alarmLambdaFeatureD560800F", + "Arn" + ] + } + ], "MetricName": "Errors", "Namespace": "AWS/Lambda", + "OKActions": [ + { + "Ref": "alarmLambdaFeatureCurrentVersionCF39751979501d2f67eaf906b2ef0c378303873b" + }, + { + "Ref": "alarmLambdaFeatureAliasaliasName16F91D34" + }, + { + "Fn::GetAtt": [ + "alarmLambdaFeatureD560800F", + "Arn" + ] + } + ], "Period": 60, "Statistic": "Sum", "Threshold": 1, @@ -309,8 +337,36 @@ } ], "EvaluationPeriods": 1, + "InsufficientDataActions": [ + { + "Ref": "alarmLambdaFeatureCurrentVersionCF39751979501d2f67eaf906b2ef0c378303873b" + }, + { + "Ref": "alarmLambdaFeatureAliasaliasName16F91D34" + }, + { + "Fn::GetAtt": [ + "alarmLambdaFeatureD560800F", + "Arn" + ] + } + ], "MetricName": "Errors", "Namespace": "AWS/Lambda", + "OKActions": [ + { + "Ref": "alarmLambdaFeatureCurrentVersionCF39751979501d2f67eaf906b2ef0c378303873b" + }, + { + "Ref": "alarmLambdaFeatureAliasaliasName16F91D34" + }, + { + "Fn::GetAtt": [ + "alarmLambdaFeatureD560800F", + "Arn" + ] + } + ], "Period": 60, "Statistic": "Sum", "Threshold": 1, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/LambdaAlarmActionIntegrationTestStackWithFeatureFlag.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/LambdaAlarmActionIntegrationTestStackWithFeatureFlag.assets.json index edab5767ec4c1..f24eedcc75bd4 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/LambdaAlarmActionIntegrationTestStackWithFeatureFlag.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/LambdaAlarmActionIntegrationTestStackWithFeatureFlag.assets.json @@ -1,7 +1,7 @@ { "version": "36.0.0", "files": { - "443cd498fca620868cfc5e66c6d03b3dcd2cc9d88b6eba24927a4e8d06ae7984": { + "db34b72b17af5b7d83e311e6552b5f6cc3c82caf16943655fcbabc28a5c9b826": { "source": { "path": "LambdaAlarmActionIntegrationTestStackWithFeatureFlag.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "443cd498fca620868cfc5e66c6d03b3dcd2cc9d88b6eba24927a4e8d06ae7984.json", + "objectKey": "db34b72b17af5b7d83e311e6552b5f6cc3c82caf16943655fcbabc28a5c9b826.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/LambdaAlarmActionIntegrationTestStackWithFeatureFlag.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/LambdaAlarmActionIntegrationTestStackWithFeatureFlag.template.json index c2fe3bad39eb1..d9b3e13120a6d 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/LambdaAlarmActionIntegrationTestStackWithFeatureFlag.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/LambdaAlarmActionIntegrationTestStackWithFeatureFlag.template.json @@ -79,8 +79,36 @@ } ], "EvaluationPeriods": 1, + "InsufficientDataActions": [ + { + "Ref": "alarmLambdaFeatureCurrentVersionCF39751979501d2f67eaf906b2ef0c378303873b" + }, + { + "Ref": "alarmLambdaFeatureAliasaliasName16F91D34" + }, + { + "Fn::GetAtt": [ + "alarmLambdaFeatureD560800F", + "Arn" + ] + } + ], "MetricName": "Errors", "Namespace": "AWS/Lambda", + "OKActions": [ + { + "Ref": "alarmLambdaFeatureCurrentVersionCF39751979501d2f67eaf906b2ef0c378303873b" + }, + { + "Ref": "alarmLambdaFeatureAliasaliasName16F91D34" + }, + { + "Fn::GetAtt": [ + "alarmLambdaFeatureD560800F", + "Arn" + ] + } + ], "Period": 60, "Statistic": "Sum", "Threshold": 1, @@ -309,8 +337,36 @@ } ], "EvaluationPeriods": 1, + "InsufficientDataActions": [ + { + "Ref": "alarmLambdaFeatureCurrentVersionCF39751979501d2f67eaf906b2ef0c378303873b" + }, + { + "Ref": "alarmLambdaFeatureAliasaliasName16F91D34" + }, + { + "Fn::GetAtt": [ + "alarmLambdaFeatureD560800F", + "Arn" + ] + } + ], "MetricName": "Errors", "Namespace": "AWS/Lambda", + "OKActions": [ + { + "Ref": "alarmLambdaFeatureCurrentVersionCF39751979501d2f67eaf906b2ef0c378303873b" + }, + { + "Ref": "alarmLambdaFeatureAliasaliasName16F91D34" + }, + { + "Fn::GetAtt": [ + "alarmLambdaFeatureD560800F", + "Arn" + ] + } + ], "Period": 60, "Statistic": "Sum", "Threshold": 1, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/manifest.json index ef633b1f8a767..cb7ad58c6f4ed 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/manifest.json @@ -18,7 +18,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/443cd498fca620868cfc5e66c6d03b3dcd2cc9d88b6eba24927a4e8d06ae7984.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/db34b72b17af5b7d83e311e6552b5f6cc3c82caf16943655fcbabc28a5c9b826.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/tree.json index 25a6b9b0b96f7..48b6babc0fda7 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.js.snapshot/tree.json @@ -135,8 +135,36 @@ } ], "evaluationPeriods": 1, + "insufficientDataActions": [ + { + "Ref": "alarmLambdaFeatureCurrentVersionCF39751979501d2f67eaf906b2ef0c378303873b" + }, + { + "Ref": "alarmLambdaFeatureAliasaliasName16F91D34" + }, + { + "Fn::GetAtt": [ + "alarmLambdaFeatureD560800F", + "Arn" + ] + } + ], "metricName": "Errors", "namespace": "AWS/Lambda", + "okActions": [ + { + "Ref": "alarmLambdaFeatureCurrentVersionCF39751979501d2f67eaf906b2ef0c378303873b" + }, + { + "Ref": "alarmLambdaFeatureAliasaliasName16F91D34" + }, + { + "Fn::GetAtt": [ + "alarmLambdaFeatureD560800F", + "Arn" + ] + } + ], "period": 60, "statistic": "Sum", "threshold": 1, @@ -516,8 +544,36 @@ } ], "evaluationPeriods": 1, + "insufficientDataActions": [ + { + "Ref": "alarmLambdaFeatureCurrentVersionCF39751979501d2f67eaf906b2ef0c378303873b" + }, + { + "Ref": "alarmLambdaFeatureAliasaliasName16F91D34" + }, + { + "Fn::GetAtt": [ + "alarmLambdaFeatureD560800F", + "Arn" + ] + } + ], "metricName": "Errors", "namespace": "AWS/Lambda", + "okActions": [ + { + "Ref": "alarmLambdaFeatureCurrentVersionCF39751979501d2f67eaf906b2ef0c378303873b" + }, + { + "Ref": "alarmLambdaFeatureAliasaliasName16F91D34" + }, + { + "Fn::GetAtt": [ + "alarmLambdaFeatureD560800F", + "Arn" + ] + } + ], "period": 60, "statistic": "Sum", "threshold": 1, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.ts index 48e806f6f191b..2b0791695d849 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudwatch-actions/test/integ.lambda-alarm-action.ts @@ -40,6 +40,12 @@ class LambdaAlarmActionIntegrationTestStack extends Stack { alarm.addAlarmAction(new cloudwatchActions.LambdaAction(version)); alarm.addAlarmAction(new cloudwatchActions.LambdaAction(aliasName)); alarm.addAlarmAction(new cloudwatchActions.LambdaAction(alarmLambda)); + alarm.addOkAction(new cloudwatchActions.LambdaAction(version)); + alarm.addOkAction(new cloudwatchActions.LambdaAction(aliasName)); + alarm.addOkAction(new cloudwatchActions.LambdaAction(alarmLambda)); + alarm.addInsufficientDataAction(new cloudwatchActions.LambdaAction(version)); + alarm.addInsufficientDataAction(new cloudwatchActions.LambdaAction(aliasName)); + alarm.addInsufficientDataAction(new cloudwatchActions.LambdaAction(alarmLambda)); if (isFeature) { const alarm2 = new cloudwatch.Alarm(this, `Alarm${lambdaIdSuffix}`, { @@ -53,6 +59,12 @@ class LambdaAlarmActionIntegrationTestStack extends Stack { alarm2.addAlarmAction(new cloudwatchActions.LambdaAction(version)); alarm2.addAlarmAction(new cloudwatchActions.LambdaAction(aliasName)); alarm2.addAlarmAction(new cloudwatchActions.LambdaAction(alarmLambda)); + alarm2.addOkAction(new cloudwatchActions.LambdaAction(version)); + alarm2.addOkAction(new cloudwatchActions.LambdaAction(aliasName)); + alarm2.addOkAction(new cloudwatchActions.LambdaAction(alarmLambda)); + alarm2.addInsufficientDataAction(new cloudwatchActions.LambdaAction(version)); + alarm2.addInsufficientDataAction(new cloudwatchActions.LambdaAction(aliasName)); + alarm2.addInsufficientDataAction(new cloudwatchActions.LambdaAction(alarmLambda)); } } } diff --git a/packages/aws-cdk-lib/aws-cloudwatch-actions/lib/lambda.ts b/packages/aws-cdk-lib/aws-cloudwatch-actions/lib/lambda.ts index 13312dc51caa9..cbbe19fb83103 100644 --- a/packages/aws-cdk-lib/aws-cloudwatch-actions/lib/lambda.ts +++ b/packages/aws-cdk-lib/aws-cloudwatch-actions/lib/lambda.ts @@ -23,12 +23,20 @@ export class LambdaAction implements cloudwatch.IAlarmAction { */ bind(scope: Construct, alarm: cloudwatch.IAlarm): cloudwatch.AlarmActionConfig { const idPrefix = FeatureFlags.of(scope).isEnabled(LAMBDA_PERMISSION_LOGICAL_ID_FOR_LAMBDA_ACTION) ? alarm.node.id : ''; - this.lambdaFunction.addPermission(`${idPrefix}AlarmPermission`, { - sourceAccount: Stack.of(scope).account, - action: 'lambda:InvokeFunction', - sourceArn: alarm.alarmArn, - principal: new iam.ServicePrincipal('lambda.alarms.cloudwatch.amazonaws.com'), - }); + const permissionId = `${idPrefix}AlarmPermission`; + const permissionNode = this.lambdaFunction.permissionsNode.tryFindChild(permissionId) as lambda.CfnPermission | undefined; + + // If the Lambda permission has already been added to this function + // we skip adding it to avoid an exception being thrown + // see https://github.com/aws/aws-cdk/issues/29514 + if (permissionNode?.sourceArn !== alarm.alarmArn) { + this.lambdaFunction.addPermission(permissionId, { + sourceAccount: Stack.of(scope).account, + action: 'lambda:InvokeFunction', + sourceArn: alarm.alarmArn, + principal: new iam.ServicePrincipal('lambda.alarms.cloudwatch.amazonaws.com'), + }); + } return { alarmActionArn: this.lambdaFunction.functionArn, diff --git a/packages/aws-cdk-lib/aws-cloudwatch-actions/test/lambda.test.ts b/packages/aws-cdk-lib/aws-cloudwatch-actions/test/lambda.test.ts index 027dfcd3f17f6..3c32c3b3a1d75 100644 --- a/packages/aws-cdk-lib/aws-cloudwatch-actions/test/lambda.test.ts +++ b/packages/aws-cdk-lib/aws-cloudwatch-actions/test/lambda.test.ts @@ -139,7 +139,12 @@ def handler(event, context): handler: 'index.handler', }); alarm1.addAlarmAction(new actions.LambdaAction(alarmLambda)); + alarm1.addOkAction(new actions.LambdaAction(alarmLambda)); + alarm1.addInsufficientDataAction(new actions.LambdaAction(alarmLambda)); + alarm2.addAlarmAction(new actions.LambdaAction(alarmLambda)); + alarm2.addOkAction(new actions.LambdaAction(alarmLambda)); + alarm2.addInsufficientDataAction(new actions.LambdaAction(alarmLambda)); // THEN Template.fromStack(stack).resourceCountIs('AWS::CloudWatch::Alarm', 2); @@ -173,9 +178,47 @@ def handler(event, context): handler: 'index.handler', }); alarm1.addAlarmAction(new actions.LambdaAction(alarmLambda)); + alarm1.addOkAction(new actions.LambdaAction(alarmLambda)); + alarm1.addInsufficientDataAction(new actions.LambdaAction(alarmLambda)); // THEN expect(() => { alarm2.addAlarmAction(new actions.LambdaAction(alarmLambda)); }).toThrow(/There is already a Construct with name 'AlarmPermission' in Function \[alarmLambda\]/); -}); \ No newline at end of file +}); + +test('can use same lambda for same action multiple time', () => { + const stack = new Stack(); + const alarm = new cloudwatch.Alarm(stack, 'Alarm', { + metric: new cloudwatch.Metric({ namespace: 'AWS', metricName: 'Test' }), + evaluationPeriods: 3, + threshold: 100, + }); + + // WHEN + const alarmLambda = new lambda.Function(stack, 'alarmLambda', { + runtime: lambda.Runtime.PYTHON_3_12, + functionName: 'alarmLambda', + code: lambda.Code.fromInline(` +def handler(event, context): + print('event:', event) + print('.............................................') + print('context:', context)`), + handler: 'index.handler', + }); + alarm.addAlarmAction(new actions.LambdaAction(alarmLambda)); + alarm.addAlarmAction(new actions.LambdaAction(alarmLambda)); + + // THEN + Template.fromStack(stack).resourceCountIs('AWS::Lambda::Permission', 1); + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { + AlarmActions: [ + { + 'Fn::GetAtt': ['alarmLambda131DB691', 'Arn'], + }, + { + 'Fn::GetAtt': ['alarmLambda131DB691', 'Arn'], + }, + ], + }); +});