From 93808851415bff269418f28d9de3c61727e143d3 Mon Sep 17 00:00:00 2001 From: Vincent Porta-Scarta Date: Tue, 1 Feb 2022 16:07:13 -0500 Subject: [PATCH] feat(cloudwatch-actions): add ssm opsitem action for cloudwatch alarm (#16923) This small PR will add SSM OpsItem action to cloudwatch alarm. The arn format was taken from the alarm UI (in view source) and with all the parameters (severity and category) closes #16861 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-cloudwatch-actions/README.md | 15 +++- .../aws-cloudwatch-actions/lib/index.ts | 1 + .../aws-cloudwatch-actions/lib/ssm.ts | 79 ++++++++++++++++++ .../aws-cloudwatch-actions/test/ssm.test.ts | 82 +++++++++++++++++++ 4 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 packages/@aws-cdk/aws-cloudwatch-actions/lib/ssm.ts create mode 100644 packages/@aws-cdk/aws-cloudwatch-actions/test/ssm.test.ts diff --git a/packages/@aws-cdk/aws-cloudwatch-actions/README.md b/packages/@aws-cdk/aws-cloudwatch-actions/README.md index 99eb74e06eb54..7788dc363a5ec 100644 --- a/packages/@aws-cdk/aws-cloudwatch-actions/README.md +++ b/packages/@aws-cdk/aws-cloudwatch-actions/README.md @@ -11,7 +11,7 @@ This library contains a set of classes which can be used as CloudWatch Alarm actions. -The currently implemented actions are: EC2 Actions, SNS Actions, Autoscaling Actions and Aplication Autoscaling Actions +The currently implemented actions are: EC2 Actions, SNS Actions, SSM OpsCenter Actions, Autoscaling Actions and Application Autoscaling Actions ## EC2 Action Example @@ -25,4 +25,17 @@ alarm.addAlarmAction( ); ``` +## SSM OpsCenter Action Example + +```ts +declare const alarm: cloudwatch.Alarm; +// Create an OpsItem with specific severity and category when alarm triggers +alarm.addAlarmAction( + new actions.SsmAction( + actions.OpsItemSeverity.CRITICAL, + actions.OpsItemCategory.PERFORMANCE // category is optional + ) +); +``` + See `@aws-cdk/aws-cloudwatch` for more information. diff --git a/packages/@aws-cdk/aws-cloudwatch-actions/lib/index.ts b/packages/@aws-cdk/aws-cloudwatch-actions/lib/index.ts index 5a384eba01247..191da008b23a9 100644 --- a/packages/@aws-cdk/aws-cloudwatch-actions/lib/index.ts +++ b/packages/@aws-cdk/aws-cloudwatch-actions/lib/index.ts @@ -2,3 +2,4 @@ export * from './appscaling'; export * from './autoscaling'; export * from './sns'; export * from './ec2'; +export * from './ssm'; diff --git a/packages/@aws-cdk/aws-cloudwatch-actions/lib/ssm.ts b/packages/@aws-cdk/aws-cloudwatch-actions/lib/ssm.ts new file mode 100644 index 0000000000000..0d26c4258c0d5 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudwatch-actions/lib/ssm.ts @@ -0,0 +1,79 @@ +import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; +import { Stack } from '@aws-cdk/core'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + +/** + * Types of OpsItem severity available + */ +export enum OpsItemSeverity { + /** + * Set the severity to critical + */ + CRITICAL = '1', + /** + * Set the severity to high + */ + HIGH = '2', + /** + * Set the severity to medium + */ + MEDIUM = '3', + /** + * Set the severity to low + */ + LOW = '4' +} + +/** + * Types of OpsItem category available + */ +export enum OpsItemCategory { + /** + * Set the category to availability + */ + AVAILABILITY = 'Availability', + /** + * Set the category to cost + */ + COST = 'Cost', + /** + * Set the category to performance + */ + PERFORMANCE = 'Performance', + /** + * Set the category to recovery + */ + RECOVERY = 'Recovery', + /** + * Set the category to security + */ + SECURITY = 'Security' +} + +/** + * Use an SSM OpsItem action as an Alarm action + */ +export class SsmAction implements cloudwatch.IAlarmAction { + private severity: OpsItemSeverity; + private category?: OpsItemCategory; + + constructor(severity: OpsItemSeverity, category?: OpsItemCategory) { + this.severity = severity; + this.category = category; + } + + /** + * Returns an alarm action configuration to use an SSM OpsItem action as an alarm action + */ + bind(_scope: Construct, _alarm: cloudwatch.IAlarm): cloudwatch.AlarmActionConfig { + if (this.category === undefined) { + return { alarmActionArn: `arn:aws:ssm:${Stack.of(_scope).region}:${Stack.of(_scope).account}:opsitem:${this.severity}` }; + } else { + return { alarmActionArn: `arn:aws:ssm:${Stack.of(_scope).region}:${Stack.of(_scope).account}:opsitem:${this.severity}#CATEGORY=${this.category}` }; + } + } +} + diff --git a/packages/@aws-cdk/aws-cloudwatch-actions/test/ssm.test.ts b/packages/@aws-cdk/aws-cloudwatch-actions/test/ssm.test.ts new file mode 100644 index 0000000000000..a4bb582005b22 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudwatch-actions/test/ssm.test.ts @@ -0,0 +1,82 @@ +import { Template } from '@aws-cdk/assertions'; +import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; +import { Stack } from '@aws-cdk/core'; +import * as actions from '../lib'; + +test('can use ssm with critical severity and performance category as alarm action', () => { + // GIVEN + const stack = new Stack(); + const alarm = new cloudwatch.Alarm(stack, 'Alarm', { + metric: new cloudwatch.Metric({ + namespace: 'AWS', + metricName: 'Test', + }), + evaluationPeriods: 3, + threshold: 100, + }); + + // WHEN + alarm.addAlarmAction(new actions.SsmAction(actions.OpsItemSeverity.CRITICAL, actions.OpsItemCategory.PERFORMANCE)); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { + AlarmActions: [ + { + 'Fn::Join': [ + '', + [ + 'arn:aws:ssm:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':opsitem:1#CATEGORY=Performance', + ], + ], + }, + ], + }); +}); + + +test('can use ssm with meduim severity and no category as alarm action', () => { + // GIVEN + const stack = new Stack(); + const alarm = new cloudwatch.Alarm(stack, 'Alarm', { + metric: new cloudwatch.Metric({ + namespace: 'AWS', + metricName: 'Test', + }), + evaluationPeriods: 3, + threshold: 100, + }); + + // WHEN + alarm.addAlarmAction(new actions.SsmAction(actions.OpsItemSeverity.MEDIUM)); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', { + AlarmActions: [ + { + 'Fn::Join': [ + '', + [ + 'arn:aws:ssm:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':opsitem:3', + ], + ], + }, + ], + }); +}); +