見出し画像

AWS LambdaのInvokeFunctionを正しく理解する

akippaの山下です。突然ですが皆さんは、AWSの好きなサービスは何ですか?色々ある中でも、個人的には Step Functions がとても魅力的だと思っています。

Lambdaと組み合わせることでロジックとフローをキレイに切り離せます(ただし、うまく設計しないとあっという間にピタゴラスイッチ)し、AWSサービスとの統合もかなり拡充した事で、大抵の事はStep Functionsで実現できるんじゃないかと思っています。

残念ながら、akippaではまだ導入のチャンスが無いものの、隙あらば活用したい、今回はそんなStep FunctionsとLambdaの連携でハマった話のご紹介です。

結論

IAMポリシーで、LambdaのInvokeFunctionは 修飾ARN と 非修飾ARN の違いを正しく理解して定義しましょう。

背景

GW中に、プライベートで以下のようなプログラムを書く機会がありました。

  1. 入力パラメーター基づいて特定の処理を実行

  2. 成果物をS3に保存

  3. 一定時間ウェイト

  4. パラメータを変えて1.〜3.をループ

  5. 条件を満たしたら終了

Step Functionsがジャストフィットするシチュエーションですね。さっそくステートマシンを作成して、でき上がったものがこちらです。


state-machine

一部のパラメーターをマスクしたJSONはこちらです。また、Lambdaのコードは本筋でないため割愛します。

{
  "StartAt": "Do the job",
  "States": {
    "Do the job": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "OutputPath": "$.Payload",
      "Parameters": {
        "Payload.$": "$",
        "FunctionName": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:my-job:$LATEST"
      },
      "Retry": [
        {
          "ErrorEquals": [
            "Lambda.ServiceException",
            "Lambda.AWSLambdaException",
            "Lambda.SdkClientException",
            "Lambda.TooManyRequestsException"
          ],
          "IntervalSeconds": 2,
          "MaxAttempts": 6,
          "BackoffRate": 2
        }
      ],
      "Next": "Still remain?"
    },
    "Still remain?": {
      "Type": "Choice",
      "Choices": [
        {
          "And": [
            {
              "Variable": "$.x",
              "IsPresent": true
            },
            {
              "Variable": "$.y",
              "IsPresent": true
            },
            {
              "Variable": "$.remain",
              "BooleanEquals": true
            }
          ],
          "Next": "Wait"
        }
      ],
      "Default": "Done"
    },
    "Wait": {
      "Type": "Wait",
      "Seconds": 3,
      "Next": "Do the job"
    },
    "Done": {
      "Type": "Pass",
      "End": true
    }
  }
}

これで後は、Step Functionsから初期パラメーターを与えて実行を開始すれば良いのですが、このままではStep FunctionsがLambdaの関数を呼び出す権限を持っていないため、実行は失敗してしまいます。

AWS SAM を使うべしという話もあるでしょうが、久々のStep Functionsだったので、自身の理解度を再確認する意味も含め、今回はすべて自前定義でチャレンジしています。

AWSコンソールからStep Functionsを作成すると、最低限のポリシーを持つロールを作成してくれるので、ここへ単純にLambdaのInvokeFunctionポリシーを追加しました。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "lambda:InvokeFunction",
            "Resource": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:my-job"
        }
    ]
}

さてこれであらためて実行してみると、無情にもLambda関数の呼び出しでエラー終了してしまいます。

User: arn:aws:sts::xxxxxxxxxxxx:assumed-role/StepFunctions-my-state-machine-role-783e8b39/CHSNGQCAzxJIPExrKeUvIBbVxpdixOfE is not authorized to perform: lambda:InvokeFunction on resource: arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:my-job:$LATEST because no identity-based policy allows the lambda:InvokeFunction action (Service: AWSLambda; Status Code: 403; Error Code: AccessDeniedException; Request ID: 9b6cd8d9-a61e-4307-9683-69cd09040477; Proxy: null)

エラーメッセージ

真っ先に疑うべきはARNの入力ミスですが、LambdaのARNをコピペしているので間違いないはず。他のパラメーターでミスしている可能性も含め、何度ポリシーを定義し直しても同じエラーでLambda関数が実行されません。

ポリシー側で呼び出しを許可する関数を「*(すべて)」にしてしまえば動くのですが、これでは根本解決にならないため、原因を特定するのにかなり手間どりました。

原因

分かってしまえば非常に単純ですが、以下が原因でした。

  • Step Functions側は、FunctionName に "$LATEST" を含む 修飾ARN で定義している

  • IAMポリシー側は、関数名までの 非修飾ARN で定義している

ARNに関数のバージョンまで含めない場合は $LATEST と同等である、と理解していたのですが、これがそもそもの誤りでした。公式ドキュメントにもしっかり記載されていました。

例えば、ポリシーが非修飾 ARN を参照する場合、Lambda は非修飾 ARN を参照するリクエストを受け入れますが、修飾 ARN を参照するリクエストは拒否します。

Lambda アクションのリソースと条件

今回のケースでは、IAMポリシーにバージョンを含めない「非修飾ARN」を記述し、Step Functions側ではバージョンも含めた「修飾ARN」で定義していたので、これがバッチリ引っかかっていたということでした。

  • Step FunctionsのFunctionName(修飾ARN)

    • function:my-job:$LATEST

  • IAMポリシーのResource(非修飾ARN)

    • function:my-job

対応

作ったものは書き捨てで構わないので、Lambda関数もバージョン管理する予定はありません。常に$LATESTで構わないので、IAMポリシー側を修飾ARNに変更しました。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "lambda:InvokeFunction",
            "Resource": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:my-job:$LATEST"
        }
    ]
}

この状態であらためて実行すると、ステートマシンは期待どおり動作開始、オールグリーンで処理完了しました。処理状況がUIで確認できるのもStep Functionsの良いところです。きれいに完了すると気持ちいいですね。

ちょっとした躓きでしたが、どなたかのお役に立てれば幸いです。

この記事が気に入ったらサポートをしてみませんか?