[AWS] CloudWatch AlarmからLambdaが起動されない

概要

  • CloudWatch AlarmからLambdaを起動したいが動作しない
  • Lambda単体のテストでは動作しているため、CloudWatch Alarmからの起動が失敗している可能性が考えられる

Alarmの状態はちゃんとALARMに変わっているのに、Lambdaが呼ばれた形跡がない、という状態です。CloudWatch Logsを見ても、Lambda側には何も出ていません。呼び出しがLambdaに到達する前に弾かれているためです。

Alarmの履歴(History)を確認すると、次のようなメッセージが記録されていることがあります。

CloudWatch Alarms is not authorized to perform: lambda:InvokeFunction on the resource because no resource-based policy allows the lambda:InvokeFunction action

これが出ていれば、以下の対処で解決します。

解決方法

この場合に落とし穴になりやすいのが、CloudWatch Alarmからの操作をLambdaが受け付けるようにポリシーを設定していないという点です。具体的には以下の手順で設定を行います。

  1. AWSコンソールで設定したいLambdaを選択する
  2. Configurationタブ > PermissionsメニューからResource-based policy statementsにpermissionを追加する
  3. Edit policy statementで以下の内容で登録する
    • AWS accountを選択
    • Statement ID : 任意の値を設定する
    • Principal : lambda.alarms.cloudwatch.amazonaws.com
    • Action : lambda:InvokeFunction

以上の設定を行えば無事CloudWatch AlarmからLambdaが起動されるようになります。

なぜこの設定が必要なのか

Lambdaを呼び出す権限には二種類あり、混同しやすいところです。

  • 実行ロール(IAMロール):Lambdaが「他のサービスに対して何をできるか」を決めるもの
  • リソースベースポリシー:「誰がこのLambdaを呼び出せるか」を決めるもの

今回必要なのは後者です。Lambda側の実行ロールをどれだけ強くしても、呼び出し元を許可していなければ起動されません。Lambda単体のテストが通るのに動かない、という症状になるのはこのためです。

そして、CloudWatch Alarmから呼び出す場合のサービスプリンシパルは lambda.alarms.cloudwatch.amazonaws.com です。CloudWatch関連で見慣れた cloudwatch.amazonaws.comevents.amazonaws.com ではないので、ここを間違えると設定したつもりでも動きません。私はこれで時間を使いました。

CLIで設定する場合

コンソールではなくCLIで設定する場合は次のようになります。

aws lambda add-permission \
  --function-name my-function-name \
  --statement-id AlarmAction \
  --action 'lambda:InvokeFunction' \
  --principal lambda.alarms.cloudwatch.amazonaws.com \
  --source-account 111122223333 \
  --source-arn arn:aws:cloudwatch:us-east-1:111122223333:alarm:alarm-name

--source-account--source-arn は必須ではありませんが、指定しておくことを勧めます。これを省くと、そのサービスプリンシパルからの呼び出しを無条件に許可することになります。特定のAlarmだけに絞っておく方が安全です。

設定内容は次のコマンドで確認できます。

aws lambda get-policy --function-name my-function-name

詳細はAWSの公式ドキュメントInvoke a Lambda function from an alarmにまとまっています。

関連記事

なお、Lambdaに関する以下のような記事も公開していますので、必要に応じてご参考にしてみてください。

EC2側からECRへ接続できない場合はEC2からECRへ接続できないときの対処にまとめています。