本記事について
- AWS CloudFormationを用いて、色々なアーキテクチャを構築していきます。テンプレートのコピペ元としてご活用いただければ幸いです
- 最新テンプレートはGitHubに配置しています
- 本記事では、Lambda関数を作成し、以下の3つの方法で呼び出します
- S3へのアップロードをトリガーとして呼び出す
- S3へのアップロードをトリガーとして、EventBridge経由で呼び出す
- S3へのアップロードをトリガーとして、SQS経由で呼び出す
S3へのアップロードをトリガーとして呼び出す
構築するアーキテクチャ
- まず、Lambda関数用のIAMロールを作成します
- Lambda用に基本的な
AWSLambdaBasicExecutionRole
を付与し、それ以外は特に何も設定しません
- Lambda用に基本的な
- 次に、Lambda関数を作成します
- python3.10をランタイムとし、適当なコードをテンプレート内に埋め込んでいます (簡単のため)
- また、サンプルとして、環境変数経由でテンプレート内のパラメータを渡すコードも書いていますが、特に意味はありません
- 次に、Lambda関数がS3から呼び出される (invokeされる) ことを許可するように、Lambda::Permissionを作成します
- Lambda関数が呼び出されない場合は、大体この設定を忘れていることが多いと思います
- 注意点
-
SourceArn
としてS3バケットのarnを指定しています。が、ここで!Ref
を使うと後で作成するS3バケットと循環参照になってしまうので、バケット名からarnの文字列を作成しています - これがないとデプロイ時にエラーが出ます
-
- 最後に、S3バケットを作成します
-
NotificationConfiguration
->LambdaConfigurations
を指定して、オブジェクトが作られたときに先ほど作成したLambda関数を呼び出すように設定しています - 注意点
- S3バケットの設定をするためには、事前にLambda関数が作られている必要があります。そのため、
DependsOn: LambdaFunction
を追加しています - これがないとデプロイ時にエラーが出ます
- S3バケットの設定をするためには、事前にLambda関数が作られている必要があります。そのため、
-
作成するリソース
- Lambda関連
- AWS::IAM::Role
- AWS::Lambda::Function
- AWS::Lambda::Permission
- S3バケット関連
- AWS::S3::Bucket
デプロイ方法
必要な準備
- AWSアカウントとアクセスキーの設定
- サーバーレス環境のため、特にVPCなどは不要です。事前の環境構築は不要です
デプロイコマンド
Region=ap-northeast-1
OrganizationName=iwatake2222
SystemName=sample-06-a
aws cloudformation deploy \
--region "${Region}" \
--stack-name "${SystemName}"-lambda-s3 \
--template-file ./lambda-s3.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
OrganizationName="${OrganizationName}" \
SystemName="${SystemName}"
動作確認
- 下記コマンドによって、適当なファイルをS3バケットへアップロードします
touch dummy
aws s3 cp dummy s3://"${OrganizationName}-${SystemName}-bucket"
- その後、Lambdaのログを確認します。場所は以下の通りです
- AWS Console -> CloudWatch -> Log Group -> /aws/lambda/${SystemName}
- (ロググループの下に関数が存在しない場合は、正しいリージョンにいるかどうかも確認してください)
- ログを見ると、Pythonでprintした内容が出力されていることが分かります
- また、引数であるevent内に、アップロードされたオブジェクトのバケット名やキー(ファイル名と場所)といった情報が格納されていることが分かります
テンプレート
lambda-s3.yaml
lambda-s3.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: |
Create a lambda function triggered by S3 upload
Parameters:
OrganizationName:
Description: Organization Name
Type: String
SystemName:
Description: System Name
Type: String
Resources:
#-----------------------------------------------------------------------------
# Lambda Function
#-----------------------------------------------------------------------------
LambdaRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${SystemName}-lambda-role
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
LambdaFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub ${SystemName}-lambda
Runtime: python3.10
Handler: index.handler
Role: !GetAtt LambdaRole.Arn
Environment:
Variables:
VarLambdaRole: !Ref LambdaRole
VarSystemName: !Sub ${SystemName}
Code:
ZipFile: |
import os
def handler(event, context):
print('Lambda is called!!')
print('event: ', event)
print(os.environ['VarLambdaRole'])
print(os.environ['VarSystemName'])
LambdaPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !Ref LambdaFunction
Principal: s3.amazonaws.com
# SourceArn: !GetAtt S3Bucket.Arn # Avoid recursive reference
SourceArn: !Sub arn:aws:s3:::${OrganizationName}-${SystemName}-bucket
#-----------------------------------------------------------------------------
# S3 bucket
#-----------------------------------------------------------------------------
S3Bucket:
Type: AWS::S3::Bucket
DependsOn: LambdaFunction
Properties:
BucketName: !Sub ${OrganizationName}-${SystemName}-bucket
NotificationConfiguration:
LambdaConfigurations:
- Event: 's3:ObjectCreated:*'
Function: !GetAtt LambdaFunction.Arn
Tags:
- Key: Name
Value: !Sub ${OrganizationName}-${SystemName}-bucket
S3へのアップロードをトリガーとして、EventBridge経由で呼び出す
構築するアーキテクチャ
- 先ほどは、S3から直接Lambdaを呼び出しました
- しかし、この方法だとS3バケット作成時にLambdaが事前に存在している必要があり、後から追加したりすることができません
- また、本当に呼び出しを行ったかどうか、どれくらいの頻度で呼び出しが発生したかという情報が分かりません
- そこで、EventBridgeという仕組みを使います
- EventBridgeが、S3バケットでのイベントを監視します
- EventBridgeに対してRuleを設定し、ある条件になったらLambdaを呼び出すようにします
- S3バケット作成時には、
EventBridgeEnabled: true
という設定をするだけで、詳細の設定は不要です - 先ほどのテンプレートに加えて、
AWS::Events::Rule
を作成します- この中で、作成したS3バケットへのアップロード(ファイル作成)を条件として、Lambdaを呼び出すように設定します
- また、Lambdaの呼び出し元がS3からEventBridgeに変わったので、LambdaPermissionのprincipalとsourceも変更が必要になります
作成するリソース
- Lambda関連
- AWS::IAM::Role
- AWS::Lambda::Function
- AWS::Lambda::Permission
- EventBridge関連
- AWS::Events::Rule
- S3バケット関連
- AWS::S3::Bucket
デプロイ方法
必要な準備
- AWSアカウントとアクセスキーの設定
- サーバーレス環境のため、特にVPCなどは不要です。事前の環境構築は不要です
デプロイコマンド
Region=ap-northeast-1
OrganizationName=iwatake2222
SystemName=sample-06-b
aws cloudformation deploy \
--region "${Region}" \
--stack-name "${SystemName}"-lambda-s3-event \
--template-file ./lambda-s3-event.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
OrganizationName="${OrganizationName}" \
SystemName="${SystemName}"
動作確認
- 下記コマンドによって、適当なファイルをS3バケットへアップロードします
touch dummy
aws s3 cp dummy s3://"${OrganizationName}-${SystemName}-bucket"
- その後、Lambdaのログを確認します。場所は以下の通りです
- AWS Console -> CloudWatch -> Log Group -> /aws/lambda/${SystemName}
- また、作成したEventBridgeのモニタから、イベントが発生したかどうか、Lambdaを呼び出したかどうか、エラーが発生したかどうか、といったことが分かります。場所は以下の通りです
- AWS Console -> Amazon EventBridge -> Rule -> 作成したルール (sample-06-b-lambda-s3-event-)
- 反映されるのに少し時間がかかります
テンプレート
lambda-s3-event.yaml
lambda-s3-event.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: |
Create a lambda function triggered by S3 upload via EventBridge
Parameters:
OrganizationName:
Description: Organization Name
Type: String
SystemName:
Description: System Name
Type: String
Resources:
#-----------------------------------------------------------------------------
# Lambda Function
#-----------------------------------------------------------------------------
LambdaRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${SystemName}-lambda-role
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
LambdaFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub ${SystemName}-lambda
Runtime: python3.10
Handler: index.handler
Role: !GetAtt LambdaRole.Arn
Environment:
Variables:
VarLambdaRole: !Ref LambdaRole
VarSystemName: !Sub ${SystemName}
Code:
ZipFile: |
import os
def handler(event, context):
print('Lambda is called!!')
print('event: ', event)
print(os.environ['VarLambdaRole'])
print(os.environ['VarSystemName'])
LambdaPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !Ref LambdaFunction
Principal: events.amazonaws.com
SourceArn: !GetAtt S3LambdaEventsRule.Arn
#-----------------------------------------------------------------------------
# EventBridge
#-----------------------------------------------------------------------------
S3LambdaEventsRule:
Type: AWS::Events::Rule
Properties:
State: ENABLED
EventPattern:
source:
- aws.s3
detail:
reason:
- CopyObject
- PutObject
- CompleteMultipartUpload
bucket:
name:
- !Ref S3Bucket
# object:
# key:
# - suffix: .jpg
Targets:
- Arn: !GetAtt LambdaFunction.Arn
Id: LambdaFunctionTarget
#-----------------------------------------------------------------------------
# S3 bucket
#-----------------------------------------------------------------------------
S3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${OrganizationName}-${SystemName}-bucket
NotificationConfiguration:
EventBridgeConfiguration:
EventBridgeEnabled: true
Tags:
- Key: Name
Value: !Sub ${OrganizationName}-${SystemName}-bucket
S3へのアップロードをトリガーとして、SQS経由で呼び出す
構築するアーキテクチャ
- 今度は、EventBridgeの代わりにSimple Queue Service (SQS) を使ってLambdaを呼び出してみます
- S3バケットにファイルがアップロードされたら、キューに情報を送信するようにします
- そのために、S3バケット作成時に
NotificationConfiguration
->QueueConfigurations
で後で作成するSQSを指定します- ちなみに、ここの結合を疎にするため、S3 -> EventBridge -> SQS -> Lambdaとすることも出来ます。参考
- そのために、S3バケット作成時に
- SQSキューを作成します
- 色々と設定はあるのですが、ここではデフォルトのままにします
- S3からこのキューに送信できるように、
QueuePolicy
の設定をします。これを忘れるとデプロイ時にS3バケット作成でエラーが出ます
- Lambda関数の方は、トリガーがSQSに変わったため、
Lambda:Permission
のsourceとprincipalを変更しておきます - また、SQSにメッセージが送られたことをトリガーとしてLambdaを呼び出すように
EventSourceMapping
の設定をします - Lambda関数のコード内で、わざわざキューから
receive_message
をしたりということは不要です。これまでと同じように呼び出し時の引数event
内に必要な情報は格納されています。また、キューからメッセージの削除も自動的に行われます- ただし、明示的にSQSの操作をしないでも、上記の通り実際にLambdaとしてSQSに対して操作をするのでLambdaのIAMロールに対して、SQSに対するアクセス権限を付与するポリシーの設定が必要になります
作成するリソース
- Lambda関連
- AWS::IAM::Role
- AWS::Lambda::Function
- AWS::Lambda::Permission
- AWS::Lambda::EventSourceMapping
- SQS関連
- AWS::SQS::Queue
- AWS::SQS::QueuePolicy
- S3バケット関連
- AWS::S3::Bucket
デプロイ方法
必要な準備
- AWSアカウントとアクセスキーの設定
- サーバーレス環境のため、特にVPCなどは不要です。事前の環境構築は不要です
デプロイコマンド
Region=ap-northeast-1
OrganizationName=iwatake2222
SystemName=sample-06-c
aws cloudformation deploy \
--region "${Region}" \
--stack-name "${SystemName}"-lambda-s3-sqs \
--template-file ./lambda-s3-sqs.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
OrganizationName="${OrganizationName}" \
SystemName="${SystemName}"
動作確認
- 下記コマンドによって、適当なファイルをS3バケットへアップロードします
touch dummy
aws s3 cp dummy s3://"${OrganizationName}-${SystemName}-bucket"
- その後、Lambdaのログを確認します。場所は以下の通りです
- AWS Console -> CloudWatch -> Log Group -> /aws/lambda/${SystemName}
- また、作成したSQSのモニタリングから、イベントが発生したかどうか、Lambdaを呼び出したかどうか、エラーが発生したかどうか、といったことが分かります。場所は以下の通りです
- AWS Console -> Amazon SQS -> キュー -> 作成したキュー (sample-06-c-queue)
- また、「メッセージを送受信」からメッセージを送信することでもLambdaを呼び出すことが可能です
- S3へファイルをアップロードしたら受信メッセージが現れるはずですが、ポーリングするよりも前にLambdaが呼ばれてメッセージは即削除されて見えないようです
- (トリガーを一時的に外せば確認できます)
テンプレート
lambda-s3-sqs.yaml
lambda-s3-sqs.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: |
Create a lambda function triggered by S3 upload via SQS Queue
Parameters:
OrganizationName:
Description: Organization Name
Type: String
SystemName:
Description: System Name
Type: String
Resources:
#-----------------------------------------------------------------------------
# Lambda Function
#-----------------------------------------------------------------------------
LambdaRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${SystemName}-lambda-role
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: !Sub ${SystemName}-lambda-policy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- 'sqs:*'
Resource:
- !GetAtt SQSQueue.Arn
LambdaFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub ${SystemName}-lambda
Runtime: python3.10
Handler: index.handler
Role: !GetAtt LambdaRole.Arn
Environment:
Variables:
SQSQueueUrl: !GetAtt SQSQueue.QueueUrl
Code:
ZipFile: |
import boto3
import os
def handler(event, context):
print('Lambda is called!!')
print('event: ', event)
# # No need to receive_message
# queue_url = os.environ['SQSQueueUrl']
# sqs_client = boto3.client('sqs')
# response = sqs_client.receive_message(QueueUrl=queue_url)
# print(response)
LambdaPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !Ref LambdaFunction
Principal: sqs.amazonaws.com
SourceArn: !GetAtt SQSQueue.Arn
LambdaEventSourceMapping:
Type: AWS::Lambda::EventSourceMapping
Properties:
EventSourceArn: !GetAtt SQSQueue.Arn
FunctionName: !GetAtt LambdaFunction.Arn
#-----------------------------------------------------------------------------
# SQS Queue
#-----------------------------------------------------------------------------
SQSQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub ${SystemName}-queue
# ContentBasedDeduplication: true
# FifoQueue: true
SQSQueuePolicy:
Type: AWS::SQS::QueuePolicy
Properties:
PolicyDocument:
Version: 2012-10-17
Statement:
- Action:
- sqs:SendMessage
Effect: Allow
Principal: '*'
Resource: !GetAtt SQSQueue.Arn
Condition:
ArnEquals:
# aws:SourceArn: !GetAtt S3Bucket.Arn # Avoid recursive reference
aws:SourceArn: !Sub arn:aws:s3:::${OrganizationName}-${SystemName}-bucket
Queues:
- !Ref SQSQueue
#-----------------------------------------------------------------------------
# S3 bucket
#-----------------------------------------------------------------------------
S3Bucket:
Type: AWS::S3::Bucket
DependsOn: SQSQueue
Properties:
BucketName: !Sub ${OrganizationName}-${SystemName}-bucket
NotificationConfiguration:
QueueConfigurations:
- Event: 's3:ObjectCreated:*'
Queue: !GetAtt SQSQueue.Arn
Tags:
- Key: Name
Value: !Sub ${OrganizationName}-${SystemName}-bucket