diff --git a/cdk/backend/function/slackAlarmForwarder/index.py b/cdk/backend/function/slackAlarmForwarder/index.py new file mode 100644 index 00000000..3f7a7781 --- /dev/null +++ b/cdk/backend/function/slackAlarmForwarder/index.py @@ -0,0 +1,44 @@ +import json +import boto3 +import json +import requests + +session = boto3.session.Session() +client = session.client( + service_name='secretsmanager', + region_name="eu-central-1" + ) + +def handler(event, context): + attachments = [] + for record in event.get('Records', []): + msg = record.get('Sns', {}).get('Message', "") + if msg: + attachments.append(create_payload(json.loads(msg))) + + requests.post( + url=get_slack_webhook_url(), + json=dict(attachments=attachments) + ) + + return dict(status_code=200, body="") + +def create_payload(msg): + colors = dict(OK='good', INSUFFICIENT_DATA='warning', ALARM='danger') + + return { + 'mrkdwn_in': ['text'], + 'title': f"{msg['Region']} -- {msg['AlarmName']}", + 'title_link': f"https://{msg['AWSAccountId']}.signin.aws.amazon.com/console/cloudwatch", + 'text': f"Alarm `{msg['AlarmName']}` is in state `{msg['NewStateValue']}`\n\n{msg['NewStateReason']}", + 'color': colors.get(msg['NewStateValue'], '#bfbfbf'), + } + +def get_slack_webhook_url(): + get_secret_value_response = client.get_secret_value( + SecretId="slack_webhook_url" + ) + response = get_secret_value_response['SecretString'] + slack_webhook_url = json.loads(response)['url'] + + return slack_webhook_url \ No newline at end of file diff --git a/cdk/backend/function/slackAlarmForwarder/requirements.txt b/cdk/backend/function/slackAlarmForwarder/requirements.txt new file mode 100644 index 00000000..cead3141 --- /dev/null +++ b/cdk/backend/function/slackAlarmForwarder/requirements.txt @@ -0,0 +1 @@ +requests==2.28.0 \ No newline at end of file diff --git a/cdk/lib/kompetanse-stack.ts b/cdk/lib/kompetanse-stack.ts index 4059ea68..48b4be1a 100644 --- a/cdk/lib/kompetanse-stack.ts +++ b/cdk/lib/kompetanse-stack.ts @@ -1,4 +1,4 @@ -import { aws_cloudwatch, CfnOutput, Duration, Stack, StackProps } from 'aws-cdk-lib'; +import { aws_cloudwatch, aws_secretsmanager, aws_sns_subscriptions, CfnOutput, Duration, Stack, StackProps } from 'aws-cdk-lib'; import * as cam from 'aws-cdk-lib/aws-certificatemanager'; import * as cognito from 'aws-cdk-lib/aws-cognito'; import * as lambda from 'aws-cdk-lib/aws-lambda'; @@ -481,6 +481,33 @@ export class KompetanseStack extends Stack { }); batchCreateUserAlarm.addAlarmAction(new SnsAction(systemAdminTopic)); + batchCreateUserAlarm.addOkAction(new SnsAction(systemAdminTopic)); + + // SlackAlarmForwarder setup + + const slackAlarmForwarderPermissions = new iam.PolicyStatement({ + actions: ["secretsmanager:GetSecretValue"], + resources: ["arn:aws:secretsmanager:eu-central-1:*:secret:slack_webhook_url-*"] + }); + + const slackAlarmForwarder = new python.PythonFunction(this, "slackAlarmForwarder", { + entry: path.join(__dirname, "/../backend/function/slackAlarmForwarder"), + runtime: lambda.Runtime.PYTHON_3_9, + initialPolicy: [slackAlarmForwarderPermissions], + timeout: Duration.seconds(10) + }); + + new aws_secretsmanager.Secret(this, "slack_webhook_url", { + secretName: "slack_webhook_url", + generateSecretString: { + secretStringTemplate: '{"url": "value must be set using AWS Console or CLI"}', + generateStringKey: "url" + } + }) + + systemAdminTopic.addSubscription( + new aws_sns_subscriptions.LambdaSubscription(slackAlarmForwarder) + ) // Admin API Setup