はじめに
AWS CDK を使って Lambda 関数をデプロイしている際に、Lambda バージョンが正しく作成されない問題に直面しました。ワークアラウンド的ではありますが、そのときの解決策を備忘録としてまとめておきます。
検証環境
- aws-cdk: 2.171.1
- typescript: 5.6.3
いきなり結論
Function
と Version
にタイムスタンプ付きの description
を付けます。
タイムスタンプはユニークな文字列となり差分を強制的に発生させられるので、毎回必ずバージョンが作成されるようになりました。
なお、本記事では Function
, Version
を使用していますが、CfnFunction
, CfnVersion
でも同じ挙動となりました。
AWS CDK - class Version (construct)
AWS CDK - class CfnVersion (construct)
Lambda 関数にも付けている理由は、Lambda 関数コード以外部分のみの変更の場合、同じ断面で新しいバージョンを作成出来ない旨のエラーとなるための対策です。
AWS CDK - class Function (construct)
AWS CDK - class CfnFunction (construct)
以下、最小構成のサンプルです。
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as iam from 'aws-cdk-lib/aws-iam';
export class SampleLambdaStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Lambda 実行用 IAM ロールを作成
const lambdaRole = new iam.Role(this, 'LambdaExecutionRole', {
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
});
lambdaRole.addManagedPolicy(
iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole')
);
const timeStamp = new Date().toLocaleString();
// Lambda 関数を作成
const sampleLambdaFunction = new lambda.Function(this, 'SampleLambdaFunction', {
code: lambda.Code.fromInline(`
exports.handler = async (event) => {
console.log('Event:', JSON.stringify(event));
return { statusCode: 200, body: "Hello, CDK Lambda" };
};
`),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_20_X,
role: lambdaRole,
description: `Updated at ${timeStamp}`, // <- タイムスタンプを含む description を追加
});
// Lambda のバージョンを作成
const sampleLambdaVersion = new lambda.Version(this, 'SampleLambdaVersion', {
lambda: sampleLambdaFunction,
description: `Version created at ${timeStamp}`, // <- タイムスタンプを含む description を追加
});
// 既存のバージョンが削除されないようにする
sampleLambdaVersion.applyRemovalPolicy(cdk.RemovalPolicy.RETAIN);
}
}
解説
元のコード
以下の CDK コードで作成された Lambda 関数があるとします。
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as iam from 'aws-cdk-lib/aws-iam';
export class SampleLambdaStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Lambda 実行用 IAM ロールを作成
const lambdaRole = new iam.Role(this, 'LambdaExecutionRole', {
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
});
lambdaRole.addManagedPolicy(
iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole')
);
const timeStamp = new Date().toLocaleString();
// Lambda 関数を作成
const sampleLambdaFunction = new lambda.Function(this, 'SampleLambdaFunction', {
code: lambda.Code.fromInline(`
exports.handler = async (event) => {
console.log('Event:', JSON.stringify(event));
return { statusCode: 200, body: "Hello, CDK Lambda" };
};
`),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_20_X,
role: lambdaRole,
});
// Lambda のバージョンを作成
const sampleLambdaVersion = new lambda.Version(this, 'SampleLambdaVersion', {
lambda: sampleLambdaFunction,
});
// 既存のバージョンが削除されないようにする
sampleLambdaVersion.applyRemovalPolicy(cdk.RemovalPolicy.RETAIN);
}
}
Lambda コードを修正
Lambda 本体のコード部分に対して適当に修正を加えます。
- return { statusCode: 200, body: "Hello, CDK Lambda" };
+ return { statusCode: 200, body: "Hello, CDK Lambda !!!" };
差分を見てみます。
cdk diff
Function
のみ更新扱いとなっていますね。
Stack SampleLambdaStack
Resources
[~] AWS::Lambda::Function SampleLambdaFunction SampleLambdaFunction430BF4AE
└─ [~] Code
└─ [~] .ZipFile:
├─ [-]
exports.handler = async (event) => {
console.log('Event:', JSON.stringify(event));
return { statusCode: 200, body: "Hello, CDK Lambda" };
};
└─ [+]
exports.handler = async (event) => {
console.log('Event:', JSON.stringify(event));
return { statusCode: 200, body: "Hello, CDK Lambda !!!" };
};
✨ Number of stacks with differences: 1
これだとこのままデプロイしても当然バージョンは作成されません。
CDK コードを修正
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as iam from 'aws-cdk-lib/aws-iam';
export class SampleLambdaStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Lambda 実行用 IAM ロールを作成
const lambdaRole = new iam.Role(this, 'LambdaExecutionRole', {
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
});
lambdaRole.addManagedPolicy(
iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole')
);
+ const timeStamp = new Date().toLocaleString();
// Lambda 関数を作成
const sampleLambdaFunction = new lambda.Function(this, 'SampleLambdaFunction', {
code: lambda.Code.fromInline(`
exports.handler = async (event) => {
console.log('Event:', JSON.stringify(event));
return { statusCode: 200, body: "Hello, CDK Lambda !!!" };
};
`),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_20_X,
role: lambdaRole,
+ description: `Updated at ${timeStamp}`, // <- タイムスタンプを含む description を追加
});
// Lambda のバージョンを作成
const sampleLambdaVersion = new lambda.Version(this, 'SampleLambdaVersion', {
lambda: sampleLambdaFunction,
+ description: `Version created at ${timeStamp}`, // <- タイムスタンプを含む description を追加
});
// 既存のバージョンが削除されないようにする
sampleLambdaVersion.applyRemovalPolicy(cdk.RemovalPolicy.RETAIN);
}
}
前述にもありますが、Lambda 関数にも description
を付けているのは、Lambda 関数コード以外部分のみの変更の場合、同じ断面で新しいバージョンを作成出来ない旨のエラーとなるための対策です。
上記でもう一度 cdk diff
確認
Stack SampleLambdaStack
Resources
[~] AWS::Lambda::Function SampleLambdaFunction SampleLambdaFunction430BF4AE
├─ [~] Code
│ └─ [~] .ZipFile:
│ ├─ [-]
exports.handler = async (event) => {
console.log('Event:', JSON.stringify(event));
return { statusCode: 200, body: "Hello, CDK Lambda" };
};
│ └─ [+]
exports.handler = async (event) => {
console.log('Event:', JSON.stringify(event));
return { statusCode: 200, body: "Hello, CDK Lambda !!!" };
};
└─ [+] Description
└─ Updated at 2024/12/6 20:07:38
[~] AWS::Lambda::Version SampleLambdaVersion SampleLambdaVersion22694689 replace
└─ [+] Description (requires replacement)
└─ Version created at 2024/12/6 20:07:38
✨ Number of stacks with differences: 1
Version
も差分が検出され更新対象となっているのが分かります。
デプロイすると
cdk deploy
おわりに
Lambda 関数自体に更新がなくてもバージョンが作成されるのはアレですが、バージョンを作成出来ないほうが問題だったので、他にも方法はあるかとは思いますが、このときは一番手っ取り早そうなこの方法で乗り切りました。
そもそも Lambda 関数の更新を検知して、良しなにバージョンを作成しれくれればありがたいんですけどね。。