AWSのSESでサンクスメール配信のひな形作ってみた
諸般の事情でAWSのSESを使わないといけなくなっちゃって、検証がてら
いじっていて、結構ややこしかったので備忘録的に手順をまとめておきます。
全体の手順としては以下の様な感じ。SDK組み込んだプログラムからしか
SES送信用のAPIを叩けない様に設定しています。
1.SESの設定
2.SNSから呼び出すLambda作成
3.SNSの設定(BOUNCE対応)
4.DynamoDB作成(BOUNCE対応)
5.SESを使うLambdaの作成
6.Cognitoの設定
7.APIGateway設定
8.フロントプログラムの準備
9.SESのBOUNCE設定
東京リージョンで設定したほうが色々と楽です。
注意点としてはsandboxの解除申請を行うこと。これを忘れていると外部にメール
送信できません。
Lambdaを準備します。
・トピックを作成します。
・トピックを作成してからサブスクリプションを作成します。
・プロトコルにLambdaを選択してエンドポイントを選択します。
DynamoDBのテーブル作成方法は適当にググれば出てくるので
そちらをご参照ください。
・新しいIDプールの作成をクリックします
・IDプール名を入力し、[認証されていないID・・・]にチェックを入れます。
・確認画面が表示されるので[許可]をクリックします。
・サンプルコードの箇所で[IdentityPoolId]を確認し[ダッシュボードに移動]をクリックします。
・IAMメニューに移動しロールにて「Cognito_XXXXUnauth_Role」を探してポリシーをアタッチします。
・[APIGatewayInvokeFullAccess]をアタッチします。
*本来はセキュリティを考慮してAPIを限定して設定すべきです。
・CORS設定してやります。
・メソッドリクエストで認可の箇所で[AWS_IAM]を選択します。
・統合リクエストにて「マッピングテンプレート」を設定します。
・「application/JSON」を追加して以下のパラメータを入力、保存します。
・フォーマットチェック用にモデルを作成します。
・モデルのスキーマに以下の様に入力し、作成します。
・メソッドリクエストで「リクエストの検証」で[本文の検証]を選択し、「リクエスト本文」にて「コンテンツタイプ」で[application/JSON]を入力後に[モデル名]で作成したモデルを選択します。
・ダウンロードしたフォルダにフロント用のHTMLを作成します。
・Bouncesの箇所でSNSに設定したトピックを選択します。
*必要に応じてCompliantsとDeliveriesも設定します。
なかなか大変ですが、一度やれば何とか流れは押さえられます。APIGatewayからエラーだろうが何だろうが200を返してくるのでエラー時はきちんと500で返す設定をしたり
Cloudwatchにもメトリクス設定したり、BOUNCE時のメールの取り扱いなど細かい運用周りで決めないといけないことは非常に多そうですね。
https://encr.jp/blog/posts/20200228_morning/
https://www.yamamanx.com/amazon-sessimple-email-service-lambda/
https://d0.awsstatic.com/webinars/jp/pdf/services/20160106_aws_backbelt-ses-public.pdf
https://www.datastadium.co.jp/engineer/e-reports/4615
https://vitalify.jp/blog/2018/03/aws-ses-bouncemail.html
https://qiita.com/Yoshi_T/items/b12c74b6af09df05dff3
https://qiita.com/KeijiYONEDA/items/5e810d1f07392ab51d22
https://bbh.bz/2019/08/20/ses-bounce-setting-cloudwatch/
https://kiraba.jp/ses-bounce-notification-get-from-sns/
https://blog.hypermkt.jp/2020/2020-03-29-aws-ses-email-architecture/
https://www.letitride.jp/entry/2020/06/11/172513
https://blog.shibayu36.org/entry/2015/08/27/101815
http://blog-tech.fukurou-labo.co.jp/2018/12/03/aws/aws-ses-sns%e3%80%80%e3%83%a1%e3%83%bc%e3%83%ab%e3%82%a4%e3%83%99%e3%83%b3%e3%83%88%e3%81%ae%e9%80%9a%e7%9f%a5%e3%81%ab%e3%81%a4%e3%81%84%e3%81%a6%ef%bc%88%e5%be%8c%e7%b7%a8%ef%bc%89/
https://qiita.com/yukidaru/items/5c95a1cf70fcecd79754
https://dev.classmethod.jp/articles/lambda-to-ses/
https://www.blog.danishi.net/2019/07/20/post-1858/
https://h-piiice16.hatenablog.com/entry/2020/02/15/225856
https://qiita.com/kskinaba/items/c692cadbe57121546413
https://www.novelworks.jp/blog/aws/%E3%80%90aws%E3%80%91api-gateway-%EF%BC%8B-lambda-%EF%BC%8B-dynamodb%E3%81%A7%E3%82%B5%E3%83%BC%E3%83%90%E3%83%AC%E3%82%B9%E3%81%AA%E8%AA%8D%E8%A8%BCapi%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B9
https://liginc.co.jp/430297
https://dev.classmethod.jp/articles/lambdac-apigateway-params/
https://confrage.jp/amazon-api-gateway%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9%E3%81%A8%E3%82%AF%E3%82%A8%E3%83%AA%E6%96%87%E5%AD%97%E5%88%97%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%81%AE%E6%B8%A1%E3%81%97%E6%96%B9/
https://tech.mti.co.jp/entry/2017/04/21/180029
https://qiita.com/t20190127/items/f8163aa85c5953fe65ba
https://qiita.com/tamura_CD/items/ca8e531f74ea5b82a5b7
https://qiita.com/Kept1994/items/663b700ddb55cf59b4c9
http://sugilog.hatenablog.com/entry/2016/05/11/135332
http://sndstudy.hatenablog.jp/entry/2017/01/18/012712
いじっていて、結構ややこしかったので備忘録的に手順をまとめておきます。
全体の手順としては以下の様な感じ。SDK組み込んだプログラムからしか
SES送信用のAPIを叩けない様に設定しています。
1.SESの設定
2.SNSから呼び出すLambda作成
3.SNSの設定(BOUNCE対応)
4.DynamoDB作成(BOUNCE対応)
5.SESを使うLambdaの作成
6.Cognitoの設定
7.APIGateway設定
8.フロントプログラムの準備
9.SESのBOUNCE設定
◆作業手順(やや省略)
1.SES設定
先人が色々とまとめてくださっているので割愛、東京で使えるようになったので東京リージョンで設定したほうが色々と楽です。
注意点としてはsandboxの解除申請を行うこと。これを忘れていると外部にメール
送信できません。
2.SNSから呼び出すLambda作成
BOUNCE時にBOUNCEになったメールアドレスをDynamoDBに登録するLambdaを準備します。
import datetime import json import os import boto3 BOUNCE_TABLE = os.getenv('TABLE_NAME') dynamodb = boto3.resource('dynamodb') def lambda_handler(event, context): try: table = dynamodb.Table(BOUNCE_TABLE) msg = event['Records'][0]['Sns']['Message'] b = json.loads(msg) bounce_type = b['bounce']['bounceType'] bounce_mail = b['bounce']['bouncedRecipients'][0]['emailAddress'] if bounce_type == 'Permanent': createdate = datetime.datetime.now() r = table.put_item( Item = { 'email': bounce_mail, 'createdate':str(createdate) } ) return r except Exception as e: print(e) raise e
3.SNSの設定
・トピックを作成します。
・トピックを作成してからサブスクリプションを作成します。
・プロトコルにLambdaを選択してエンドポイントを選択します。
4.DynamoDB作成
[email]項目をキーとしたDynamoDBのテーブルを作成します。DynamoDBのテーブル作成方法は適当にググれば出てくるので
そちらをご参照ください。
5.SESを使うLambdaの作成
SESからメール送信するLambdaを作成します。import boto3 import json import os SRC_MAIL = os.getenv('FROM_ADDR') REGION = os.getenv('SES_REGION') BUCKET = os.getenv('BUCKET_NAME') BOUNCE_TABLE = os.getenv('TABLE_NAME') def exist_email(mail_addr): """ Send Mail Check """ try: result = 'notexist' dynamodb = boto3.resource('dynamodb') table = dynamodb.Table(BOUNCE_TABLE) em = table.get_item( Key={ 'email':mail_addr } ) if ('Item' in em): result = 'exist' return result except Exception as e: print(e) raise e def get_template(file_name): """ S3 get mailbody """ try: s3_cl = boto3.client('s3') response = s3_cl.get_object(Bucket=BUCKET, Key=file_name) template_data = response['Body'].read() return template_data except Exception as e: print(e) raise e def send_email(source, to, subject, body): """ SES send email """ try: client = boto3.client('ses', region_name=REGION) response = client.send_email( Source=source, Destination={ 'ToAddresses': [ to, ] }, Message={ 'Subject': { 'Data': subject, }, 'Body': { 'Text': { 'Data': body, }, } } ) return response except Exception as e: print(e) raise e def lambda_handler(event, context): try: data = event["body-json"] tomail = data['email'] fn = data['template'] +'.txt' subject = "Test Send Mail" template = get_template(fn) message = template.decode('utf-8') mc = exist_email(tomail) if mc == 'notexist': r = send_email(SRC_MAIL, tomail, subject, message) else: r = { 'statusCode': 500, 'body': json.dumps('Bounce Mail exist') } return r except Exception as e: print(e) raise e
6.Cognitoの設定
・IDプールを作成します。・新しいIDプールの作成をクリックします
・IDプール名を入力し、[認証されていないID・・・]にチェックを入れます。
・確認画面が表示されるので[許可]をクリックします。
・サンプルコードの箇所で[IdentityPoolId]を確認し[ダッシュボードに移動]をクリックします。
・IAMメニューに移動しロールにて「Cognito_XXXXUnauth_Role」を探してポリシーをアタッチします。
・[APIGatewayInvokeFullAccess]をアタッチします。
*本来はセキュリティを考慮してAPIを限定して設定すべきです。
7.APIGateway設定
通常通りAPIを作成してやります。今回はPOSTで作成しました。・CORS設定してやります。
・メソッドリクエストで認可の箇所で[AWS_IAM]を選択します。
・統合リクエストにて「マッピングテンプレート」を設定します。
・「application/JSON」を追加して以下のパラメータを入力、保存します。
{ "body-json" : $input.json('$') }
・フォーマットチェック用にモデルを作成します。
・モデルのスキーマに以下の様に入力し、作成します。
{ "title": "sendEmail", "type": "object", "properties": { "email": { "type": "string" }, "template": { "type": "string" } }, "required": ["email", "template"] }
・メソッドリクエストで「リクエストの検証」で[本文の検証]を選択し、「リクエスト本文」にて「コンテンツタイプ」で[application/JSON]を入力後に[モデル名]で作成したモデルを選択します。
8.フロントプログラムの準備
・APIGatewayでSDKを生成しダウンロードします。・ダウンロードしたフォルダにフロント用のHTMLを作成します。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title>API test</title> <!-- AWS SDK --> <script src="https://sdk.amazonaws.com/js/aws-sdk-2.7.19.min.js"></script> <!-- jQuery --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script> <!-- API用SDK --> <script type="text/javascript" src="lib/axios/dist/axios.standalone.js"></script> <script type="text/javascript" src="lib/CryptoJS/rollups/hmac-sha256.js"></script> <script type="text/javascript" src="lib/CryptoJS/rollups/sha256.js"></script> <script type="text/javascript" src="lib/CryptoJS/components/hmac.js"></script> <script type="text/javascript" src="lib/CryptoJS/components/enc-base64.js"></script> <script type="text/javascript" src="lib/url-template/url-template.js"></script> <script type="text/javascript" src="lib/apiGatewayCore/sigV4Client.js"></script> <script type="text/javascript" src="lib/apiGatewayCore/apiGatewayClient.js"></script> <script type="text/javascript" src="lib/apiGatewayCore/simpleHttpClient.js"></script> <script type="text/javascript" src="lib/apiGatewayCore/utils.js"></script> <script type="text/javascript" src="apigClient.js"></script> </head> <body> <!-- HTML --> <input type="text" name="mail" placeholder="メールアドレスを入力"/><br> <input type="text" name="templateNO" placeholder="メールテンプレート番号を入力"/><br> <button id="btn">ボタン</button> <!-- Other Script --> <script type="text/javascript"> var apigClient; $(function(){ //AWS Credentialの初期化 AWS.config.region = 'ap-northeast-1'; AWS.config.credentials = new AWS.CognitoIdentityCredentials({ IdentityPoolId: 'ap-northeast-1:XXXXXXX' //Identity Pool Idを貼り付ける(ARNではない。) }); AWS.config.credentials.get(function(err){ if(!err){ //API ClientにaccessKey,secretKey,sessionToken,regionを設定し、初期化します。 apigClient = apigClientFactory.newClient({ accessKey: AWS.config.credentials.accessKeyId, secretKey: AWS.config.credentials.secretAccessKey, sessionToken: AWS.config.credentials.sessionToken, region: 'ap-northeast-1'//大事 }); }else{ console.log(err); } }); }); $('#btn').click(function(){ var email = $('input[name="mail"]').val(); var template = $('input[name="templateNO"]').val(); apigClient.rootPost({},{"email":email, "template":template},{}) .then(function(result){ // Add success callback code here. console.log(result); }).catch( function(result){ // Add error callback code here. console.log(result); }); }); </script> </body> </html>
9.SESのBOUNCE設定
・SESの「Notifications」から[EditConfiguration]をクリックします。・Bouncesの箇所でSNSに設定したトピックを選択します。
*必要に応じてCompliantsとDeliveriesも設定します。
なかなか大変ですが、一度やれば何とか流れは押さえられます。APIGatewayからエラーだろうが何だろうが200を返してくるのでエラー時はきちんと500で返す設定をしたり
Cloudwatchにもメトリクス設定したり、BOUNCE時のメールの取り扱いなど細かい運用周りで決めないといけないことは非常に多そうですね。
◆参照サイト
・SES関連
https://developers.cyberagent.co.jp/blog/archives/24908/https://encr.jp/blog/posts/20200228_morning/
https://www.yamamanx.com/amazon-sessimple-email-service-lambda/
https://d0.awsstatic.com/webinars/jp/pdf/services/20160106_aws_backbelt-ses-public.pdf
・BOUNCE関連
https://junkbox.wicurio.com/index.php?SES%E3%81%A8SNS%E3%81%A8SQS%E3%82%92%E9%80%A3%E6%90%BA%E3%81%95%E3%81%9B%E3%80%81%E3%83%90%E3%82%A6%E3%83%B3%E3%82%B9%E3%83%A1%E3%83%BC%E3%83%AB%E5%87%A6%E7%90%86%E3%82%92%E8%A1%8C%E3%81%86https://www.datastadium.co.jp/engineer/e-reports/4615
https://vitalify.jp/blog/2018/03/aws-ses-bouncemail.html
https://qiita.com/Yoshi_T/items/b12c74b6af09df05dff3
https://qiita.com/KeijiYONEDA/items/5e810d1f07392ab51d22
https://bbh.bz/2019/08/20/ses-bounce-setting-cloudwatch/
https://kiraba.jp/ses-bounce-notification-get-from-sns/
https://blog.hypermkt.jp/2020/2020-03-29-aws-ses-email-architecture/
https://www.letitride.jp/entry/2020/06/11/172513
https://blog.shibayu36.org/entry/2015/08/27/101815
http://blog-tech.fukurou-labo.co.jp/2018/12/03/aws/aws-ses-sns%e3%80%80%e3%83%a1%e3%83%bc%e3%83%ab%e3%82%a4%e3%83%99%e3%83%b3%e3%83%88%e3%81%ae%e9%80%9a%e7%9f%a5%e3%81%ab%e3%81%a4%e3%81%84%e3%81%a6%ef%bc%88%e5%be%8c%e7%b7%a8%ef%bc%89/
・APIGateway+Lambda+SES
https://poriweb.hatenablog.com/entry/2018/12/14/073000https://qiita.com/yukidaru/items/5c95a1cf70fcecd79754
https://dev.classmethod.jp/articles/lambda-to-ses/
https://www.blog.danishi.net/2019/07/20/post-1858/
https://h-piiice16.hatenablog.com/entry/2020/02/15/225856
https://qiita.com/kskinaba/items/c692cadbe57121546413
・APIGateway認証
https://qiita.com/Fujimon_fn/items/5197e2e3db6de9a68401https://www.novelworks.jp/blog/aws/%E3%80%90aws%E3%80%91api-gateway-%EF%BC%8B-lambda-%EF%BC%8B-dynamodb%E3%81%A7%E3%82%B5%E3%83%BC%E3%83%90%E3%83%AC%E3%82%B9%E3%81%AA%E8%AA%8D%E8%A8%BCapi%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B9
・APIGatewayパラメータ取得関連
https://dev.classmethod.jp/articles/sugano-013-api-gateway/https://liginc.co.jp/430297
https://dev.classmethod.jp/articles/lambdac-apigateway-params/
https://confrage.jp/amazon-api-gateway%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9%E3%81%A8%E3%82%AF%E3%82%A8%E3%83%AA%E6%96%87%E5%AD%97%E5%88%97%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%81%AE%E6%B8%A1%E3%81%97%E6%96%B9/
https://tech.mti.co.jp/entry/2017/04/21/180029
https://qiita.com/t20190127/items/f8163aa85c5953fe65ba
https://qiita.com/tamura_CD/items/ca8e531f74ea5b82a5b7
https://qiita.com/Kept1994/items/663b700ddb55cf59b4c9
http://sugilog.hatenablog.com/entry/2016/05/11/135332
http://sndstudy.hatenablog.jp/entry/2017/01/18/012712
コメント