AWS SAM CLIを使って、 SNS -> Lambda (Python3.8) 起動できるようなテンプレートのメモ。ついでに、S3のイベントでSNSに通知するような場合についても記述する。
環境
- AWS SAM CLI 1.6.2
- LambdaのRuntimeはPython 3.8
SNS -> Lambda
node12.xだとSNS起動のテンプレートが用意されているのだが、Pythonでは用意されていない。まぁだいたい同じなので、node12.xのテンプレートをPython用にちょっとアレンジして使う。
ディレクトリ構造
まずディレクトリ構造は以下。
.
├── hello_world_function
│ ├── hello_world
│ │ ├── __init__.py
│ │ └── app.py
│ └── requirements.txt
├── samconfig.toml
└── template.yaml
app.py
実行される関数。ここではeventをログに出すだけのものとする。
def lambda_handler(event, context):
print(event)
__init__.py
空ファイル
requrements.txt
sam cli使っていて本当に楽だなぁと思うのが、ここで手軽にライブラリを導入できることだ。まぁでも今回は空ファイルでよい。
template.yaml
テンプレートだと.ymlなのだが、個人的には.yamlのほうが馴染むんだが、一般的にはどうなんだろうか。
node12.xのテンプレートをちょっと変えたものを以下に。あえてコメントアウトは残している。参照に使えるので。
# This is the SAM template that represents the architecture of your serverless application
# https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-template-basics.html
# The AWSTemplateFormatVersion identifies the capabilities of the template
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/format-version-structure.html
AWSTemplateFormatVersion: 2010-09-09
Description: >-
sam-app
# Transform section specifies one or more macros that AWS CloudFormation uses to process your template
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/transform-section-structure.html
Transform:
- AWS::Serverless-2016-10-31
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
Function:
Timeout: 3
# Resources declares the AWS resources that you want to include in the stack
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resources-section-structure.html
Resources:
# This is an SNS Topic with all default configuration properties. To learn more about the available options, see
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sns-topic.html
SimpleTopic:
Type: AWS::SNS::Topic
# This is the Lambda function definition associated with the source code: sns-payload-logger.js. For all available properties, see
# https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
SNSPayloadLogger:
Type: AWS::Serverless::Function
Properties:
Description: A Lambda function that logs the payload of messages sent to an associated SNS topic.
Runtime: python3.8
CodeUri: hello_world_function
Handler: hello_world/app.lambda_handler
# This property associates this Lambda function with the SNS topic defined above, so that whenever the topic
# receives a message, the Lambda function is invoked
Events:
SNSTopicEvent:
Type: SNS
Properties:
Topic: !Ref SimpleTopic
MemorySize: 128
Timeout: 100
Policies:
# Give Lambda basic execution Permission to the helloFromLambda
- AWSLambdaBasicExecutionRole
これでsam build; sam deploy --guided で samconfig.toml を作れば出来上がり。早い。
S3からSNSに通知する
せっかくなので、 S3 -> SNS -> Lambda まで組んでみる。
SAM側
S3のイベントでSNSのトピックに通知する時などは、SNSのアクセスポリシーで当該のバケットからのアクセスを許可する必要がある(S3 の put event を sns に流そうとした時の Permissions error の解決方法 - Qiita)。
ということで、トピックポリシーをAWS::SNS::TopicPolicyでする必要がある。Resrouces:の下に以下を挿入する。ここでは、既存のバケットを使うものとする(SAMでS3指定するには、バケット生成からやらないといけないから…)。なおS3のバケット名は、バケットポリシーから確認できる。
SimpleTopicPolicy:
Type: AWS::SNS::TopicPolicy
Properties:
PolicyDocument:
Version: "2008-10-17"
Id: "from_s3_policy_id"
Statement:
-
Sid: SendToSns
Effect: Allow
Principal:
AWS: "*"
Action:
SNS:Publish
Resource: !Ref SimpleTopic
Condition:
ArnLike:
aws:SourceArn: arn:aws:s3:::バケット名
Topics:
- !Ref SimpleTopic
ちなみにArnLikeでバケットを指定する時は、ワイルドカード(*)で一括指定もできる。
ここまでエラーとめっちゃ戦った。めっちゃエラー出た。どうも最初、Actionで色々指定したのがだめくさかった気がするが……。
S3の設定
今回S3は既存のバケットを使っているので、Webコンソールからバケットの設定をする。
当該バケットから「プロパティ」→「イベント」→SNSトピックとか設定して保存。
おまけ: Lambdaのeventの中身
S3 -> SNS -> Lambda の時
ちなみにこれでLambdaが受け取る内容は以下みたいな感じ。これはファイル生成した時。
{
"Records": [
{
"EventSource": "aws:sns",
"EventVersion": "1.0",
"EventSubscriptionArn": "TopicのARNになんか加えたやつが入る",
"Sns": {
"Type": "Notification",
"MessageId": "9e9a2471-37df-5726-b997-738c618e37d1",
"TopicArn": "TopicのARNが入る",
"Subject": "Amazon S3 Notification",
"Message": "<serialized-json>"
"Timestamp": "2020-10-11T18:50:57.486Z",
"SignatureVersion": "1",
"Signature": "BASE64っぽい",
"SigningCertUrl": "xxxxpem",
"UnsubscribeUrl": "xxxx",
"MessageAttributes": {}
}
}
]
}
Sns.Message部分はシリアライズされたjsonで、戻すと以下。
{
"Records": [
{
"eventVersion": "2.1",
"eventSource": "aws:s3",
"awsRegion": "ap-northeast-1",
"eventTime": "2020-10-11T18:50:51.353Z",
"eventName": "ObjectCreated:Put",
"userIdentity": {
"principalId": "なんかID"
},
"requestParameters": {
"sourceIPAddress": "xx.xx.xx.xx"
},
"responseElements": {
"x-amz-request-id": "xxxx",
"x-amz-id-2": "xxxx"
},
"s3": {
"s3SchemaVersion": "1.0",
"configurationId": "xxxx",
"bucket": {
"name": "バケット名",
"ownerIdentity": {
"principalId": "xxxx"
},
"arn": "バケットのARN"
},
"object": {
"key": "ファイル名",
"size": 844,
"eTag": "xxxx",
"sequencer": "xxxx"
}
}
}
]
}
ファイル名しかわからんね。まぁ仕方ない。
CloudWatch(Alarm) -> SNS -> Lambda の時
今回関係ないけどよく使うので。
API Gatewayで4xxエラーが出た時にSNSにalarmを飛ばしている。
{
"Records": [
{
"EventSource": "aws:sns",
"EventVersion": "1.0",
"EventSubscriptionArn": "xxxx",
"Sns": {
"Type": "Notification",
"MessageId": "xxxx",
"TopicArn": "xxxx",
"Subject": "ALARM: \"xxxx\" in Asia Pacific Tokyo",
"Message": "serialized-json"
"Timestamp": "2020-10-12T04:50:23.043Z",
"SignatureVersion": "1",
"Signature": "xxxx",
"SigningCertUrl": "xxxx",
"UnsubscribeUrl": "xxxx",
"MessageAttributes": {}
}
}
]
}
Message部分をjson.loads()するとこんな感じ。
{
'AlarmName': 'xxxx',
'AlarmDescription': None,
'AWSAccountId': 'xxxx',
'NewStateValue': 'ALARM',
'NewStateReason': 'xxxx',
'StateChangeTime': '2020-10-12T04:50:22.996+0000',
'Region': 'Asia Pacific Tokyo',
'AlarmArn': 'xxxx',
'OldStateValue': 'OK',
'Trigger': {
'MetricName': '4XXError',
'Namespace': 'AWS/ApiGateway',
'StatisticType': 'Statistic',
'Statistic': 'AVERAGE',
'Unit': None,
'Dimensions':
[
{
'value': 'xxxx',
'name': 'xxxx'
}
],
'Period': 300,
'EvaluationPeriods': 1,
'ComparisonOperator': 'GreaterThanThreshold',
'Threshold': 0.03,
'TreatMissingData': 'xxxx'
'EvaluateLowSampleCountPercentile': ''
}
}
参考
ありがとうございました。
コメント