S3にオブジェクトを作成したイベントをトリガーにしてSNS経由でLambda関数を呼び出す
S3にオブジェクトを作成した際にPUTやPOSTのイベントが発生する。
それをトリガーにしてSNS経由でLambda関数を呼び出し、作成されたS3オブジェクトを取得して何らかの処理をするための各種設定とLambda関数の作成を行った。
作業した環境
$ aws --version aws-cli/1.11.13 Python/2.7.10 Darwin/18.6.0 botocore/1.4.70
以下、設定の作業内容。
必要なリソースの作成
S3バケットを作る
$ aws s3 mb s3://nabewata07-event-test00 --region ap-northeast-1
make_bucket: nabewata07-event-test00
SNS Topicを作成する
$ aws sns create-topic --name event-test-topic01 --region ap-northeast-1 { "TopicArn": "arn:aws:sns:ap-northeast-1:xxxxxxxxxxxxx:event-test-topic01" }
SNSトピックをトリガーにしたLambda関数を作成する
イベントを発行したオブジェクトのContentTypeを表示する関数
console.log('Loading function'); const aws = require('aws-sdk'); const s3 = new aws.S3({ apiVersion: '2006-03-01' }); exports.handler = async (event, context) => { // Get the object from the event and show its content type const jsonStr = event.Records[0].Sns.Message; const obj = JSON.parse(jsonStr) const target = obj.Records[0].s3; const bucket = target.bucket.name; const key = decodeURIComponent(target.object.key.replace(/\+/g, ' ')); const params = { Bucket: bucket, Key: key, }; try { const { ContentType, Body } = await s3.getObject(params).promise(); console.log('CONTENT TYPE:', ContentType); // do something with content body //console.log('Body:', Body.toString()); return ContentType; } catch (err) { console.log(err); throw new Error(message); } };
作成したコードをzip圧縮する
デプロイのため。
$ zip index.js.zip index.js adding: index.js (deflated 47%)
Lambda関数を作成する
$ aws lambda create-function --function-name s3GetObjectSample --runtime nodejs10.x --role arn:aws:iam::xxxxxxxxxxxx:role/service-role/LambdaS3FuncRole --handler index.handler --zip-file fileb://index.js.zip --region ap-northeast-1 { "CodeSha256": "78l2lot/Qie5DQX2R7j1PMXLbiNivMToAaVbny7OQpQ=", "FunctionName": "s3GetObjectSample", "CodeSize": 635, "MemorySize": 128, "FunctionArn": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxx:function:s3GetObjectSample", "Version": "$LATEST", "Role": "arn:aws:iam::xxxxxxxxx:role/service-role/LambdaS3FuncRole", "Timeout": 3, "LastModified": "2019-07-28T23:34:32.759+0000", "Handler": "index.handler", "Runtime": "nodejs10.x", "Description": "" }
Lambda関数のポリシーに必要な権限
LambdaS3FuncRoleには以下のポリシー設定が必要。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject" ], "Resource": "arn:aws:s3:::nabewata07-event-test00/upload/sampledata/*" }, { "Effect": "Allow", "Action": "logs:CreateLogGroup", "Resource": "arn:aws:logs:ap-northeast-1:xxxxxxxxxxxxxx:*" }, { "Effect": "Allow", "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": [ "arn:aws:logs:ap-northeast-1:xxxxxxxxxxxxxx:log-group:/aws/lambda/s3GetObjectSample:*" ] } ] }
SNS TopicからLambda関数を呼び出す許可を設定する
Lambda関数のFunction policyで設定する。
$ aws lambda add-permission --function-name s3GetObjectSample --statement-id invoke-from-sns-topic00 --action lambda:InvokeFunction --principal sns.amazonaws.com --source-arn arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:event-test-topic01 { "Statement": "{\"Sid\":\"invoke-from-sns-topic00\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"sns.amazonaws.com\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:ap-northeast-1:325528992442:function:s3GetObjectSample\",\"Condition\":{\"ArnLike\":{\"AWS:SourceArn\":\"arn:aws:sns:ap-northeast-1:325528992442:event-test-topic01\"}}}" }
LambdaがSNSトピックをサブスクライブする設定を行う
--topic-arn
は先に作成したSNS TopicのARNを指定。
--notification-endpoint
には先に作成したLambda関数のARNを指定。
$ aws sns subscribe --protocol lambda \ --topic-arn arn:aws:sns:ap-northeast-1:325528992442:event-test-topic01 \ --notification-endpoint arn:aws:lambda:ap-northeast-1:325528992442:function:s3GetObjectSample \ --region ap-northeast-1 { "SubscriptionArn": "arn:aws:sns:ap-northeast-1:325528992442:event-test-topic01:af6d12be-b9ed-40f3-9041-49c59fa3360f" }
SNS TopicがS3から呼び出される許可をする
下記のStatementをSNSのアクセスポリシーに追加する。
{ "Sid": "Stmt1561879283049", "Effect": "Allow", "Principal": "*", "Action": "sns:Publish", "Resource": "arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:event-test-topic01", "Condition": { "ArnEquals": { "aws:SourceArn": "arn:aws:s3:::nabewata07-event-test00" } } }
これはCLIから実行する方法がわからなかったのでマネジメントコンソールのGUIから行った。
S3バケットにイベントの設定を行う
設定用のJSONファイルを作る
対象のバケットのupload/sampledata
というプレフィックスかつ.csv
というサフィックスが付いたオブジェクトがPUTまたはPOSTによって作成されたときに先ほど作成したSNSトピックに通知する設定にした。
{ "TopicConfigurations": [ { "Filter": { "Key": { "FilterRules": [ { "Name": "Prefix", "Value": "upload/sampledata" }, { "Name": "Suffix", "Value": ".csv" } ] } }, "Id": "event-test-topic01", "TopicArn": "arn:aws:sns:ap-northeast-1:xxxxxxxxxxxxx:event-test-topic01", "Events": [ "s3:ObjectCreated:Put", "s3:ObjectCreated:Post" ] } ] }
これをs3_put_notification.json
というファイル名で保存した。
S3バケットにイベントの設定を反映する
$ aws s3api put-bucket-notification-configuration --bucket nabewata07-event-test00 --notification-configuration file://s3_put_notification.json
Lambda関数を呼び出してみる
S3にオブジェクトをアップロードする
$ touch test.csv $ aws s3 cp ./test.csv s3://nabewata07-event-test00/upload/sampledata/ upload: ./test.csv to s3://nabewata07-event-test00/upload/sampledata/test.csv
CloudWatchLogsでログが確認できればOK
Lambdaが/aws/lambda/s3GetObjectSample
というロググループを作成するため、そこで確認した。
INFO CONTENT TYPE: text/csv
所感
それぞれのリソースを作ってからそれらの連携の設定と、連携を許可する権限の設定があり少しややこしい。
連携の設定はしたけど許可の設定を忘れていて実行できないなどが発生しないためにもここに書いてある各設定を忘れないようにしたい。