slack等チャットサービスが主流となりつつある今日この頃ですが、何かと使う機会が多いAPI Gateway + lambdaでメール通知が必要な案件がありましたので覚え書き。
ついでに、駆け出しRubyistなものでlambdaもrubyで書いてみました。
AWSでメールといえばSES(Simple Mail Service)ですね。今回はAPIにPOSTされたデータの内容をメール本文に入れて送信してみます。
1. lambdaからメール送信してみる
まずはlambda - sesだけでメールの送信テスト。
SESの設定
2019/3時点で日本リージョンにありませんでした。バージニア北部(us-east1)で使います。
Email Addressの登録
送信元となるメールアドレスを登録します。登録すると確認メールが飛ぶのでリンクをクリック。
ステータスがverifyになればOK。
なお、SESの初期状態はサンドボックスモードで稼働するため、送信先もこちらのEmail Addressに登録されたアドレスのみとなります。
今回の要件では送信先・送信元が同一なので実施しませんでしたが、制限を解除する場合はAWSサポートから申請してください。
lambdaの設定
まずはテストということで、lambdaのtestからメールを送信できるようにします。
Roleの作成
先にlambdaからsesをcallできるようにIAM Roleを作っちゃいます。
紐づけるポリシーは以下2つ。可読性のためにcloudwatch(ログ)周りとSES周りで分離しました。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
"arn:aws:logs:*:*:*"
]
}
]
}
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ses:SendBounce",
"ses:SendEmail",
"ses:SendRawEmail"
],
"Resource": "*"
}
]
}
lambda_use_ses_roleという名前でRoleを作成し、上記ポリシーを紐づけ。
関数の作成
関数を作成します。名前は適当にsendmail_ses、ランタイムはいざ、Ruby2.5に。
実行ロールには先ほど作成した lambda_use_ses_roleを選択。DesignerにcloudwatchとSESが追加されればOK。
require 'json'
require 'aws-sdk'
def lambda_handler(event:, context:)
to = "yourmail@example.com"
from = "yourmail@example.com"
subject = "Amazon SES test"
sesregion = "us-east-1"
encoding = "UTF-8"
begin
body = "test mail"
ses = Aws::SES::Client.new(region: sesregion)
resp = ses.send_email({
destination: {
to_addresses: [
to,
],
},
message: {
body: {
text: {
charset: encoding,
data: body,
},
},
subject: {
charset: encoding,
data: subject,
},
},
source: from,
})
return body
rescue Aws::SES::Errors::ServiceError => error
return "Email not sent. Error message: #{error}"
end
end
AWS公式ドキュメントを参考にしています。
テスト
lambdaからテストを作成、実行。(リクエストデータは関係ないので初期値で)
メールが届いていたら幸せ。順調ですね。
2. API化してみる
続いて上記lambda-SES構成をAPI化します。
API Gatewayの設定
APIの作成
プロトコルはREST、エンドポイントは外部からのアクセスを想定しているのでリージョンを選択。
リソースの設定
リソース -> アクションからメソッドの作成を選択して任意のものを作成。
今回はwebアクセスで確認したかったのでGETで。
テスト
早速テストしてみます。GETメソッドからテストをクリックするとリクエストデータを入力できます。
が、今回は関係ないので空でもOK。
メールが送信されたら幸せです。いい感じ。
リソースポリシーの設定
公開時にIP制限をかけたかったのでリソースポリシーに以下を追加
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:ap-northeast-1:AWSアカウントID:APIID/*",
"Condition": {
"IpAddress": {
"aws:SourceIp": "xxx.xxx.xxx.xxx"
}
}
}
]
}
APIIDは画面上API名の横に()でくくって書いてあります。許可するIPは任意のものを。
デプロイ
作成が完了したらAPIをデプロイ(公開)します。
アクションからAPIのデプロイ、ステージは新しいステージを選択し、任意のものを指定します。
完了するとステージが作成され、エンドポイントURLが表示されます。
テスト
早速ブラウザからエンドポイントにアクセス。(webアクセスではGETが実行されます。)
リソースポリシーで許可したIPから接続することを忘れずに!
テスト時の応答と同じく、
メールが送信されたら幸せです。ハッピー!
3. リクエストデータの値を取得してみる
APIが動作してメール送信まで確認出来たので、ちょっと実用的(というか必須ですよね)なことを。
jsonで来たリクエストをlambdaでごにょごにょしてメール本文に加えます。
API Gatewayの設定
こちら側では「マッピングテンプレート」という機能でリクエストをlambdaに渡す条件、内容の編集を行えます。
どこまでをマッピングテンプレートで処理するか、またはlambda側で処理するかは好みがわかれますが、今回はテストなのでjsonデータをハッシュ化してまるっと渡します。
マッピングテンプレートの設定
リソースからメソッドを選択し、統合リクエスト(日本語だと変な感じですね。Integrationだと思いますが。)を選択。
マッピングテンプレートで、リクエスト本文のパススルーは推奨設定を選択。(任意のものを選択してください。)
マッピングテンプレートの追加から、コンテンツタイプを入力します。jsonなのでapplication/jsonで。
ソースはこんな感じで全文取得。
これでlambda側で値を取得できるようになります。
lambdaの設定
続いてlambda側、とりあえずメール本文にそのまま値を突っ込みます。
require 'json'
require 'aws-sdk'
def lambda_handler(event:, context:)
to = "yourmail@example.com"
from = "yourmail@example.com"
subject = "Amazon SES test"
sesregion = "us-east-1"
encoding = "UTF-8"
begin
body = event["jsonbody"]["text"]
ses = Aws::SES::Client.new(region: sesregion)
resp = ses.send_email({
destination: {
to_addresses: [
to,
],
},
message: {
body: {
text: {
charset: encoding,
data: body,
},
},
subject: {
charset: encoding,
data: subject,
},
},
source: from,
})
return body
rescue Aws::SES::Errors::ServiceError => error
return "Email not sent. Error message: #{error}"
end
end
リクエストデータはlambda_handlerのeventに格納されています。context等使い方は公式ドキュメントまで。
bodyをちょこっと編集して、リクエストデータの'test'というキーの値を取得します。
テスト
API Gateway側のテストで、リクエストデータに'test'キーを入れてあげて実施。
メールが届けばすべて完了です。最高!
カスタムドメイン名を設定する
API Gatewayのカスタムドメイン名を使用して、独自ドメインでエンドポイントを提供します。ついでにSSL化も。
※既にRoute53で独自ドメインを管理している前提
証明書の取得
使用したいドメインの証明書を取得します。Certificate Managerから利用したいFQDNの証明書を取得してください。(ワイルドカード証明書でも可)
この時、APIがreginalの場合は同じリージョンで証明書を取得する必要があります。
カスタムドメイン名の登録
API Gateway カスタムドメイン名からFQDNの入力、先ほど取得した証明書を選択すると設定は完了です。(リージョナルを選択)
ベースパスマッピングにデプロイ済みのステージを登録するのを忘れずに。
レコード追加
最後に、Route53にてAレコードの登録を行います。
AレコードのAliasを選択し、上記ターゲットドメイン名を登録します。
以上で設定は完了。登録したカスタムドメイン名でアクセスができたら幸せ。
まとめ
今回は1から全て作りましたが、lambdaでAPIしてるなかで、ちょっとメールで送りたいなーという時に簡単に実装できるかと思います。
要件にもよりますが外部に公開したり本番運用する場合には、SESの制限にご注意ください。
P.S. AWSの日本語訳版、もうちょっと分かり易いと良いんですけどね。「エッジ最適化」とかなんのこっちゃって感じですし。これからに期待です。(^^)