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

2019.08.16

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なら、もっと簡単に書けかも知れないという確認でした。