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

2019.08.16

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

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