前回のエントリで、スタックイベントをメールで受信できるようになりました。
しかし、全てのイベントを受信するため、大量のメールが届きます。
完了時だけひょっこりメールを受け取りたい!!
最初は、メッセージのフィルタリング機能を使えばできると思ったのですが、メッセージ属性 に対してのフィルタリングであって メッセージ本文 ではできません。
そこで、間に AWS Lambda を挟んで、完了イベントのみ再通知する仕組みを検討します。
概要
大まかな流れは以下になります。

- ユーザーがスタックを作成
- CloudFormationがスタックイベントをLambdaに送信
- Lambdaでメッセージをチェック、スタックの完了メッセージならSNSメール送信
- ユーザーがメールを受信
トピックの作成
Lambda用とMail用の2つのトピックを作成します。
サービス [アプリケーション統合] - [Simple Notification Service] を選択して、Lambda用のトピックを作成します。
Lambda用
- トピック名:
cloudformation_filter - サブスクリプション
- ここでは作成しません。(Lambda作成時に作成される)
Mail用
- トピック名:
cloudformation - サブスクリプション
- プロトコル:
Email - エンドポイント:
送信したいメールアドレス
- プロトコル:
Mail用は前回の記事を参照してください。
AWS Lambdaの作成
次にLambdaを作成します。
サービス [コンピューティング] - [Lambda] を選択し、[関数の作成]ボタンをクリックします。
関数の作成

- 設計図を選択
- テキストボックスに
snsと入力してEnterキー、設計図をフィルタリング sns-messageを選択- [設定]ボタンをクリック
基本的な情報

- 名前:
filterMessage - ロール:
テンプレートから新しいロールを作成 - ロール名:
lambda_send_sns - ポリシーテンプレート:
SNN 発行ポリシー
SNSトリガー

- SNSトピック:
cloudformation_filter - トリガーの有効化:
ON
問題なければ、[関数の作成]ボタンをクリックします。
Designer
Lambdaの左側に SNS、右側にAmazon CloudWatchとAmazon SNSが表示されていることを確認します。

関数コード
以下のコードを入力します。
************は自身のAWSアカウントを指定してください。
var aws = require('aws-sdk'); var sns = new aws.SNS(); let result = {}; exports.handler = async (event, context) => { try { const mail = event.Records[0].Sns; const found = mail.Message.match(/^(?=[\s\S]*ResourceStatus='CREATE_COMPLETE')(?=[\s\S]*ResourceType='AWS::CloudFormation::Stack')/); if (found) { result = await sns.publish({ Subject: mail.Subject, Message: mail.Message, TopicArn: "arn:aws:sns:ap-northeast-1:************:cloudformation" }).promise(); } } catch (err) { console.log(err); return err; } return result; };
補足
- デフォルトのランタイムは
Node.js 8.10です。async, await, promiseを利用しています。 - 改行を含む文字列、メッセージ内に
ResourceStatus='CREATE_COMPLETE'かつResourceType='AWS::CloudFormation::Stack'を含む正規表現でチェックしています。 - メールの件名と本文は、受信したメールをそのまま設定しています。わかりやすい内容に変更してください。
Lambdaの保存
右上の[保存]ボタンをクリックします。
「保存されました。」と表示されれば正常です。

Lambdaのテスト
メールが受信できるか確認をします。
[テスト]ボタンをクリックして、テストイベントの設定をします。

- 新しいテストイベントの作成:
ON - イベントテンプレート:
SNS - イベント名:
myEvent
JSON文字列内の "Message" 値を変更します。
"Message": "ResourceStatus='CREATE_COMPLETE' ResourceType='AWS::CloudFormation::Stack'"
[作成]ボタンをクリック、テストイベントmyEventが選択されている状態で、再度[テスト]ボタンをクリックします。
メールが届けば成功です。これで準備が整いました。
CloudFormationスタックの作成
スタックの作成時にLambda用のトピックを指定します。

既存のAmazon SNSトピックでLambda用のcloudformation_filterを選択します。
スタック作成後、メールが1件だけ届いたでしょうか?
まとめ
AWS Lambdaを挟むことによって、メールをフィルタリングすることが出来ました。 今回はメール通知ですが、Slackに通知したりするのも面白そうです。
参考
- AWSドキュメント
- AWS Compute Blog