Lambda-backed カスタムリソースを理解するためにチュートリアルをやってみた

Lambdaを使ったカスタムリソース[Lambda-backed カスタムリソース]のチュートリアルでカスタムリソースへの理解を深めてみました。まだCloudFormationを使っているけどカスタムリソースはまだという人は一度やってみてはいかがでしょうか。
2021.08.11

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

CloudFormationでリソースを定義する際、対応していないリソースがあったりパラメータではなく自動で値を取得したいと考えたことはないでしょうか?

そんなときに柔軟に対応してくれるのがカスタムリソースと呼ばれる定義方法です。

これまではカスタムリソースは使わずにCloudFormationテンプレートを利用していましたが、ついに使う機会がきたのでチュートリアルで理解を深めていこうと思います。

カスタムリソースには紐づけるリソースとしてSNSとLambdaの2種類がありますが、今回はLambdaを使ったLambda-backed カスタムリソースを使っていきます。

やってみる

それでは早速チュートリアルをやっていきます。今回実施するのはAMIのIDを自動取得するLambdaをカスタムリソースとして作成して、取得したAMI IDからEC2インスタンスを起動するというものです。

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

チュートリアルで実施するイメージとしては、CloudFormationテンプレート内で以下のようなことを実行します。

  1. S3からLambdaのコードを取得
  2. カスタムリソースCustom::AMIInfoからLambdaを実行
  3. LambdaからAMI IDをレスポンスとして返す
  4. 取得したAMI IDをEC2が参照し起動

ステップ 1: サンプルパッケージをダウンロードして Amazon S3 に保存する

サンプルとしてLambda関数を作成するためのパッケージをダウンロードします。WindouwとLinuxを選べるのですが、今回はLinux AMI の ID の検索するパッケージを以下からダウンロードしました。

https://s3.amazonaws.com/cloudformation-examples/lambda/amilookup.zip

次にS3バケットを作成します。バケット名はお好きなもので大丈夫です。リージョンはスタックを作成する予定のリージョンに作成するようにしてください。

その他の設定はデフォルトで作成して、先ほどダウンロードしたサンプルのzipファイルをアップロードします。

ここでアップロードしたファイルをCloudFormationテンプレートから呼び出して利用していきます。

ステップ 2: スタックを作成する

以下からスタック作成用のテンプレートをダウンロードします。

https://s3.amazonaws.com/cloudformation-examples/lambda/LambdaAMILookupSample.template

テンプレートの確認

そのままテンプレートからスタックを作成する前にテンプレートを理解しておきます。ダウンロードできるテンプレートはjsonですが、分かりにくいのでyamlに変換して確認しています。

AMIInfoFunction:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        S3Bucket: !Ref "S3Bucket"
        S3Key: !Ref "S3Key"
      Handler: !Join
        - ""
        - - !Ref "ModuleName"
          - .handler
      Role: !GetAtt "LambdaExecutionRole.Arn"
      Runtime: nodejs12.x
      Timeout: "30"

Lambdaが定義されているところを抜粋しています。先ほどアップロードしたS3バケットがCodeプロパティの!Ref "S3Bucket"で定義されていることが確認できます。

次にカスタムリソースの定義されているところはType:Custom::AMIInfoです。カスタムリソースの場合はTypeでCustomと定義する必要があるので覚えておきましょう。

AMIInfo:
    Type: Custom::AMIInfo
    Properties:
      ServiceToken: !GetAtt "AMIInfoFunction.Arn"
      Region: !Ref "AWS::Region"
      Architecture: !FindInMap
        - AWSInstanceType2Arch
        - !Ref "InstanceType"
        - Arch

ServiceTokenではLambdaのArnを取得していて、CloudFormationによってLambdaが呼び出されます。このLambdaが呼び出されると、Lambda関数がEC2 DescribeImages APIを呼び出し、最新のAMI IDを返してくれます。

返してくれるAMI IDの形は以下の通りです。

"Data": { 
  "Id": "ami-43795473"
}

上記のレスポンスを取得するときにはFn::GetAttを使います。レスポンスを取得しているのがSampleInstanceの部分です。

SampleInstance:
  Type: AWS::EC2::Instance
  Properties:
    InstanceType: !Ref InstanceType
    ImageId: !GetAtt AMIInfo.Id

AMIInfo のIdを取得するとカスタムリソースのレスポンスで取得したAMI IDを自動で設定することができます。

スタックの作成

テンプレートを確認したところで、スタックを作成していきましょう。S3バケットと同じリージョンにすることを忘れないようにしてください。

スタックの作成からテンプレートにS3のURLを指定します。S3のURLは以下を利用してください。

https://s3.amazonaws.com/cloudformation-examples/lambda/LambdaAMILookupSample.template

スタック名にSampleEC2Instanceを入力し、S3Bucketにはステップ1で作成したバケット名を入力してください。

その他の値についてはデフォルトのまま作成します。スタックの作成が完了したら、出力タブにAMI IDが確認できます。

ステップ 3: リソースをクリーンアップする

お金がかからないようクリーンアップを忘れずに。料金は起動したEC2にかかるので作成したスタックを削除してください。作成したS3バケットも不要な場合は削除しておきましょう。

まとめ

チュートリアルを通してLambda-backed カスタムリソースを試してみました。意外とテンプレートの定義自体はそこまで難しくないので、Lambdaのコードをどう記述してレスポンスを返すかを考えれば活用できそうですね。

まだカスタムリソースが難しそうだと思っている人はこのチュートリアルからカスタムリソースを体験してみてはいかがでしょうか?