0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CDK で Lambda バージョンが作成されない問題対応の一例

Posted at

はじめに

AWS CDK を使って Lambda 関数をデプロイしている際に、Lambda バージョンが正しく作成されない問題に直面しました。ワークアラウンド的ではありますが、そのときの解決策を備忘録としてまとめておきます。

検証環境

  • aws-cdk: 2.171.1
  • typescript: 5.6.3

いきなり結論

FunctionVersion にタイムスタンプ付きの 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

ちゃんとバージョンが作成されているのが確認出来ました!!
1.png

おわりに

Lambda 関数自体に更新がなくてもバージョンが作成されるのはアレですが、バージョンを作成出来ないほうが問題だったので、他にも方法はあるかとは思いますが、このときは一番手っ取り早そうなこの方法で乗り切りました。

そもそも Lambda 関数の更新を検知して、良しなにバージョンを作成しれくれればありがたいんですけどね。。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?