SendGridでメールを受信してAWSのAPIGateway/Lambdaに渡す

# はじめに

最近SendGridを使い始めました。

任意のアドレスでSendGridがメールを受信して、外部のWebサービスを呼び出すという Inbound Parse Webhookという機能から、Amazon APIGateway/Lambdaを呼び出すというところを試してみます。

# 前提

* SendGridのアカウントがある
* AWSのアカウントがある
* Amazon Route 53 で管理しているHosted Zoneがある（注）

（注）この記事の前提というだけで、Route53じゃなければWebhookが使えないという意味ではありません。

# ゴール

Route53で"example.com"というHostedZoneを管理している状況で、
_monamu＠sub1.sub2.example.com_ という宛先にメールを送信するとLambda Functionが呼び出される。

# AWSの準備

## Lambda Functionの作成

以下の内容のLambda Functionを作成します。

* FunctionName : 任意
* Runtime : Node.js 4.3
* Role : Policyに"AWSLambdaBasicExecutionRole"が含まれたRole（結果をCloudWatch Logsで確認します）
* Code : 以下の通り

``` javascript
exports.handler = (event, context, callback) => {
    console.log(JSON.stringify(event));
    console.log(event.body);
    callback(null, {
        statusCode: 200,
        body: JSON.stringify({message:'OK'})
    });
};
```

## APIGatewayでREST APIの作成

以下の内容でREST APIを作成します。

* API name : 任意

![image](https://qiita-image-store.s3.amazonaws.com/0/117772/1027f7fc-e6d4-d5b8-00c6-03bd7e8bd4b6.png)

作成したら、デプロイします。ここではStage Nameを"v1"としました。

![image](https://qiita-image-store.s3.amazonaws.com/0/117772/0e88de10-2d2e-a0ed-ee80-3204c1676dd4.png)

__Invoke URL__ を後ほど使用します。

# SendGridとRoute53を設定する

SendGridがRoute53で管理されたドメインのメールを受信して、REST APIを呼び出すためには、

* SendGridでDomain Whitelabelの設定
* Route53でexample.comへのメールはSendGridに行くようにレコード登録
* SendGridで、example.comドメインのメールを受けたらREST APIを呼び出すように設定

という作業が必要になります。

## SendGridでDomain Whitelabelを設定

詳しい手順　→　[ユーザマニュアル](https://sendgrid.kke.co.jp/docs/User_Manual_JP/Settings/Whitelabel/domains.html)

1. SendGridのDashboardにログインし、左のメニューから`Settings / Whitelabels / Domains `を選択し、右側の`Add Whitelabel`をクリックします。
1. 以下のように入力して`Save`します。
![image](https://qiita-image-store.s3.amazonaws.com/0/117772/579b58a6-fab7-27e3-eca0-465d8912b370.png)
ここで、Subdomainに"em"と設定していますが、これは受信するメールアドレスとは関係なく"em"と設定してください。

> Domain Whitelabelを設定するドメインの前に、サブドメインを付けます。他で使用していない好きな名前を付けられますが、とくに希望がなければ「em」や「em1」といったサブドメイン名を推奨します。
> [ユーザマニュアル](https://sendgrid.kke.co.jp/docs/User_Manual_JP/Settings/Whitelabel/domains.html) より

Whitelabelの登録が完了すると、以下のような表示になります。

![image](https://qiita-image-store.s3.amazonaws.com/0/117772/5767d945-3e22-2d3b-c9d2-d2163620f23e.png)

”VALID”の列がすべて:x:になっています。
下に表示されている３つのCNAMEレコードをRoute53に登録すると、このステータスが変わります。

### Route53にレコード追加

SendGridで指示された３行を追加します。TypeをCNAMEで作ることを忘れずに。

![image](https://qiita-image-store.s3.amazonaws.com/0/117772/585840f7-1c4b-9296-7e64-244815bf6d85.png)

### SendGridで確認

少し時間をおいてから、SendGridのWhitelabelのページの`Validate Recorde`をクリックすると、VALIDの列が:heavy_check_mark:に変わります。

これでWhitelabelの設定は完了です。

## Route53でexample.comへのメールはSendGridに行くようにレコード登録

詳しい手順　→　[Documentation(英語)](https://sendgrid.com/docs/Classroom/Basics/Inbound_Parse_Webhook/setting_up_the_inbound_parse_webhook.html) の(3)

Route53で以下のレコードを追加します。

* Type : MX
* Name : sub1.sub2.example.com. (入力は"sub1.sub2")
* Value : 10 mx.sendgrid.net. (10の後ろのスペースと末尾の . を忘れずに)

これで、monamu＠sub1.sub2.example.comへのメールはSendGridへ行くようになりました。

## SendGridで、example.comドメインのメールを受けたらREST APIを呼び出すように設定

詳しい手順　→　[Documentation(英語)](https://sendgrid.com/docs/Classroom/Basics/Inbound_Parse_Webhook/setting_up_the_inbound_parse_webhook.html) 

1. SendGridのDashboardの左から`Settings / Inbound Parse `を選択し、右側の`Add HOST &
 URL`をクリックします。
1. 以下のように入力して`Add`をクリックします。
![image](https://qiita-image-store.s3.amazonaws.com/0/117772/77cb3d99-e93a-577d-b2f7-41841c6653f5.png)
 * Subdomain : sub1.sub2
 * Domain : example.com (リストから選択）
 * Destination URL : API Gatewayで作成したREST APIのInvoke URL

以上ですべての設定が完了です。

# 確認

メールを送信してみます。

![image](https://qiita-image-store.s3.amazonaws.com/0/117772/dc3a3c03-9e9e-c49f-c79c-eda179f5ac73.png)

SendGridのDashboardからActivityを表示すると、以下のように受信してParseが実行されたことが記録されています。

![image](https://qiita-image-store.s3.amazonaws.com/0/117772/fac6db30-6867-9d0a-7245-bbd17dccfbf9.png)

続いて、CloudWatchLogで、LambdaFunctionのログを確認します。

![image](https://qiita-image-store.s3.amazonaws.com/0/117772/e459b041-2cae-0c02-12ff-82f18417428f.png)

メールの内容が取得できていました。あとは取得した内容に応じて、Lambdaでいろいろ処理を実装していけそうです。

## Lambdaがエラーを返したらどうなる？

SendGridはエラーを受信すると、自動的にリトライするそうです。

> 有効な 2xx HTTP レスポンスを 受け取らなかった 場合、SendGridのサーバは、メッセージの送信に失敗したと考え、送信をし続けます。3日経っても送信できなかったメッセージはドロップされます
> [APIリファレンス](https://sendgrid.kke.co.jp/docs/API_Reference/Webhooks/parse.html)

# まとめ

メール受信をトリガーにしたシステムのベースが、30分くらいでできてしまいました。
この後は、エラーハンドリングやREST APIの保護の方法について検討したいと思います。

では！

# 参考
* [API リファレンス > Webhooks > Inbound Parse Webhook](https://sendgrid.kke.co.jp/docs/API_Reference/Webhooks/parse.html)
* [ユーザマニュアル > Settings > Inbound Parse](https://sendgrid.kke.co.jp/docs/User_Manual_JP/Settings/parse.html)