はじめに
S3バケットにオブジェクトを配置・更新した時に、複数の別のアカウントのLambdaから当該オブジェクトを取得してゴニョゴニョしたい、といったことがありました。
一部、S3の制約に引っかかるなどして手戻りもあったので作業の記録として記事に残します。
本文
S3イベント通知
S3バケットのオブジェクト配置・更新をトリガーにLambdaなどをキックしたい場合、S3のイベント通知を使用することが有効です。
S3オブジェクトのイベントタイプを指定することで、オブジェクトが作成された時・削除された時など、必要に応じてイベントを指定することができます。
また、イベント通知のフィルターを活用することで、特定のファイルに絞り込むことが可能です。
大量のファイルを扱うバケットでも、特定のファイルの特定オブジェクトイベントのみLambdaをキックする、といったことが可能です。
制約
ただ、この通知設定において、フィルターのプレフィックスとサフィックスを指定している、かつ同じイベントタイプを設定しようとすると、エラーとなります。
例えば、「はじめに」に書いたように、特定オブジェクトの更新に対して、Lambdaを起動したいS3通知設定を作成します。
event: s3:ObjectCreated:*
filter prefix: images
filter suffix: jpg
通知先: Lambda Function A
そのうえで、以下のように設定を追加しようとすると、エラーとなります。
event: s3:ObjectCreated:*
filter prefix: images
filter suffix: jpg
通知先: Lambda Function A
event: s3:ObjectCreated:*
filter prefix: images
filter suffix: jpg
通知先: Lambda Function B
無効なプレフィックスやサフィックスの重複がある通知設定の例。を確認すると、
サフィックスが重複していなければ、プレフィックスが重複していても問題ありません。
とあります。ですのでprefixだけ指定していれば、eventが重複していても問題ないのかもしれません。
しかし、今回はsuffixまで厳密に指定したいため、別の方法で対応を考えます。
構成
上記を踏まえて、以下のような構成になります。
S3バケットのイベント通知先をSNSトピックにするところがミソになります。
S3の同じ条件のイベント通知を複数のLambdaに送信したい場合、SNSトピックを経由させます。SNSトピックのサブスクリプション先をLambda関数にすることで、複数のLambdaなどのサブスクリプション先にS3イベント通知を送信することができます。
Cfnテンプレート
今回使用したCfnテンプレートを載せておきます。
今回の構成を実現するにはバケットポリシーや、SNSトピックのポリシーなど、複数の権限管理が必要になります。
一部マスクしていますが、それらも網羅しています。
S3バケットを配置するアカウント
AWSTemplateFormatVersion: '2010-09-09'
Resources:
SnsTopic:
Type: 'AWS::SNS::Topic'
Properties:
TopicName: 'topic'
DisplayName: 'topic'
SnsTopicPolicy:
Type: 'AWS::SNS::TopicPolicy'
Properties:
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: 'Subscribe'
Effect: Allow
Principal:
AWS: !Ref Principals
Action: 'sns:Subscribe'
Resource: !Ref SnsTopic
- Sid: 'Publish'
Effect: Allow
Principal:
Service: 's3.amazonaws.com'
Action: 'sns:Publish'
Resource: !Ref SnsTopic
Condition:
ArnLike:
'aws:SourceArn': !Sub arn:aws:s3:::<bucket_name>
Topics:
- !Ref SnsTopic
S3Bucket:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: <bucket_name>
VersioningConfiguration:
Status: Enabled
NotificationConfiguration:
TopicConfigurations:
- Event: 's3:ObjectCreated:*'
Topic: !Ref SnsTopic
Filter:
S3Key:
Rules:
- Name: prefix
Value: prefix
- Name: suffix
Value: suffix
DependsOn: SnsTopicPolicy
S3BucketPolicy:
Type: 'AWS::S3::BucketPolicy'
Properties:
Bucket: !Ref S3Bucket
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
AWS: !Ref Principals
Action: 's3:GetObject'
Resource: !Sub arn:aws:s3:::<bucket_name>
送信先のLambdaを管理するアカウント
AWSTemplateFormatVersion: '2010-09-09'
Resources:
LambdaRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: 'sts:AssumeRole'
Policies:
- PolicyName: LambdaPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- 's3:GetObject'
Resource: !Sub arn:aws:s3:::<bucket_name>/*
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
Lambda:
Type: 'AWS::Lambda::Function'
Properties:
FunctionName: function
Handler: index.main
Role: !GetAtt LambdaRole.Arn
Code:
ZipFile: 'xxxxxxxxxxxxx'
Runtime: python3.9
Timeout: 300
MemorySize: 128
LambdaPermission:
Type: 'AWS::Lambda::Permission'
Properties:
Action: 'lambda:InvokeFunction'
FunctionName: !Ref Lambda
Principal: 'sns.amazonaws.com'
SourceAccount: !Ref SoureAccountID
SourceArn: !Sub arn:aws:sns:${AWS::Region}:<source_account_id>:topic
ExternalSnsTopicSubscription:
Type: 'AWS::SNS::Subscription'
Properties:
Protocol: 'lambda'
TopicArn: !Sub arn:aws:sns:${AWS::Region}:<source_account_id>:topic
Endpoint: !GetAtt Lambda.Arn
以下の、SNSサブスクリプションは、送信先のLambdaを配置するアカウント側で実行する必要があります。
(S3バケット側で実行しようとすると、エラーになります。)
ExternalSnsTopicSubscription:
Type: 'AWS::SNS::Subscription'
Properties:
Protocol: 'lambda'
TopicArn: !Sub arn:aws:sns:${AWS::Region}:<source_account_id>:topic
Endpoint: !GetAtt Lambda.Arn
おわりに
S3のイベント通知などを駆使するイベントドリブンな構成は、AWSを利用する上ではよくある構成です。
しかし、ちょっと複雑なことをやろうとするといろいろな制約に引っ掛かり、手戻りなどで思ったより時間がかかってしまいました。
どなたかに役立てば幸いです。
Appendix
- Amazon S3 イベント通知を作成するときに「Amazon S3 イベント通知を作成する際に、次の宛先の設定を検証できません」というエラーが表示されるのはなぜですか?
- Lambda 関数をトリガーする Amazon S3 イベント通知を作成するときに、「Configuration is ambiguously defined」(構成の定義があいまいです) というエラーが表示されるのはなぜですか?
- CloudFormationでSNSトピックへの複数のイベント通知を設定したS3バケットを作成する
- Fanout S3 Event Notifications to Multiple Endpoints