S3+Cognito+Lambda+SESでのフォーム作成
WEBページをS3+CloudFrontに移したので問い合わせフォームが使えなくなり、せっかくなのでAWSの機能を組み合わせてみようと思ったところ
先人の方が作っていたようなので、AWSのドキュメントと突き合わせつつ作成
*ついでにIE5の時代で止まっていたJavaScriptの勉強しなおしも兼ねて…
regionはSESのみus-east、後のサービスは東京に統一して作成。
以下、参考先から弄ったところを列挙
Lambdaの設定
設定
Lambda側ではinquiry_ + .txtのものがPutされた場合に関数が走るよう設定を行う
ポリシー
Lambda上で関数を作成する際のポリシー作成でSES権限を付与してもses.SendBounceの権限で作成される為、ses.SendEmailで送ろうとしている場合、当然許可はされず送信は出来ない
そのため、ポリシー選択でAmazonSESの権限は追加せず、S3の権限のみを選択し、関数を作成
その後、ses.sendEmailの権限を付与するカスタムポリシーを作成し、IAMロールに追加する
SES
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1505896993000",
"Effect": "Allow",
"Action": [
"ses:SendEmail"
],
"Resource": [
"*"
]
}
]
}
フォームのJS
index.js
'use strict';
const $id = function (id) { return document.getElementById(id); };
AWS.config.region = 'ap-northeast-1'; // リージョン
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'ap-northeast-1:xxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
});
AWS.config.credentials.get(function (err) {
if (err) {
return alert('初期化に失敗しました。')
}
});
const upload = function () {
AWS.config.region = "ap-northeast-1";
const s3BucketName = "inquiry.example.com";
const date = new Date();
const mail_param = {
"name": $id("name").value
, "kana": $id("kana").value
, "company": $id("company").value
, "tel": $id("tel").value
, "mail": $id("mail").value
, "date": date.toLocaleString("ja")
};
const s3 = new AWS.S3({ params: { Bucket: s3BucketName } });
const blob = new Blob([JSON.stringify(mail_param)], { type: 'text/plain' });
s3.putObject(
{
Key: `inquiry_${date.getTime()}.txt`
, ContentType: "text/plain"
, Body: blob
, ACL: "private"
},
function (err, data) {
if (err) {
console.log(err, err.stack);
return alert(err.message);
} else {
location.assign("./thanks.html");
}
}
);
};
LambdaのJS
mail.js
'use strict';
console.log('Loading function');
const aws = require('aws-sdk');
const s3 = new aws.S3({ apiVersion: '2006-03-01' });
const ses = new aws.SES({ apiVersion: '2010-12-01', region: 'us-east-1' });
exports.handler = (event, context, callback) => {
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 {
const json = JSON.parse(data.Body.toString());
console.log(`title:${json.title}`);
const message = `
名前: ${json.name}(${json.kana})
会社: ${json.company}
TEL: ${json.tel}
MAIL: ${json.mail}
問合せ日時:
${json.date}
`
const params = {
Destination: {
ToAddresses: [process.env.TOADDRESS]
},
Message: {
Body: {
Text: {
Charset: "UTF-8",
Data: message
}
},
Subject: {
Charset: "UTF-8",
Data: "フォームからのお問い合わせ"
}
},
Source: process.env.SOURCEADDRESS
};
ses.sendEmail(params, function (err, data) {
if (err) console.log(err, err.stack);
else console.log(data);
});
}
});
};
引っかかったところ
S3への書き込み権限エラー
S3にアップロードする際に、権限のエラーでアップロードが出来ない
ARNはバケットのプロパティからコピーした筈だから…とハマる
エラー時のポリシー
policy.before
"Resource": [
"arn:aws:s3:::inquiry.example.com"
]
ARNをS3のバケットに対してのものと勘違いし、バケットのARNをそのままコピペした所
ファイルに対しての権限が無いため、アップロードがエラーに…
修正したポリシー
policy.after
"Resource": [
"arn:aws:s3:::inquiry.example.com/*"
]