[AWS CDK] AWS Lambda-backed カスタムリソースは、もう必要ない!(場合もある)

1 はじめに

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

AWS CloudFormation(以下、CFn)では、Lambda関数とカスタムリソースを関連付けて、CFnで記述できないリソースなどを扱うことが出来ます。

しかし、AWS CDK (Cloud Development Kit) は、TypeScript等の言語で記述されるため、AWS SDKをimportしたりして各種のリソースにアクセスする事も可能なので、わざわざ、テンプレートからLambdaを実行しなくても、条件によっては、必要な作業を完了させてしまうことができます。

AWSのドキュメントでは、AWS Lambda-backed カスタムリソースの例として、以下のチュートリアルが紹介されています。

チュートリアル: Amazon マシンイメージ ID を参照する

今回は、上記のような作業を、カスタムリソース無しでAWS CDKで書いてみました。

2 場合もある

最初に、誤解のないように記載させて頂きますが、タイトルにも書いたように 「場合もある」 ということに注意が必要です。

カスタムリソースでは、CFnで、Create/Update/Deleteの各ステージで作業内容を記述できますが、ここでやっていることは、あくまで、Create/Update時の処理であり、スタック削除で、削除が必要なリソースを扱うことはできません。

このような場合は、AWS CDKからでも、素直にカスタムリソースを使用しましょう。
参考:AWS CDK(Cloud Development Kit)を使用したカスタムリソースの利用

3 AWS CDK

下記がコードです。

チュートリアルでは、カスタムリソースのLamdaで処理されていた、最新のAMIのイメージIDを取得する処理は、getAmiId()という関数にしました。

getAmiId()は、awaitで呼び出す必要があるため、AWS CDKのコンストラクタ内で呼び出すことは出来ません。

そこで、CDKでリソースを生成する前にコールしてしまって、取得したイメージIDをCDKのコンストラクタに引数で送っています。

#!/usr/bin/env node
import 'source-map-support/register';

import * as aws from 'aws-sdk';
import cdk = require('@aws-cdk/core');
import ec2 = require('@aws-cdk/aws-ec2');

const amiList =  [
    {id:"PV64", namePattern: "amzn-ami-pv*x86_64-ebs" , owner: "amazon"},
    {id:"HVM64", namePattern: "amzn-ami-hvm*x86_64-gp2" , owner: "amazon"},
    {id:"HVMG2", namePattern: "amzn-ami-graphics-hvm*x86_64-ebs*" , owner: "679593333241"}
]

async function getAmiId(region: string, architecture: "PV64"|"HVM64"|"HVMG2"): Promise<string|undefined> {
    const ec2 = new aws.EC2({region: region});

    const ami = amiList.find(a => {return a.id == architecture})!
    const describeImagesParams = {
        Filters: [{ Name: "name", Values: [ami.namePattern]}],
        Owners: [ami.owner]
    };
    try {
        const data = await ec2.describeImages(describeImagesParams).promise();
        let images = data.Images;
        if(images){
            images.sort(function(x, y) { return y.Name!.localeCompare(x.Name!) });
            const target = images.find( i => { 
                return (i.Name!.toLowerCase().indexOf("beta") == -1 && i.Name!.toLowerCase().indexOf(".rc") == -1)
            } )
            if(target){
                console.log(JSON.stringify(target));
                return target.ImageId;
            }
        }
    } catch (err) {
        console.log(err);
    }
    return undefined;
}

export class CdkSampleEC2InstanceStack extends cdk.Stack {
    constructor(scope: cdk.Construct, id: string, amiId: string, props?: cdk.StackProps) {
        super(scope, id, props);
        new ec2.CfnInstance(this, "sample-instance" , { imageId: amiId })
    }
}

async function job(){
    // イメージIDの取得
    const amiId = await getAmiId("ap-northeast-1", "PV64");
    if (amiId) {
        // スタックの生成
        const app = new cdk.App();
        new CdkSampleEC2InstanceStack(app, 'sampleEC2Instance', amiId);
    }
}

job();

4 deploy

以下が、deployの様子です。スタックが作成前に、IMAの情報取得が終わっていることが確認できます。

先の条件では、本日(2019/8/16)時点の最新は、「Amazon Linux AMI 2018.03.0.20190611 x86_64 PV ebs」ということのようです。

5 最後に

CFnでカスタムリースとしてLambdaを呼び出すのは、ちょっと手間のかかる作業かも知れませんが、条件によっては、AWS CDKなら、もっと簡単に書けかも知れないという確認でした。