S3 + Cognito＋Lambda＋SNSでフォーム投稿内容をメール通知

## 概要
S3にホストしたHTMLファイルから問い合わせ内容を投稿するとメールを飛ばす。基本的には下記をなぞらせていただきつつ、メール送信をSNS経由に変更。

[S3 + Lambda + Cognitoを使って、簡単お問い合わせシステム構築](http://qiita.com/takuma_yoshida/items/3bd3af8b09307d25981a)

問い合わせ内容はS3にファイルとして保存、S3へのアップロード権限を付与するためにCognitoを利用。

S3のファイル保存をトリガーにLambdaを起動して、SNS経由でメール送信。

<img width="398" alt="構成.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/124221/a8939c2a-1a06-598b-0f23-f402518d1751.png">

<img width="393" alt="sequence.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/124221/4680219a-179a-fa4a-125f-a922ab351d7d.png">



## S3
- Bucketを作成
- 静的ウェブサイトホスティングを有効に
  + インデックスドキュメント：index.html
- アクセス許可 -> CORS設定の編集
  + ホストした静的ウェブサイト（別origin）からクロスドメインでPOSTできるようにするため

```
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>http://{静的ウェブサイトホスティングのエンドポイント}</AllowedOrigin>
        <AllowedMethod>PUT</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>
```

## Cognito
- Federated Identities -> Create new identity pool
- Enable access to unauthenticated identitiesをチェック
- じどう作成されるRole（今回はUnauthenticate roleを利用する）に、S3へのput権限をつける
  + IAMのRoleからインラインポリシー -> ロールポリシーの作成

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": [
                "arn:aws:s3:::{bucket名}/*"
            ]
        }
    ]
}
```

## S3に問い合わせフォーム本体をアップロード
- 問い合わせフォーム本体をアップロード、ファイルを公開にする
  + index.html
      - http://qiita.com/takuma_yoshida/items/3bd3af8b09307d25981a
      - こちらのhtmlをそのまま使用、regionに注意
  + aws-sdk.min.js
      -  https://github.com/aws/aws-sdk-js/tree/master/dist
      - こちらからダウンロードしたファイルを使用
  + 「ファイルを公開」しておかないと、403になるので注意

## SNS
- Create new topics -> Create Subscription
  + Protocol: Email
  + Endpoint: 自身のメールアドレス
- メール認証を済ませておく

## Lambda
- Roleの作成
  + サービスロール：AWS Lambda
  + ポリシーのアタッチ
      - AWSLambdaExecute
      - AmazonSNSFullAccess
      - s3の権限は、AWSLambdaExecuteに含まれているので不要
- Functionの作成
  + Node.js -> s3-get-object

<img width="910" alt="lambda.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/124221/b5e1c68c-4712-ae03-6d67-e6f2adc5d772.png">

  + Bucket: 上記で作成したBucket
  + EventType: Object Created
  + Prefix: uploads（投稿フォームhtml上のSDK呼び出し時に指定しているKey）
  + EnableTriggerをチェック
  + 上記で作成したRoleを指定
  + なおLambda環境変数対応が発表されて実際に画面に指定する箇所ができていたので試してみた。が、環境変数を指定するとなぜか"Please correct the errors below."とエラーになり諦めた。。

```js

'use strict';

console.log('Loading function');

const aws = require('aws-sdk');

const s3 = new aws.S3({ apiVersion: '2006-03-01' });

const sns = new aws.SNS({
   apiVersion: '2010-03-31',
   region: 'ap-northeast-1'
});

exports.handler = (event, context, callback) => {
    //console.log('Received event:', JSON.stringify(event, null, 2));

    // Get the object from the event and show its content type
    const bucket = event.Records[0].s3.bucket.name;
    const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));
    const params = {
        Bucket: bucket,
        Key: key
    };
    s3.getObject(params, (err, data) => {
        if (err) {
            console.log(err);
            const message = `Error getting object ${key} from bucket ${bucket}. Make sure they exist and your bucket is in the same region as this function.`;
            console.log(message);
            callback(message);
        } else {
            var json = JSON.parse(data.Body.toString());
            console.log('title:' + json.title);
            var message = '■日時\n' + json.date + '\n\n■件名\n' + json.title +
                '\n\n■メールアドレス\n' + json.mail + '\n\n■内容\n' + json.contents;
            sns.publish({
                Message: message,
                Subject: '問い合わせ['+ json.title + ']',
                TopicArn: "{SNSのARN}"
            }, function(err, data){
                if (err) {
                    context.fail('fail');
                } else {
                    callback(null, json.title);
                }
            });  
        }
    });
};
```

## 実行
- S3の静的ウェブサイトホスティングのエンドポイントにブラウザでアクセス
- 問い合わせ内容を適当に入力して「投稿」（なおメールアドレスは何にも使わないため適当でOK）

<img width="308" alt="入力.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/124221/4c203784-00be-515a-8f07-9bcf8498cf7b.png">

- こんな感じのメールが届くはず！
<img width="368" alt="mail.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/124221/458cb82b-5faa-4a19-a4a4-83ca5003eb69.png">

- S3のBucketのuploads以下に入力内容を格納したファイルが作成されている


## トラブルシューティング
- s3 bucketに対するOPTIONSのrequestで403 Forbiddenが返ってきている場合
    - CORSに設定したAllowedOriginが、request headerのoriginと一致しているかを確認（http://に注意）
- s3 bucketに対するPUTのrequestで403 Forbiddenが返ってきている場合
    - cognitoで作成されたUnauthenticate roleに対して、s3へのput権限が付与できているかを確認
- lambdaからsnsへのpublishがエラーになる
    - lambda内で正しいregionが指定できているかを確認

## 参考
- http://qiita.com/takuma_yoshida/items/3bd3af8b09307d25981a
- http://ktkooon.hatenablog.com/entry/2016/08/27/213000