LoginSignup
9
3

More than 5 years have passed since last update.

CloudFormationテンプレート(JSON) - SNS トリガで Lambda を呼び出して Slack へ

Last updated at Posted at 2018-01-16

#1. 概要

  • CloudWatch Alarm の通知を Slack へ送るための設定を CloudFormation 一発で。
  • Lambdaの完成形はこんな感じ

    スクリーンショット 2018-01-16 20.47.58.png
  • 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. 参考

9
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
3