Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

SESとLambdaでメールをSlackに通知してみよう

4,009 views

Published on

2016年3月21日にJAWS-UG福岡で発表した内容です。

Published in: Technology
  • Be the first to comment

SESとLambdaでメールをSlackに通知してみよう

  1. 1. SES と Lambda でメールを Slack に 通知してみよう      2016 年 3 月 21 日 木村健一郎
  2. 2. 名前:木村健一郎 所属:株式会社コム・アンド・コム お仕事:技術に関することはなんでも 好きな言語: perl 好きな DB : PostgreSQL
  3. 3. 今日のお題:   SES と Lambda でメールを Slack に通知してみよう
  4. 4. Slack 使ってますか? • チャットツールです • WEB ブラウザ、 iOS/Android/Windows/Mac アプリで使えます • API あります • 詳しくは http://slack.com/  へ
  5. 5. メール使ってますか? • なんだかんだで使わざるを得ない 重要なメールを携帯に飛ばすとかよくあるよね? • 監視システムからの通知もメールが基本 最近は API 叩けたり、 slack プラグインがあることも • できれば通知系は slack にまとめたいよね?
  6. 6. メールの内容を slack に飛ばそう! ぱっと思いつくレガシーなやり方 1.SMTP サーバを作る。例えば postfix 。 2.受信するメールアドレスを作って、そこ宛のメールをコマン ドに渡す 3.コマンドにはメールが標準入力経由で渡されるので、頑張っ てパースして slack に飛ばす hoge: “ | /path/to/command” /etc/aliases  にこんな感じで書く
  7. 7. ・・・超めんどくさい (´ ・ ω ・ `) ( 特にサーバのお守りなんてしたくない )
  8. 8. やっぱ時代はサーバレスでしょ! ( `・ ω ・ ´) ( 言ってみたかった )
  9. 9. 構成 インターネット SES メール S3 S3 に保存 イベント通知 メール読み込み API 呼び出し
  10. 10. 1. S3 を設定する 受信したメールを保存する S3 を設定します。 適当な名前でバケット作りましょう。 ( )S3※ オブジェクトは Lambda に渡すためにしか使わないから 、期限を設定して自動で消しましょう!
  11. 11. { "Version": "2008-10-17", "Statement": [ { "Sid": "GiveSESPermissionToWriteEmail", "Effect": "Allow", "Principal": { "Service": [ "ses.amazonaws.com" ] }, "Action": [ "s3:PutObjectAcl", "s3:PutObject" ], "Resource": "arn:aws:s3:::BUCKET_NAME/*", "Condition": { "StringEquals": { "aws:Referer": "ACCOUNT_NO" } } } ] } Policy はこんな感じで。
  12. 12. 2. SES を設定する 受信するドメインを決めます。 自分で持ってるドメインでサブドメイン作るなり、新しいド メイン取るなりでまず準備しましょう。 手順 1.メールアドレスを登録する 2.ドメインの verify をする (Route53 なら早いです ) 3.アクションを追加する S3 に保存するので、先ほどのバケット名を設定します
  13. 13. 今更ですが、なんで S3 ? なんで S3 経由?アクションに Lambda ってあるやん? S3 使わないと、本文や添付ファイルが取れないから (多分理由は、 Lanbda 呼び出すときに渡すデータがでかいと嫌ってことかと思います)
  14. 14. 3. Lambda ファンクション書くぜ! 今回は Python で書きます。理由は以下の通り。 1.メールを取り扱うライブラリが標準である 2.Slack 連携のライブラリがある 3.新しく使えるようになったから試してみたい 4.matetsu さんの記事を参考にしたから
  15. 15. 下準備 Python2.7 のインストールは適当に。 ワークディレクトリを作って、 slack 用のライブラリ をインストールします。 %mkdir ses-slack %cd ses-slack %pip install slackweb -t ./
  16. 16. コード書くぜ(1) lambda_function.py というファイルで作ります。 utf-8 で書きます。 # coding: utf-8 from __future__ import print_function import boto3 import json import ConfigParser import email from email.parser import FeedParser from email.header import decode_header import slackweb 出だしはこんな感じで。
  17. 17. コード書くぜ(2) ハンドラーの前半。 S3 からメールを取得。 def lambda_handler(event, context): try: record = event["Records"][0] bucket_region = record["awsRegion"] bucket_name = record["s3"]["bucket"]["name"] mail_object_key = record["s3"]["object"]["key"] s3 = boto3.client('s3', region_name=bucket_region) mail_object = s3.get_object(Bucket = bucket_name, Key = mail_object_key) mail_body = '' try: mail_body = mail_object["Body"].read().decode('utf-8') except: try: mail_body = mail_object["Body"].read().decode('iso-2022-jp') except: mail_body = mail_object["Body"].read()   msg_object = email.message_from_string(mail_body)
  18. 18. コード書くぜ(3) ハンドラーの中盤。本文、サブジェクト、送信元を取得します。 if msg_object.is_multipart(): body = msg_object.get_payload()[0] else: body = msg_object try: body = body.get_payload(decode=True).decode(body.get_content_charset()) except: #iso-2022-jp なのに丸文字があるとき code = 'iso-2022-jp-2004' body = body.get_payload(decode=True).replace('033$B', '033$(Q').decode(code) (d_sub, sub_charset) = decode_header(msg_object['Subject'])[0] if sub_charset == None: subject = d_sub else: subject = d_sub.decode(sub_charset) (d_from, from_charset) = decode_header(msg_object['From'])[0] if from_charset == None: mfrom = d_from else: mfrom = d_from.decode(from_charset)
  19. 19. コード書くぜ( 4 ) ハンドラーの後半。エラーハンドリングします。 except:   subject = u"Error!"   body = u" メールを受信しましたが、エラーが発生しました。 " mfrom = u" 送信元不明 " どうしてもおかしな形式のメールというのはあるものでして・・・
  20. 20. コード書くぜ( 5 ) ハンドラーの終盤。いよいよ slack に流します。 inifile = ConfigParser.SafeConfigParser() inifile.read("./config.ini") attachments = [] attachment = { "fallback": u"From:%snSub:%s" % (mfrom,subject), "pretext": u“From:%snSub:%s" % (mfrom,subject), "color": "#aaaaaa", "text": body } attachments.append(attachment) slack = slackweb.Slack(url=inifile.get('slack', 'hook_url')) slack.notify(attachments=attachments, channel=inifile.get('slack', 'channel'), username=inifile.get('slack', 'username'), icon_emoji=inifile.get('slack', 'icon_emoji')) return "CONTINUE"
  21. 21. コード書くぜ( 6 ) 設定ファイルを config.ini という名前で準備します。 [slack] hook_url = https://hooks.slack.com/services/*** username = alert_bot channel = 流す先のチャンネル名 icon_emoji = :guardsman: hook_url は slack の設定から取得します。 Web ブラウザでアクセスし、設定の「 Apps & Custom Integrations 」→「 Incoming WebHooks 」です。
  22. 22. アップロードするよ! zip でまとめます。 %zip -r ses-s3-lambda-slack.zip config.ini lambda_function.py slackweb で、これを管理コンソールからアップします
  23. 23. S3 のイベント設定するよ! 先ほどの S3 バケットに更新イベントを追加します。 Lambda の管理コンソールだけでなく、以下のように S3 の管理コンソール からも追加できます。
  24. 24. 先ほど設定したメールアドレスにメールを送ってみましょう。 動かないときは以下のようにちょっと修正してローカルで動かしてみる ( 引数にテ キストファイルとして保存したメールを渡す ) とデバッグしやすいです。 もしくは、 print すると CloudWatch のログに出力されるので print デバッグで。 import sys def lambda_handler(m): try: mail_object = email.message_from_string(m) …… return "CONTINUE" if __name__ == "__main__": if len(sys.argv)>1: raw=open(sys.argv[1]).read() else: print "no args" exit lambda_handler(raw) 5 .試験するよ!
  25. 25. 6 .応用するよ! ここまでできたら色々遊べますね。 •キーワードをハイライトしたり色を変える •本文に応じて通知するメールを取捨選択する •Slack から返信もできるようにしてみる •添付ファイルも見れるようにしてみる •Twilio と連携して SMS も送ってみる 色々遊んでみましょう!
  26. 26. 7 .感想 • Lambda 超便利 (^o^) • SES はウィルスメールフィルタなどもあって便利 • この組み合わせ、スロッティングとか考えなくていいし楽 ちん • もっと楽にデバッグできない? • Perl on Lambda マダー? たぶん永久に来ない? (´ ・ ω ・ `) • Qiita にまとめてますのでコードはこちらを参考に http://goo.gl/2m0dqK

×