前回の続き。
LambdaからSESによるメール送信を行う方法として、前回はIAMを作成し、アクセスキーを使ったが、今回はLambdaにロールを付与してみる。
環境
前回と同様。
実装
以下のissueに記載の通り、Lambda関数を作成し、生成されたCloudFormation定義を編集して、ロールを付与できる。
SESのドメインの検証は省略。
Lambdaの作成
amplify add function
で、メール送信用のLambdaを作成する。関数名や言語は前回と同じものを指定。
記事作成時点で、コマンド実行時のオプション等で任意のロール付与は行えないため、advanced settingsでは何も設定しない。
また、前回は Select the categories you want this function to have access to
でAPIを選択したが、不要だったため除外している。
amplify add function ? Select which capability you want to add: Lambda function (serverless function) ? Provide an AWS Lambda function name: sendMail ? Choose the runtime that you want to use: NodeJS ? Choose the function template that you want to use: Hello World Available advanced settings: - Resource access permissions - Scheduled recurring invocation - Lambda layers configuration - Environment variables configuration - Secret values configuration ? Do you want to configure advanced settings? No ? Do you want to edit the local lambda function now? No Successfully added resource sendMail locally.
Lambdaへのロール付与
生成された amplify/backend/function/<関数名>/<関数名>-cloudformation-template.json
がCloudFormation定義ファイル。
Resources > lambdaexecutionpolicy > Properties > PolicyDocument > Statement
に追加すればいい。
今回の作成方法の場合、 amplify/backend/function/sendMail/sendMail-cloudformation-template.json
が対象。
生成段階で、ログ用にリソース指定された定義が存在するため、それとは別に "Resource": "*"
で追加する。
// 前略 "Statement": [ // ここから追加 { "Effect": "Allow", "Action": [ "ses:SendEmail", "ses:SendRawEmail" ], "Resource": "*" }, // ここまで追加 // 以下、もともと存在していたログ用設定 { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": { "Fn::Sub": [ "arn:aws:logs:${region}:${account}:log-group:/aws/lambda/${lambda}:log-stream:*", { "region": { "Ref": "AWS::Region" }, "account": { "Ref": "AWS::AccountId" }, "lambda": { "Ref": "LambdaFunction" } } ] } } ] // 後略
Lambdaの実装
今回はSystems Managerが不要なので、SESのパッケージだけインストールする。
cd amplify/backend/function/sendMail/src yarn add @aws-sdk/client-ses
Lambdaの内容も前回と変わらず、 amplify/backend/function/<関数名>/src/index.js
だが、実装はシークレットが不要になったのでシンプルになった。
// ファイル先頭の Amplify Params コメントは省略 const { SESClient, SendEmailCommand } = require('@aws-sdk/client-ses') /** * @type {import('@types/aws-lambda').APIGatewayProxyHandler} */ exports.handler = async event => { console.log(JSON.stringify(event)) const sesClient = new SESClient({ region: 'ap-northeast-1', }) const params = event.arguments const sendEmailCommandInput = { Source: params.from, Destination: { ToAddresses: params.to, CcAddresses: params.cc, BccAddresses: params.bcc, }, Message: { Subject: { Data: params.subject }, Body: { Text: { Data: params.body, }, }, }, } const sendEmailCommand = new SendEmailCommand(sendEmailCommandInput) const sendEmailCommandOutput = await sesClient.send(sendEmailCommand) return { statusCode: 200, body: sendEmailCommandOutput.MessageId, } }
自分の設定が悪いのか、この方法だと amplify mock function <関数名>
でのテストが失敗する。
amplify function push
でAWS側に反映し、AWS ConsoleのLambdaのテストから実行できることを確認。
GraphQL定義およびコードからの呼び出し
前回と同様のため省略。
振り返り
Amplify CLIから設定できないAWSのサービスも、今回の方法を応用すれば、Lambda経由でどうとでもなりそう。
一方で、Amplifyの仕組みの外となるので、使いすぎるのは管理対象が増えるなど、デメリットにもなりそう。使いどころは考える必要があると感じた。