チュートリアル: Lambda を使用するカスタムリソースで AMI ID を検索する
このチュートリアルでは、Lambda とカスタムリソースを使用して、CloudFormation テンプレートで Amazon マシンイメージ (AMI) ID を動的に検索する方法を説明します。これは、CloudFormation テンプレート内の AMI ID を更新する方法を効率化するのに役立ちます。
Amazon EC2 インスタンスを起動するための CloudFormation テンプレートを作成する際には、AMI ID を指定する必要があります。これは、インスタンスにインストールされるオペレーティングシステムとソフトウェア用のテンプレートのようなものです。正しい AMI ID は、インスタンスタイプと、インスタンスを起動する AWS リージョン によって異なります。これらの ID は定期的に変更される可能性があります (AMI が更新されてソフトウェアアップデートが追加されたときなど)。
通常、AMI ID を特定のインスタンスタイプとリージョンにマッピングするマッピングセクションで AMI ID を指定します。つまり、ID を更新するには、各テンプレートで ID を手動で変更する必要があります。ただし、カスタムリソースと Lambda を使用すると、使用しているインスタンスタイプとリージョンの最新の AMI の ID を取得する関数を作成できます。これにより、スタックテンプレートの AMI ID、インスタンスタイプ、リージョン間のマッピングを手動で管理する必要がなくなります。
このチュートリアルでは、カスタムリソースを作成して Lambda 関数を関連付け、AMI ID を参照する方法を示します。カスタムリソースと Lambda 関数に精通していることを前提としています。カスタムリソースとその仕組みの概要については、「カスタムリソース」を参照してください。Lambda の詳細については、[AWS Lambda デベロッパーガイド] を参照してください。
注記
CloudFormation は無料サービスです。ただし、スタックに追加する AWS リソース (Lambda 関数や EC2 インスタンスなど) にはそれぞれ、現在の料金が課金されます。AWS 料金に関する詳細については、http://aws.amazon.com
トピック
概要
次の手順では、この実装の概要を説明します。
-
Lambda 関数コードを含むサンプルパッケージを、EC2 インスタンスを作成する同じ AWS リージョン 内の Amazon S3 バケットに保存します。
-
サンプルテンプレートを使用して、カスタムリソース、Lambda 関数、EC2 インスタンス、および Lambda が Amazon EC2 への呼び出しに使用する IAM ロールを使用してスタックを作成します。
-
スタックは、Lambda 関数をカスタム リソースに関連付けます。スタックが作成されると、CloudFormation はこの関数を呼び出し、リクエストタイプ、入力データ、署名付き Amazon S3 URL などの情報を送信します。
-
Lambda 関数は、入力データを使用して最新の AMI ID を検索し、署名付き URL への応答として AMI ID を送信します。
-
CloudFormation は、署名付き URL の場所で応答を取得し、スタックの作成を続行します。CloudFormation は、インスタンスを作成するときに、Lambda 関数が提供する AMI ID を使用して、最新の AMI で EC2 インスタンスを作成します。
テンプレートのチュートリアル
サンプルテンプレート全体を確認するには、以下を参照してください。
次のスニペットは、Lambda 関数をカスタムリソースと関連付ける方法や、その関数の応答を使用する方法について理解するのに役立つサンプルテンプレートの関連する部分について説明しています。
AWS::Lambda::Function リソース AMIInfoFunction
AWS::Lambda::Function
リソースは、関数のソースコード、ハンドラー名、ランタイム環境、実行ロールの Amazon リソースネーム (ARN) を指定します。
-
Code
プロパティでは、サンプルパッケージをアップロードした Amazon S3 の場所 (バケット名とファイル名) を指定します。サンプルテンプレートでは、スタックの作成時に名前を指定できるように、バケット名とファイル名の設定には入力パラメーター ("Ref": "S3Bucket"
および"Ref": "S3Key"
) が使用されます。同様に、.zip
パッケージに含まれているソースファイル (JavaScript ファイル) の名前に対応するハンドラー名にも、入力パラメーター ("Ref": "ModuleName"
) が使用されます。ソースファイルは JavaScript であるため、ランタイムはnodejs18.x
として指定されます。 -
このチュートリアルで使用されているコードでは、関数の実行時間がデフォルト値の
3
秒を超えるため、タイムアウトは30
秒に設定されています。十分な長さのタイムアウトを指定しなかった場合、関数が完了する前に Lambda でタイムアウトが発生し、スタックの作成に失敗することがあります。 -
Role
プロパティは、Fn::GetAtt
関数を使用して、テンプレートの他の場所で宣言されたLambdaExecutionRole
実行ロールの ARN を取得します。
JSON
"AMIInfoFunction": { "Type": "AWS::Lambda::Function", "Properties": { "Code": { "S3Bucket": { "Ref": "S3Bucket" }, "S3Key": { "Ref": "S3Key" } }, "Handler": { "Fn::Join" : [ "", [{ "Ref": "ModuleName" },".handler"] ] }, "Runtime": "nodejs18.x", "Timeout": "30", "Role": { "Fn::GetAtt" : ["LambdaExecutionRole", "Arn"] } } }
YAML
AMIInfoFunction: Type: AWS::Lambda::Function Properties: Code: S3Bucket: !Ref S3Bucket S3Key: !Ref S3Key Handler: !Sub "${ModuleName}.handler" Runtime: nodejs18.x Timeout: 30 Role: !GetAtt LambdaExecutionRole.Arn
AWS::IAM::Role リソース LambdaExecutionRole
実行ロールは Lambda 関数に、AWS にログを送信して EC2 DescribeImages
API を呼び出すためのアクセス権限を付与します。
JSON
"LambdaExecutionRole": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": {"Service": ["lambda.amazonaws.com"]}, "Action": ["sts:AssumeRole"] }] }, "Path": "/", "Policies": [{ "PolicyName": "root", "PolicyDocument": { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": ["logs:CreateLogGroup","logs:CreateLogStream","logs:PutLogEvents"], "Resource": "arn:aws:logs:*:*:*" }, { "Effect": "Allow", "Action": ["ec2:DescribeImages"], "Resource": "*" }] } }] } }
YAML
LambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: "/" Policies: - PolicyName: root PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: arn:aws:logs:*:*:* - Effect: Allow Action: - ec2:DescribeImages Resource: "*"
Custom::AMIInfo リソース AMIInfo
Linux および Windows のテンプレートのどちらも、カスタムリソースは関連付けられた Lambda 関数を起動します。関数をカスタムリソースに関連付けるには、Fn::GetAtt
組み込み関数を使用して、ServiceToken
プロパティの関数の ARN を指定します。CloudFormation は、カスタムリソース宣言に含まれる追加のプロパティ (Region
や Architecture
など) を入力として Lambda 関数に送信します。Lambda 関数は、これらの入力プロパティの正しい名前と値を判断します。
JSON
"AMIInfo": { "Type": "Custom::AMIInfo", "Properties": { "ServiceToken": { "Fn::GetAtt" : ["AMIInfoFunction", "Arn"] }, "Region": { "Ref": "AWS::Region" }, "Architecture": { "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } } }
YAML
AMIInfo: Type: Custom::AMIInfo Properties: ServiceToken: !GetAtt AMIInfoFunction.Arn Region: !Ref "AWS::Region" Architecture: Fn::FindInMap: - AWSInstanceType2Arch - !Ref InstanceType - Arch
Windows の場合、カスタムリソースはインスタンスのアーキテクチャではなく Windows バージョンのバージョンを Lambda 関数に提供します。
JSON
"AMIInfo": { "Type": "Custom::AMIInfo", "Properties": { "ServiceToken": { "Fn::GetAtt": ["AMIInfoFunction", "Arn"] }, "Region": { "Ref": "AWS::Region" }, "OSName": { "Ref": "WindowsVersion" } } }
YAML
AMIInfo: Type: Custom::AMIInfo Properties: ServiceToken: !GetAtt AMIInfoFunction.Arn Region: !Ref "AWS::Region" OSName: !Ref "WindowsVersion"
CloudFormation によって Lambda 関数が呼び出されると、この関数は AWS リージョン とインスタンスのアーキテクチャまたは OS 名を使用してイメージのリストをフィルタリングし、EC2 DescribeImages
API を呼び出します。さらに、日付によってイメージのリストをソートし、最新 AMI の ID を返します。
関数では、最新 AMI の ID を返す際に、この ID を応答オブジェクトの Data
プロパティで署名付き URL に送信します。データは、次の例に示すように、名前と値のペアとして構造化されます。
"Data": { "Id": "ami-02354e95b3example" }
AWS::EC2::Instance リソース SampleInstance
次のスニペットは、Lambda 関数からのデータの取得方法を示しています。Fn::GetAtt
組み込み関数を使用して、取得するカスタムリソースの名前と使用する値の属性名を指定します。このチュートリアルでは、カスタムリソース名は AMIInfo
、属性名は Id
です。
JSON
"SampleInstance": { "Type": "AWS::EC2::Instance", "Properties": { "InstanceType" : { "Ref": "InstanceType" }, "ImageId": { "Fn::GetAtt": [ "AMIInfo", "Id" ] } } }
YAML
SampleInstance: Type: AWS::EC2::Instance Properties: InstanceType: !Ref InstanceType ImageId: !GetAtt AMIInfo.Id
前提条件
スタックを作成する手順を完了する前に、Amazon S3 バケットが予め存在している必要があります。Lambda 関数を使用してスタックを作成する際は、関数のソースコードが格納されている Amazon S3 バケットの場所を指定する必要があります。バケットは、スタックを作成するのと同じ AWS リージョン にある必要があります。バケットの作成の詳細については、「Amazon Simple Storage Service ユーザーガイド」の「Creating a bucket」(バケットの作成) を参照してください。
Lambda、Amazon EC2、CloudFormation などの対応するサービスをすべて使用するには、IAM アクセス許可も必要です。
ステップ 1: サンプル Lambda パッケージを Amazon S3 に保存する
このステップでは、サンプル Lambda パッケージ (.zip
ファイル) を Amazon S3 バケットにアップロードします。
このパッケージには、Lambda 関数のソースコードおよび必要なライブラリが含まれています。このチュートリアルでは、関数に追加のライブラリは必要ありません。
サンプルパッケージを Amazon S3 に保存するには
-
サンプルパッケージを Amazon S3 からダウンロードします。ファイルを保存する際には、サンプルと同じファイル名の
amilookup.zip
もしくはamilookup-win.zip
を使用してください。- Linux AMI の ID の検索
-
https://s3.amazonaws.com/cloudformation-examples/lambda/amilookup.zip
- Windows AMI の ID の検索
-
https://s3.amazonaws.com/cloudformation-examples/lambda/amilookup-win.zip
-
https://console.aws.amazon.com/s3/home
にある Amazon S3 コンソールを開きます。 -
画面の上部のナビゲーションバーで、Amazon S3 バケットを作成した AWS リージョンを選択します。
-
[バケット名] リストで、バケット名を選択します。バケット名は、スタックの作成時に使用するため、メモしておきます。
-
[アップロード] を選択します。
-
[アップロード] の [ファイルとフォルダー] で、サンプル パッケージをバケットにアップロードします。詳細については、Amazon Simple Storage Service 開発者ガイドの「オブジェクトのアップロード」を参照してください。
ステップ 2: スタックを起動する
このステップでは、サンプルテンプレートからスタックを起動します。スタックには、Lambda 関数、IAM ロール (実行ロール)、関数を呼び出すカスタムリソース、関数の結果を使用する EC2 インスタンスを使用します。
スタックの作成時、カスタムリソースは Lambda 関数を呼び出し、この関数によって応答が署名済み Amazon S3 の URL に送信されるまで待ちます。応答では、EC2 インスタンスタイプと、インスタンスを作成する AWS リージョン に対応する最新の AMI ID が返されます。関数の応答からのデータは、カスタムリソースの属性として保存されます。この属性は、EC2 インスタンスの AMI ID を指定するために使用されます。
スタックを作成するには
-
CloudFormation コンソール (https://console.aws.amazon.com/cloudformation/
) を開きます。 -
[Create Stack] (スタックの作成) を選択します。
-
[テンプレート] セクションで、[Amazon S3 テンプレート URL の指定)] を選択し、次の URL をコピーしてテキストボックスに貼り付けます。
- Linux のテンプレート
-
https://s3.amazonaws.com/cloudformation-examples/lambda/LambdaAMILookupSample.template
- Windows のテンプレート
-
https://s3.amazonaws.com/cloudformation-examples/lambda/LambdaAMILookupSample-win.template
-
[Next] を選択します。
-
[スタックの名前] フィールドに
SampleEC2Instance
と入力します。 -
[パラメータ] セクションで、作成した Amazon S3 バケットの名前を指定し、[次へ] を選択します。
他のパラメーターのデフォルト値は、サンプルの
.zip
パッケージで使用されている名前と同じです。 -
このチュートリアルでは、タグの追加も詳細設定の指定も不要です。[次へ] を選択します。
-
スタック名とテンプレート URL が正しいことを確認し、[作成] を選択します。
CloudFormation によってスタックが作成されるまでに数分かかることもあります。進捗状況を監視するには、スタックイベントを確認します。詳細については、「CloudFormation コンソールから CloudFormation スタック情報を表示する」を参照してください。
スタックの作成に成功した場合は、スタック内のすべてのリソース (Lambda 関数、カスタムリソース、EC2 インスタンスなど) も作成されています。Lambda 関数とカスタムリソースを正常に使用して EC2 インスタンスの AMI ID を指定できています。このテンプレートで AMI ID のマッピングを作成および維持する必要はありません。
どの AMI ID を使用して CloudFormation で EC2 インスタンスが作成されたか確認するには、スタック出力を確認します。
Lambda 関数からエラーが返される場合は、CloudWatch Logs コンソール
ステップ 3: リソースをクリーンアップする
不要なリソースに対して課金されないよう、作成したすべてのスタックリソースをクリーンアップするために、スタックを削除します。
スタックを削除するには
-
CloudFormation コンソールから、SampleEC2Instance スタックを選択します。
-
[アクション] を選択し、[スタックの削除] を選択します。
-
確認メッセージで、[はい、削除する] を選択します。
作成したすべてのリソースが削除されます。
これで、CloudFormation によって Lambda 関数を作成して使用する方法を理解できました。このチュートリアルのサンプルテンプレートおよびコードを使用して、他のスタックおよび関数を作成することができます。