CDKでゴリゴリインフラを書いていくとどうしてもスタックが長くなりがちなので、適切な粒度でスタックを分割しているかと思います。 その際に、あるスタックで作成したLambdaに対して、以降の別スタックで権限を付与しようとした際にハマったので対処法を紹介します。
やりたいことの概要は以下の通りです。
LambdaStack
でLambda
を作成(ここではhoge
とする)ApiStack
でApiGateway
を作成し、そこにhoge
を割り当てるApiGateway
から呼ばれる(InvokeFunction
)する際はLambda
側にもリソースベースのポリシーが必要なのでApiStack
で追加するこの3つ目のApiStack
でLambda
に対してポリシーを追加するのがネックでした。
LambdaStack
ではOutput
としてhoge
のARN
を吐き出しています(HogeArn
)。
import * as cdk from 'aws-cdk-lib';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as lambda from 'aws-cdk-lib/aws-lambda';
/**
* API Gateway Stack
*/
export class ApiStack extends cdk.Stack {
public readonly api: apigateway.RestApi;
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// hogeを別スタックのOutputから取得
const hoge = lambda.Function.fromFunctionArn(this, 'HogeFunction', cdk.Fn.importValue('HogeArn'));
// REST APIを作成
this.api = new apigateway.RestApi(this, 'HogeApi', {
cloudWatchRole: false,
});
// ルートにGETメソッドを作成しhogeを割り当てる
this.api.root.addMethod('GET', new apigateway.LambdaIntegration(hoge));
// hogeのリソースベースのポリシーでApiGatewayからのInvokeFunctionを許可する
hoge.addPermission(
'InvokeFromApi',
{
principal: new iam.ServicePrincipal('apigateway.amazonaws.com'),
action: 'lambda:InvokeFunction',
// APIのARNを取得
sourceArn: this.api.arnForExecuteApi('GET', '/', '*')
}
);
}
}
一見これでうまくいきそうですが、肝心のリソースベースのポリシーが割り当てられていませんでした。 エラー自体は出ていないのですが、実際に作成されたAPIをテスト実行してみると権限エラーとなってしまいます。
以下のような記事を見つけました。 参考:DeveloperIO - AWS CDKで1つのスタックにLambdaを21個以上作る場合に起きる問題と対処方法の紹介
その中で以下のような記述があります。
(IFunction内にaddPermissionがありそちらも試したのですが、既存のLambdaをインポートした場合はCloudFormationにPermissionが出力されませんでした。 既存のLambdaをインポートした場合は、別アカウントのLambdaを参照して権限を付与しないためにaddPermissionが権限を生成せず終了するようになっています。 今回はアカウントの管理下にあるLambdaなのでCfnPermissionを使用して権限を追加しています。)
なるほど、と思いましたが同様の記事に以下のような記述も。
2020/10/27 追記 CDKのversion 1.64.0で同一のAWSアカウント内のLambdaをimportした場合Permissionが自動生成されるようになったので下記のCfnPermissionの記述は不要になりました。 詳しくはCDKのリリースノートをご参照ください。 AWS CDK v1.64.0 Release Note
手元のCDK
のバージョンは2.10.0
なのでとっくに解消されているはずだが、うーん・・・
何はなくともCfnPermission
を試してみます。
import * as cdk from 'aws-cdk-lib';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as lambda from 'aws-cdk-lib/aws-lambda';
/**
* API Gateway Stack
*/
export class ApiStack extends cdk.Stack {
public readonly api: apigateway.RestApi;
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// hogeを別スタックのOutputから取得
const hoge = lambda.Function.fromFunctionArn(this, 'HogeFunction', cdk.Fn.importValue('HogeArn'));
// REST APIを作成
this.api = new apigateway.RestApi(this, 'HogeApi', {
cloudWatchRole: false,
});
// ルートにGETメソッドを作成しhogeを割り当てる
this.api.root.addMethod('GET', new apigateway.LambdaIntegration(hoge));
// hogeのリソースベースのポリシーでApiGatewayからのInvokeFunctionを許可する
// hoge.addPermission(
// 'InvokeFromApi',
// {
// principal: new iam.ServicePrincipal('apigateway.amazonaws.com'),
// action: 'lambda:InvokeFunction',
// // APIのARNを取得
// sourceArn: this.api.arnForExecuteApi('GET', '/', '*')
// }
// );
// CfnPermissionで同じことをする
new lambda.CfnPermission(this, `InvokeFromApi`, {
principal: 'apigateway.amazonaws.com',
action: 'lambda:InvokeFunction',
functionName: hello.functionName,
sourceArn: this.api.arnForExecuteApi('GET', '/', '*')
});
}
}
これでhoge
にリソースベースのポリシーが付与されました。
手元のCDK
のバージョンなら、最初のやり方でも問題ないはずですが、またダメになったのかもしれません。
※もしくはaddPermission
に何かしらパラメータを付けるとうまくいく??
今回はスタックを分けた状態で、別なスタックで作成したLambda
をインポートした時に権限を割り当てられずにハマった件と、その対処法について紹介しました。
自分的には腑に落ちない解決方だったので、うまいやり方をご存知の方がいらっしゃいましたらコメントいただけると幸いです。