🐞

【AWS CDK】他のスタックからインポートしたLambda関数への権限設定:CfnPermissionの使用が鍵

2024/07/24に公開

AWS CDKでインフラを構築する際に、適切な粒度でスタックを分割するかと思います。
スタックの依存関係を排除したい場合、他のスタックでは、LambdaをArnからインポートして使用するケースがあるかと思います。
その際に、ArnからインポートしたLambdaに対し権限付与する際にハマった為、対処方法を紹介します。

はじめに

まずは結論からインポートしたLambdaの権限変更については、CfnPermissionを用いることで解決することができます。

今回の構成は下記になります。

  1. 他のスタックで作成したLambdaをArnからインポート
  2. LambdaをEventBrigeのruleと連携
  3. \textcolor{red}{EventBrigeからの実行権限をLambdaに付与 ← 問題が発生}

上記のステップ3を解消していく流れになります。

問題の特定

まずは変更前のソースコードです。

export class SampleStack extends Stack {
  constructor(scope: Construct, id: string, props: any) {
    super(scope, id, props);
      // SSMからLambda関数のARNを取得
      const lambdaArn = StringParameter.valueForStringParameter(this, 'LambdaArn');
      // Lambda関数のインスタンスを生成
      const lambdaFunction = Function.fromFunctionArn(this, `function`, lambdaArn);
      // EventBridgeのルールとLambdaの連携
      const rule = new Rule(this, 'events', {
        schedule: Schedule.expression('cron(1-59 * * * ? *)' as string),
        targets: [new LambdaFunction(lambdaFunction)]
      })

      // ------------- ここに権限の付与処理を追加 ----------------

  }
}

「ここに権限の付与処理を追加」の場所に、実際に権限追加のコードを記載していきます。

addPermissionの例

下記の権限付与処理を追加します。そして、stackのdiffを取ります。

lambdaFunction.addPermission(`permission`, {
  principal: new iam.ServicePrincipal('events.amazonaws.com'),
  sourceArn: rule.ruleArn
});

上記の権限付与処理を追加した後にcdk diff ${スタック名}を実行しても下記のように差分がないと判断されます。

There were no differences

✨  Number of stacks with differences: 0

cfnPermissionの例

下記の権限付与処理を追加します。そして、stackのdiffを取ります。

new CfnPermission(this, `permission`, {
  principal: 'events.amazonaws.com',
  action: 'lambda:InvokeFunction',
  functionName: lambdaFunction.functionName,
  sourceArn: rule.ruleArn
});

上記の権限付与処理を追加した後にcdk diff ${スタック名}を実行すると、下記のように差分が出てきます。

This deployment will make potentially sensitive changes according to your current security approval level (--require-approval broadening).
Please confirm you intend to make the following modifications:

IAM Statement Changes
┌───┬──────────────────────┬────────┬──────────────────────┬──────────────────────┬───────────────────────┐
│   │ Resource             │ Effect │ Action               │ Principal            │ Condition             │
├───┼──────────────────────┼────────┼──────────────────────┼──────────────────────┼───────────────────────┤
│ + │ {"Fn::Select":[6,"{\ │ Allow  │ lambda:InvokeFunctio │ Service:events.amazo │ "ArnLike": {          │
│   │ "Fn::Split\":[\":\", │        │ n                    │ naws.com             │   "AWS:SourceArn": "  │
│   │ \"xxxx"              │        │                      │                      │ lambdaArn"            │
└───┴──────────────────────┴────────┴──────────────────────┴──────────────────────┴───────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

なぜcfnPermissionだとうまくいくのか

これは参考にはっている資料を見ても明確に書いていませんでした。
その為、作者の想定で書きます。

CfnPermissionとaddPermissionの違い

cdkでAWSリソースを作成する際には、aws-cdk-libライブラリを使用して、作成されるかと思います。
このaws-cdk-libは主に「Low Level Construct(L1)」と「High Level Construct(L2)」と「Patterns(L3)」のどれかを用いてサービスを作成しています。詳しい話はこちらを参考にしてください。

今回は、下記のようになります。

  • CfnPermission - Low Level Construct
  • addPermission - High Level Construct
level 使いどき
Low level ユーザーはAWSリソースのすべての側面を細かく制御したいとき。CloudFormationのリソース定義をそのまま反映しており、細かい設定やカスタマイズが可能になる。
High level Low levelより抽象化されたコンストラクトで、デフォルトの設定や簡単なインターフェース等が備わっている

このように抽象化レベルでいうとaddPermissionの方がCfnPermissionよりも、便利になっている分細かい制御がライブラリに依存する形になっています。
その為、Low levelからHigh levelへの抽象化の段階で、インポートしたLambdaに対して権限付与が正常にされないバグみたいなものかと考えています。(もしかしたら意図的に作られているかもしれません。)

まとめ

この記事では、AWS CDKを使用して他のスタックからインポートしたLambda関数に対する権限付与の手段を紹介しました。そして似たような使い方を行うaddPermissionとCfnPermissionの違いを示しました。
また、AWS CDKでは、抽象化レベルが異なるコンストラクトが提供されており、問題の起因として、この抽象化の段階でバグが起きているかもしれないと考察もしました。

この結果から、CDKも抽象化されるほど予期しないバグや挙動に見舞われる可能性がありそうです。その為、High levelとPatternsについては、すべてのシナリオに対して期待通りに機能するわけではないことを念頭に入れ開発を行っていきたいです。そして今回のようなケースに直面した際は、できるだけ素早くLow levelの方に舵を切れるにしていきたいと思います。

参考

https://github.com/aws/aws-cdk/issues/10509
https://nekoniki.com/20220214_cross-stack-lambda-permission
https://dev.classmethod.jp/articles/aws-cdk-construct-explanation/
https://docs.aws.amazon.com/cdk/v2/guide/constructs.html

Discussion