今回はCloudFormationでLambdaを触ってみるということで、Slackに通知を送信するLambda関数を構築していきたいと思います。
調べてみたところ、Lambdaを構築する場合はCloudFormationの AWS::Serverless
というマクロ機能を利用するみたいです。
AWS::Serverless変換
マクロを利用すると「AWS SAM 構文」なるものでLambdaを定義できるようになり、 aws cloudformation package
コマンド実行時に、通常のCloudFormationテンプレートに展開されます。
マクロ機能を利用するには、テンプレート内で下記のように宣言します。
Transform: AWS::Serverless-2016-10-31
SlackのWebhook URLを取得する
Slack通知を行うためのWebhookURLを取得します。
Slack での Incoming Webhook の利用
1. 「アプリを追加する」から Incoming Webhookを追加
2. 投稿したいチャンネルを選択し、インテグレーションの追加をクリック
3. Webhook URLをコピー
テンプレートの実装
ディレクトリ構成
- project-notify-slack.yml
- src/
- lambda_function.py
1. CloudFormationテンプレート実装
下記3つのリソースを作成します
- Lambda発火の起点となるトピック
- LambdaのDLQ用トピック
- Slackに通知を送信するLambda
AWS::Serverless::Function
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: Slack notification app
Parameters:
SlackWebhookUrl:
Type: String
SlackChannel:
Type: String
Email:
Type: String
Outputs:
SlackNotifySnsTopicArn:
Value: !Ref SlackNotifyTopic
Resources:
# Lambda発火の起点となるトピック
# https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-sns-topic.html
SlackNotifyTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: !Sub ${AWS::StackName}-topic-notify
# lambdaのDLQ用トピック
# https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-sns-topic.html
DlqTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: !Sub ${AWS::StackName}-topic-dlq
# https://docs.aws.amazon.com/sns/latest/api/API_Subscribe.html
Subscription:
- Endpoint: !Ref Email
Protocol: email
# Slackに通知を送信するLambda
# https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html
SlackNotifyFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub ${AWS::StackName}-function
CodeUri: ./src
# lambdaのエントリーポイント(ファイル名.関数名)
Handler: lambda_function.lambda_handler
Runtime: python3.8
MemorySize: 128
Timeout: 180
# 環境変数
Environment:
Variables:
SLACK_WEBHOOK_URL: !Ref SlackWebhookUrl
SLACK_CHANNEL: !Ref SlackChannel
# Lambdaの処理が規定回数以上失敗した場合の通知先
# https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-deadletterqueue.html
DeadLetterQueue:
Type: SNS
TargetArn: !Ref DlqTopic
# 関数実行の起点となるイベントソースを定義
# https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-eventsource.html
Events:
Rule:
Type: SNS
Properties:
Topic: !Ref SlackNotifyTopic
2. Lambda関数の実装
Slackに通知を送信する関数を実装します。(公式のコピペです)
#!/usr/bin/python3.8
import urllib3
import json
import os
http = urllib3.PoolManager()
def lambda_handler(event, context):
url = os.getenv("SLACK_WEBHOOK_URL")
channel = os.getenv("SLACK_CHANNEL")
print("url={}, channel={}".format(url, channel))
msg = {
"channel": channel,
"text": event['Records'][0]['Sns']['Message'],
"icon_emoji": ""
}
encoded_msg = json.dumps(msg).encode('utf-8')
resp = http.request('POST',url, body=encoded_msg)
print({
"message": event['Records'][0]['Sns']['Message'],
"status_code": resp.status,
"response": resp.data
})
デプロイ
aws cloudformation package
コマンドで、AWS SAM形式のテンプレート(マクロ)を展開します。
デプロイに必要な資材をs3にアップロードする必要があるので、適当なバケットを作ってオプションに渡します。
# lambdaのデプロイ資材をアップロードするバケット
S3_BUCKET="sample-bucket"
# デプロイ資材の格納パス
S3_PREFIX="path/to/package"
# lambda関数をパッケージ
# --output-template-file に指定したファイル名に展開後のテンプレートが保存されます。
aws cloudformation package \
--region "ap-northeast-1" \
--profile "default" \
--template-file "project-notify-slack.yml" \
--output-template-file "project-notify-slack-package.yml" \
--s3-bucket "${S3_BUCKET}" \
--s3-prefix "${S3_PREFIX}"
展開されたテンプレートをデプロイします。
# スタック名任意
STACK_NAME="sample-stack"
# 先ほど取得したWebhook URLを定義
SLACK_WEBHOOK_URL="https://hooks.slack.com/services/xxxxxxxxx/YYYYYYYYYYY/ZZZZZZZZZZZ"
# 通知を飛ばしたいチャンネル名を指定
SLACK_CHANNEL="#test-midorikawa"
# lambda処理失敗時に通知を飛ばすメールアドレスを指定
EMAIL="hogehoge@example.com"
# デプロイ
aws cloudformation deploy \
--region "ap-northeast-1" \
--profile "default" \
--stack-name "${STACK_NAME}" \
--template-file "project-notify-slack-package.yml" \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
SlackWebhookUrl="${SLACK_WEBHOOK_URL}" \
SlackChannel="${SLACK_CHANNEL}" \
Email="${EMAIL}"
動作確認
CLIからトピックにpublishしてSlackに通知が送信されるか確認します。
# TopicArnを確認
TOPIC_ARN=$(aws cloudformation describe-stacks --stack-name $STACK_NAME | jq '.Stacks[].Outputs[]')
# メッセージを送信
aws sns publish --topic-arn "${TOPIC_ARN}" --subject testSubject --message testMessage
参考