CloudFormationでCloudWatchEvents→Lambdaの環境を作るときに気をつけたいこと
CloudWatchEventsルールでLambdaを動かす環境をCloudFormationで作っていました。
その際、Lambdaが動かずハマってしまったのでその備忘録です。
やりたかったこと
CloudWatchEventsでcron式を定義して、日次でLambdaを動かしたい! 作りたかったものは、ただこれだけのシンプルな構成です。
CloudFormationテンプレートで書くとこんな感じです。(※IAM Roleは適切に設定されているものとしてください)
--- AWSTemplateFormatVersion: '2010-09-09' Parameters: RoleName: Type: String Default: SampleLambdaRole Resources: # まずはLambdaを定義 Lambda: Type: 'AWS::Lambda::Function' Properties: Code: ZipFile: | # -- coding: utf-8 -- import logging logger = logging.getLogger() logger.setLevel(logging.INFO) def lambda_handler(event, context): logger.info("Event: " + str(event)) Description: LambdaSample FunctionName: LambdaSample Handler: index.lambda_handler MemorySize: 128 Role: !Sub 'arn:aws:iam::${AWS::AccountId}:role/${RoleName}' Runtime: python3.6 Timeout: 3 # 次にCloudWatchEventsのルールを定義 Rule: Type: 'AWS::Events::Rule' Properties: Description: sample-rule Name: everyday ScheduleExpression: 'cron(0 1 * * ? *)' State: ENABLED Targets: - Arn: !GetAtt Lambda.Arn Id: lambda # 最後にCloudWatchEventsからLambdaの実行を許可する LambdaEvent: Type: 'AWS::Lambda::Permission' Properties: Action: 'lambda:InvokeFunction' FunctionName: !Ref Lambda Principal: 'events.amazonaws.com' SourceAccount: !Ref AWS::AccountId SourceArn: !GetAtt Rule.Arn
一見すると正しそうなのに、時間になってもLambdaが実行されていない!なぜ!?
ってことが起きました。
Lambdaのテストをしてみる
まずはLambda単独で動くかテストをしてみました。
Lambda単独では動きそうです。 と、いうことはCloudWatchEventsの設定がおかしい?cron式が間違ってるとか?と、もやもやしながら次へ。
マネジメントコンソールから同じ環境を作ってみる
Lambdaを作って。
ソースを適当にコピペして。
動作確認用に毎分発火するCloudWatchEventsのルールを作って。
Lambdaのトリガーに設定して。
同じ環境を作ったつもりなのに普通に動いてる。なぜ???
公式ドキュメントを見てみる
何が起きているのか全然良くわからないのでググります。 CloudWatchEventsのトラブルシューティングに、今の状況にぴったりあいそうな記述がありました。
CloudWatch イベント のトラブルシューティング | ルールはトリガーされたが、Lambda 関数が呼び出されなかった
その中をよーく見るとちらっと書いてありました。
Lambda 関数がトリガーに失敗するもう 1 つの理由は、get-policy の実行中に表示されるポリシーに、SourceAccount フィールドが含まれている場合です。SourceAccount 設定により、CloudWatch イベントは関数を呼び出すことができなくなります。
そういえばCloudFormationテンプレートの中でSourceAccountを設定した気がする…。
関数のポリシーを比較する
実際そうなのかマネジメントコンソールで作ったLambdaの関数のポリシーを確認してみます。 ちなみに関数のポリシーはここから確認できます。
見てみると確かに、マネジメントコンソールで作られたLambdaでは、ポリシーにSoruceAccountが含まれていなさそうです。
CloudFormationテンプレートからSourceAccountの記述を消して、発火しないLambdaの問題は無事に解決いたしました。
まとめ
CloudWatchEventsからLambdaを実行したいときは、関数のポリシー(AWS::Lambda::Permission)にSourceAccountを設定してはいけない。
ということだけ、覚えて帰っていただければ幸いです。