AWS CloudFormation で Lambda リソースベースポリシー と ターゲットグループのARN指定 が循環依存関係になる問題を Lambda-backend カスタムリソースを使わないで回避する方法
Lambdaとターゲットグループを1つのテンプレート作成する機会がありました。実現したいことは1つのテンプレートで...
- ELBのターゲットグループ(
AWS::ElasticLoadBalancingV2::TargetGroup
) - Lambdaのリソースベースポリシー(
AWS::Lambda::Permission
)
上記リソースを作成する必要がありました。すると循環依存関係の問題が起きます。この問題とどう向き合ったかを紹介します。
まとめ
AWS公式のワークアラウンドはLambda-Backend カスタムリソースを利用する。
Lambda-Backend カスタムリソースを使いたくないときはリソースベースポリシーにワイルドカードを使う。
- リソースベースポリシー(
AWS::Lambda::Permission
)で指定するターゲットグループARNの箇所をワイルドカードに置き換える。 - ワイルドカードを置く位置によってはバリデーションエラーが出力されるケースも確認された。
LambdaPermission: Type: "AWS::Lambda::Permission" Properties: Action: "lambda:InvokeFunction" FunctionName: !GetAtt Function.Arn Principal: "elasticloadbalancing.amazonaws.com" SourceArn: !Sub "arn:aws:elasticloadbalancing:${AWS::Region}:${AWS::AccountId}:*" # ワイルドカードを採用
検証環境
項目 | 値 |
---|---|
AWS SAM | 1.36.0 |
問題と向き合った
Lambdaのリソースベースポリシーと、ターゲットグループを同一のテンプレートで作成したい具体的な理由、構成は以下のリンクで紹介しています。
循環依存関係で問題となる箇所はターゲットグループARNをリソースベースポリシーで指定して明示的にターゲットグループを許可するために、ターゲットグループのリソースを先に作成してターゲットグループのARNを確定させたいです。
それならばDepndsOn
, !Ref
, !Sub
あたりでなんとかなりそうな気もしますがなんとかなりませんでした。
AWS公式の回避策
AWS公式の回避策はLambda-Backend カスタムリソースを利用する方法を紹介されています。
ライフサイクルの関係上、1つテンプレートにまとめて管理したい上にどうしてもLambda-Backend カスタムリソースを使いたくない場合、他に回避方法はないか考えましたので紹介していきます。
AWS SAMを利用していますがCloudFormationのテンプレート作成しても同様です。
ワイルドカードを利用した回避策
普通にテンプレート作成して循環依存問題に直面するケースを先に確認し、その後ワイルドカードを利用した回避策を試してみます。
まず、リソースベースポリシーとは
Lambda関数にはリソースベースポリシーというアクセス権限設定があります。たとえばAPI Gatewayや、EventBridgeなどのリソースからLambda関数を呼び出すときには、リソースベースポリシーで呼び出し元のリソース名(ARN)を許可しておく必要があります。
リソースベースポリシーの詳細については以下のリンクを参照ください。
問題のあるテンプレート記述
リソースベースポリシーで指定するSorceArn
に!Ref
でターゲットグループのARNを渡したいと思います。
# リソースベースポリシーの作成 LambdaPermission: Type: "AWS::Lambda::Permission" Properties: Action: "lambda:InvokeFunction" FunctionName: !GetAtt Function.Arn Principal: "elasticloadbalancing.amazonaws.com" SourceArn: !Ref TargetGroup1 # ELBからLambadaを呼び出すためにはターゲットグループARNを指定して明示的な許可を与える必要がある # ターゲットグループの作成 TargetGroup1: # ここで作成されるターゲットグループのARNが欲しい Type: "AWS::ElasticLoadBalancingV2::TargetGroup" Properties: HealthCheckIntervalSeconds: 15 HealthCheckPath: "/" HealthCheckTimeoutSeconds: 10 UnhealthyThresholdCount: 2 TargetType: "lambda" Matcher: HttpCode: "200" HealthyThresholdCount: 2 Name: "first-lambda-tg" HealthCheckEnabled: false TargetGroupAttributes: - Key: "lambda.multi_value_headers.enabled" Value: "false" Targets: - Id: !GetAtt Function.Arn AvailabilityZone: "all"
sam deploy
でLambdaをデプロイしてみます。以下のエラーによりデプロイに失敗します。
...snip... CREATE_FAILED AWS::ElasticLoadBalancingV2:: TargetGroup1 API: elasticloadbalancingv2:R TargetGroup egisterTargets elasticloadbalancing principal does not have permission to invoke arn:aws:lambda:ap- ...snip...
ワイルドカード指定のテンプレート記述
事前にターゲットグループARNを入力してしまえばよいのでは?と最初考えました。しかし、ターゲットグループのARNの末尾には英数字ランダムが付与されます。マネージメントコンソールから作成しても、CloudFormationから作成しても英数字ランダムの箇所を削除することはできませんでした。
つまり、事前にターゲットグループのARN決め打ちして入力しておく案は使えませんでした。
リソースベースポリシーのSourceArn
指定は明示的に許可を与えるため最小特権の原則にしたがって通常設定しているかと思います。英数字ランダム要素を回避するためにワイルドカードを指定できるのでしょうか?
ここでひとつ問題がありワイルドカードを含めるとバリデーションエラーが表示されるケースを何パターンか確認できました。しかし、バリデーションエラーのパターンに規則性は見つけられないこと、設定時にはエラーにならないがマネージメントコンソールから確認するとバリデーションエラーが表示される状況でした。
切り分けの詳細は以下のリンクをご確認ください。
最終的にバリデーションエラーが確認されなかった以下のパターンに落ち着きました。権限の範囲が広くなり同アカウントのELBからの呼び出しを許可することになります。特定のターゲットグループからに絞りたかったのですがバリデーションエラーが確認されたため無難な設定にしています。
# リソースベースポリシーの作成 LambdaPermission: Type: "AWS::Lambda::Permission" Properties: Action: "lambda:InvokeFunction" FunctionName: !GetAtt Function.Arn Principal: "elasticloadbalancing.amazonaws.com" SourceArn: !Sub "arn:aws:elasticloadbalancing:${AWS::Region}:${AWS::AccountId}:*" # ワイルドカードを採用 # ターゲットグループの作成 TargetGroup1: Type: "AWS::ElasticLoadBalancingV2::TargetGroup" Properties: HealthCheckIntervalSeconds: 15 HealthCheckPath: "/" HealthCheckTimeoutSeconds: 10 UnhealthyThresholdCount: 2 TargetType: "lambda" Matcher: HttpCode: "200" HealthyThresholdCount: 2 Name: "first-lambda-tg" HealthCheckEnabled: false TargetGroupAttributes: - Key: "lambda.multi_value_headers.enabled" Value: "false" Targets: - Id: !GetAtt Function.Arn AvailabilityZone: "all"
この記述であればsam deploy
しても問題なくリソース作成できました。
まとめ
参考にワイルドカード指定のテンプレート全文を載せておきます。
折りたたみ
AWSTemplateFormatVersion: "2010-09-09" Transform: AWS::Serverless-2016-10-31 Description: > first-lambda Sample SAM Template for first-lambda Resources: Function: Type: AWS::Serverless::Function Properties: FunctionName: "first-lambda" CodeUri: ./first-lambda Handler: app.lambda_handler Runtime: python3.9 Architectures: - arm64 Timeout: 5 LambdaPermission: Type: "AWS::Lambda::Permission" Properties: Action: "lambda:InvokeFunction" FunctionName: !GetAtt Function.Arn Principal: "elasticloadbalancing.amazonaws.com" SourceArn: !Sub "arn:aws:elasticloadbalancing:${AWS::Region}:${AWS::AccountId}:*" # --------------------------- # Resources associated with the ALB # --------------------------- TargetGroup1: Type: "AWS::ElasticLoadBalancingV2::TargetGroup" Properties: HealthCheckIntervalSeconds: 15 HealthCheckPath: "/" HealthCheckTimeoutSeconds: 10 UnhealthyThresholdCount: 2 TargetType: "lambda" Matcher: HttpCode: "200" HealthyThresholdCount: 2 Name: "first-lambda-tg" HealthCheckEnabled: false TargetGroupAttributes: - Key: "lambda.multi_value_headers.enabled" Value: "false" Targets: - Id: !GetAtt Function.Arn AvailabilityZone: "all" ListenerRule: Type: "AWS::ElasticLoadBalancingV2::ListenerRule" Properties: Priority: "1" ListenerArn: !ImportValue blog-alb-Listener1 Conditions: - Field: "path-pattern" Values: - "/v1/lambda1" Actions: - Type: "forward" TargetGroupArn: !Ref TargetGroup1 Order: 1 ForwardConfig: TargetGroups: - TargetGroupArn: !Ref TargetGroup1 Weight: 1 TargetGroupStickinessConfig: Enabled: false Outputs: Function: Description: "Lambda Function ARN" Value: !GetAtt Function.Arn FunctionIamRole: Description: "Implicit IAM Role created for function" Value: !GetAtt FunctionRole.Arn
- リソースベースポリシー(
AWS::Lambda::Permission
)で指定するターゲットグループARNの箇所をワイルドカードに置き換える。 - ワイルドカードを置ける位置によってはバリデーションエラーが出力されるケースも確認された。
LambdaPermission: Type: "AWS::Lambda::Permission" Properties: Action: "lambda:InvokeFunction" FunctionName: !GetAtt Function.Arn Principal: "elasticloadbalancing.amazonaws.com" SourceArn: !Sub "arn:aws:elasticloadbalancing:${AWS::Region}:${AWS::AccountId}:*" # ワイルドカードを採用
おわりに
循環依存関係が問題だったのですが、厄介だったのはリソースベースポリシーのSorceArn
にワイルドカードが含めるとバリデーションエラーになる条件がわからないことでした。