概要

CloudFormationが標準でサポートしていないリソースや処理を、Lambda関数やSNSトピックを使って実装する仕組み

出典: Create custom provisioning logic with custom resources - AWS CloudFormation

Custom resources in AWS CloudFormation allow users to write custom provisioning logic into CloudFormation templates and manage related resources in a single stack.

ユースケース

  • CloudFormationが未対応のAWSリソースの作成
  • 外部APIの呼び出し
  • 動的な値の計算・取得
  • 既存リソースの情報取得

処理フロー

CloudFormation                    Lambda                         S3 (CFn専用バケット)
     |                              |                            |
     |-- 1. Lambda呼び出し -------->|                            |
     |   イベントJSON送信            |                            |
     |   - RequestType: Create      |                            |
     |   - ResponseURL:             |                            |
     |     https://cloudformation-custom-resource-response-<region>.s3-<region>.amazonaws.com/...
     |     ↑ S3の署名付きURL                                      |
     |   - ResourceProperties: ...  |                            |
     |                              |                            |
     |   ※非同期呼び出しのため       |-- 2. 処理実行               |
     |     戻り値は受け取れない      |                            |
     |                              |                            |
     |                              |-- 3. HTTP PUT ------------->|
     |                              |   ResponseURL(=S3)に対して |
     |                              |   結果JSONを送信             |
     |                              |   - Status: SUCCESS         |
     |                              |   - Data: {計算結果}         |
     |                              |                            |
     |<-- 4. ポーリングで読み取り ----------------------------- --|
     |   S3に保存されたJSONを取得                                 |
     |                                                           |
     |-- 5. スタック操作続行                                      |
     |   Status=SUCCESSなら次へ                                   |
     |   Status=FAILEDならロールバック                            |

なぜS3を経由するのか

CloudFormationはLambda関数を非同期で呼び出すため、Lambda関数の戻り値を直接受け取ることができない

出典: Using AWS Lambda with CloudFormation

CloudFormation invokes the Lambda function asynchronously with an event that includes a callback URL. The function is responsible for returning a response to the callback URL indicating success or failure.

この制約を解決するため、CloudFormationはResponseURL(S3の署名付きURL)をイベントに含めてLambda関数に渡す

Lambda関数は処理完了後、このURLにHTTP PUTで結果JSONを送信する

実際にはS3バケットにJSONファイルがアップロードされ、CloudFormationがそれを読み取る

S3バケット

レスポンス送信先のS3バケットはCloudFormationが各リージョンに用意しており、ユーザーが作成する必要はない

出典: Access CloudFormation using an interface endpoint (AWS PrivateLink)

CloudFormation has S3 buckets in each Region to monitor responses to a custom resource request

バケット名の形式は cloudformation-custom-resource-response-<region>(リージョン名からハイフンが除去される)

このバケットはCloudFormation専用のため、ユーザーが直接アクセスして中身を確認することはできない

署名付きURL

CloudFormationがLambda関数に渡すイベントに含まれる。署名付きURLには認証情報が埋め込まれているため、Lambda関数はIAM権限なしでS3にPUTできる

構造:

部分意味
ホストCloudFormation専用S3バケット
パススタックID、リソースID、リクエストIDを組み合わせたオブジェクトキー
X-Amz-Expires署名の有効期限(秒)。7200秒 = 2時間
X-Amz-Signature署名。この署名があることでPUT操作が許可される

出典: CloudFormation custom resource request and response reference

The response URL identifies a presigned S3 bucket that receives responses from the custom resource provider to CloudFormation.

リクエストオブジェクト

CloudFormationからLambda関数に送信されるJSONの構造

フィールド説明
RequestTypeCreate, Update, Deleteのいずれか
ResponseURLレスポンス送信先の署名付きURL(S3)
StackIdスタックのARN
RequestIdリクエストの一意識別子
ResourcePropertiesテンプレートで定義したプロパティ

レスポンスオブジェクト

Lambda関数が署名付きURLにHTTP PUTで送信するJSON

フィールド説明
StatusSUCCESSまたはFAILED。CloudFormationはこの値でスタック操作を続行するかロールバックするかを決定する
PhysicalResourceIdリソースの物理ID。更新時にこの値が変わるとCloudFormationはリソース置換と判断し、古いリソースの削除を試みる
DataFn::GetAttで取得可能な値。他のリソースに渡すデータをここに格納する

出典: CloudFormation custom resource request and response reference

The custom resource provider sends a response to the pre-signed URL for all request types. If the custom resource provider doesn’t send a response, CloudFormation waits until the operation times out.

cfn-responseモジュール

Lambda関数のコードをZipFileプロパティで記述する場合、cfn-responseモジュールが自動的に利用可能になる。このモジュールは署名付きURLへのHTTP PUT送信を内部で行ってくれる

出典: cfn-response module - AWS CloudFormation

The cfn-response module is a library that simplifies sending responses to the custom resource that invoked your Lambda function.

cfn-responseを使わない場合は、urllib.request等でResponseURLにHTTP PUTを送信する必要がある

署名付きURLには認証情報が埋め込まれているため、Lambda関数のIAMロールにS3への書き込み権限は不要

タイムアウト

カスタムリソースのデフォルトタイムアウトは3600秒(1時間)。Lambda関数がレスポンスを返さない場合、CloudFormationはこの時間待機し続ける

出典: AWS::CloudFormation::CustomResource

The ServiceTimeout property sets the maximum time, in seconds, that can elapse before a custom resource operation times out. The default value is 1 hour (3600 seconds).

Lambda関数内でエラーが発生した場合でも、必ずレスポンスを返すようにエラーハンドリングを実装する必要がある

参考資料

試してみる