AWS CDK(Cloud Development Kit)を使用したカスタムリソースの利用

2019.08.03

1 はじめに

CX事業本部の平内(SIN)です。

CloudFormationのリソースタイプとして用意されていないものは、カスタムリソースを使用します。

カスタムリソースでlambdaを使用すれば、ちょっと、トリッキーなので注意が必要ですが、たいがい何でもできてしまいます。

今回は、AWS CDKでカスタムリソース(Lambda)を使用する方法を確認してみました。

ちなみに、lambdaは、S3に置く方法とインラインで記述する方法の2種類がありますが、カスタムリソースの応答オブジェクトがcfn-responseモジュールで簡単に返せるインラインでの設置となってます。

2 スタック

スタック生成のコードです。

カスタムリソースは、cfn.CustomResource()で作成できます。providerにインラインのLambda、そして、パラメータは、propertiesから送ることができます。

cdk_custom_resource_lambda.ts

import cdk = require('@aws-cdk/core');
import cfn = require('@aws-cdk/aws-cloudformation');
import lambda = require('@aws-cdk/aws-lambda');
import fs = require('fs');

export class CdkCustomResourceStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const customResourse = new cfn.CustomResource(this, 'function', {
      provider: cfn.CustomResourceProvider.lambda(new lambda.SingletonFunction(this, 'singleton', {
        functionName: "customResourceLambdaFunction",
        uuid: '7731874a-3a77-80d5-cd8e-6350510edf90',
        code: new lambda.InlineCode(fs.readFileSync('lambda/index.js', { encoding: 'utf-8' })),
        handler: 'index.handler',
        timeout: cdk.Duration.seconds(300),
        runtime: lambda.Runtime.NODEJS_8_10
      })),
      properties: {"Message": "bod boy"}
    });
    new cdk.CfnOutput(this, "response", {
      value: customResourse.getAtt('Response').toString()
    });
  }
}

const app = new cdk.App();
new CdkCustomResourceStack(app, 'CdkCustomResourceLambdaStack');

カスタムリソースに送ったパラメータを、再び戻り値で受け取って出力しています。

3 カスタムリソース

カスタムリソースのLambdaです。

cfn-responseモジュールを使用して、レスポンスを生成しています。

CDKからのパラメータは、 event['ResourceProperties'] で受け取り、send()の第4パラメータで戻り値を返すことができます。

すいません、今回は試していませんが、event['RequestType'] で、Create/Delete/Updateに対応した処理も記述可能のようです。

"use strict";
const cfnresponse = require('cfn-response');

exports.handler = (event, context) => {
    console.log(JSON.stringify(event));
    console.log(JSON.stringify(context));

    const params = event['ResourceProperties']; // Papameters
    const _requestType = event['RequestType']; // Create, Delete, Update
    try {
        const data = {
            'Response': 'Your message: ' + params.Message
        };
        cfnresponse.send(event, context, cfnresponse.SUCCESS, data);
    }
    catch (error) {
        console.log(error);
        cfnresponse.send(event, context, cfnresponse.FAILED, {});
    }
};

4 サンプル

サンプルをGitHubに置きました。名前等の競合がなければ、下記の手順で利用可能です。

  • ダウンロード
$ git clone https://github.com/furuya02/CdkCustomResourceLambda.git
$ cd CdkCustomResourceLambda
  • デプロイ
$ yarn
$ tsc
$ cdk synth
$ cdk deploy
Do you wish to deploy these changes (y/n)? y
  • スタックの削除
cdk destroy

5 注意

今回、色々試していて気がついた点を列挙させて下さい。

これは、AWS CDKに限った話では無いですが、カスタムリソースでLambdaを使用する場合、下記の点に注意すると作業がスムーズに進むかも知れません。

  • インラインで記述するので、特別なモジュールの読み込みはできない
  • Lambdaの定義は、inlineで行う code: new lambda.InlineCode(fs.readFileSync()
  • AWS CDKからパラメータで送ったキー名が、ラージキャメルに変更される(ログから渡されているパラメータを確認したほうが良い)
  • カスタムリソースのLambdaは、確実に応答オブジェクトを返す send()
  • カスタムリソースのLambdaは、asyncで起動すると、応答オブジェクトが返る前にLambdaが終了するので正常にスタックが作成されない(CREATE_IN_PROGRESSで止まる)
  • send()メソッドの第4パラメータは、Deleteでも必要
  • カスタムリソースのLambdaでNode10.xは使えない
  • cdkの下の.gitignoreには、.jsが入っているので、インライン用のjsを対象外にしておかない、Githubへのpushで洩れる
  • CREATE_IN_PROGRESSで止まってしまったら、削除して1時間ぐらい放置する(すぐには消せない) [2019/08/03]↑こちらについては、下記で対策可能であると@toriclsさんから教えて頂きました。
    https://aws.amazon.com/jp/premiumsupport/knowledge-center/cloudformation-lambda-resource-delete/

6 最後に

今回は、カスタムリソースをAWS CDKで書いてみました。 失敗して、CREATE_IN_PROGRESSのまま返ってこなくなると、悲しくなります。