#1. 概要
- CloudWatch Alarm の通知を Slack へ送るための設定を CloudFormation 一発で。
- Lambdaの完成形はこんな感じ
- SNS:トリガ
- Amazon CloudWatch Logs:Lambda の実行ログをを CloudWatch Logs へ送るため
- Amazon S3:S3バケットにある Lambda 関数のファイルを get するため
- CloudFormationで作成するリソース
- IAM Role
- SNS Topic + Subscription
- Lambda Function + Lambda Permission
#2. 事前に行なうこと
- 通知を受けるSlack チャンネルの incoming-webhook URL を発行する
- Lambda 関数を S3 へ put する
#3. Lambda 関数
- SNS から受け取った CloudWatch Alarm の内容を Slack へ投げる Python3.6 コード
slack-syscrit.py
import os
import json
import logging
from datetime import datetime, timedelta
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError
Slack_Webhook_URL = os.environ['SLACK_WEBHOOK_URL']
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
logger.info("Event: " + str(event))
message = json.loads(event['Records'][0]['Sns']['Message'])
logger.info("Message: " + str(message))
NewStateValue = message['NewStateValue']
NewStateReason = message['NewStateReason']
AlarmName = message['AlarmName']
StateChangeTime = (datetime.strptime(message['StateChangeTime'][:-9], '%Y-%m-%dT%H:%M:%S') + timedelta(hours=9)).strftime("%Y/%m/%d %H: %M :%S")
SlackText = "[" + NewStateValue + "] " + AlarmName
SlackAttachments = message['AlarmDescription'] + "\n" + message['NewStateReason'] + "\n" + StateChangeTime
color = "danger"
icon = ":scream:"
if NewStateValue == "OK":
color = "good"
icon = ":wink:"
slack_message = {
'username': "AWS CloudWatch Alarm",
'text': SlackText,
'icon_emoji': icon,
'attachments': [
{
"color": color,
"text": SlackAttachments
}
]
}
req = Request(Slack_Webhook_URL, json.dumps(slack_message).encode('utf-8'))
try:
response = urlopen(req)
response.read()
logger.info("Message posted to %s", Slack_Webhook_URL)
except HTTPError as e:
logger.error("Request failed: %d %s", e.code, e.reason)
except URLError as e:
logger.error("Server connection failed: %s", e.reason)
- これをこのまま Zip で固めて、「slack-syscrit.py.zip」ファイルを S3 のバケットに put する
- 今回は、バケット「lambda-function-AWSアカウントID」配下に「slack-syscrit.py.zip」を置いた
#4. CloudFormation
SNS-Lambda-Slack.json
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "CloudWatch Alarm -> SNS -> Lambda -> Slack #syscrit",
"Resources": {
"IAMRole" : {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"ManagedPolicyArns": [
"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
"arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
],
"Path": "/",
"RoleName": "lambda-slack-syscrit"
}
},
"LambdaFunction" : {
"DependsOn" : [ "IAMRole" ],
"Type" : "AWS::Lambda::Function",
"Properties" : {
"Code" : {
"S3Bucket" : "lambda-function-AWSアカウントID",
"S3Key" : "slack-syscrit.py.zip"
},
"Environment" : { "Variables" : { "SLACK_WEBHOOK_URL": "通知を受けるSlack チャンネルの incoming-webhook URL" } },
"FunctionName" : "slack-syscrit",
"Handler" : "slack-syscrit.lambda_handler",
"Role" : { "Fn::GetAtt" : [ "IAMRole", "Arn" ] },
"Runtime" : "python3.6",
"Timeout" : 10
}
},
"SNSTopic": {
"DependsOn" : [ "LambdaFunction" ],
"Type" : "AWS::SNS::Topic",
"Properties" : {
"DisplayName" : "slack syscrit",
"TopicName" : "slack-syscrit",
"Subscription": [
{
"Endpoint" : { "Fn::GetAtt" : [ "LambdaFunction", "Arn" ] },
"Protocol": "lambda"
}
]
}
},
"LambdaPermission" : {
"Type" : "AWS::Lambda::Permission",
"Properties" : {
"Action" : "lambda:InvokeFunction",
"FunctionName" : { "Fn::GetAtt" : [ "LambdaFunction", "Arn" ] },
"Principal" : "sns.amazonaws.com",
"SourceArn" : { "Ref" : "SNSTopic" }
}
}
},
"Outputs" : {
"IAMRole" : {
"Description" : "IAM Role Name",
"Value" : { "Ref" : IAMRole }
},
"SNSTopic" : {
"Description" : "SNS Topic Name",
"Value" : { "Fn::GetAtt" : [ "SNSTopic", "TopicName" ] }
},
"LambdaFunction" : {
"Description" : "Lambda Function Name",
"Value" : { "Ref" : LambdaFunction }
}
}
}
- 2箇所を適宜変更
- "S3Bucket" : "lambda-function-AWSアカウントID"
- "Environment" : { "Variables" : { "SLACK_WEBHOOK_URL": "通知を受けるSlack チャンネルの incoming-webhook URL" } },
#5. 参考
- https://iangilham.com/2016/03/22/Sns-trigger-lambda-via-cloudformation.html
- SNS トリガの場合は、AWS::Lambda::Permission の プロパティに SourceAccount を入れてはいけない。