【テクニカル・上級編】CloudFormation Custom Resources(カスタムリソース)を活用して標準外のリソースを制御する – インフラ構成管理(IaC)活用バイブル

CloudFormation Custom Resources:IaCの限界を突破する「最後の切り札」を使いこなせ

多くのエンジニアが「Infrastructure as Code (IaC)」を語るとき、彼らは既存のAWSリソースの定義に終始する。しかし、真のSREは知っている。「AWSが提供するネイティブなリソース定義だけでは、システムの心臓部は決して完結しない」という冷徹な事実を。

CloudFormation Custom Resourcesは、単なる機能ではない。それは、AWSのAPIが直接サポートしていないリソースや、複雑なビジネスロジックを伴うセットアップを、宣言的なIaCのサイクルの中に強制的にねじ込むための「究極のフック」だ。

本稿では、この深淵に踏み込み、単なる「Lambdaを動かす」段階を超えた、本番環境で生き残るためのカスタムリソース実装術を解き明かす。

—

1. カスタムリソースの本質:IaCの抽象化レイヤーを破壊する

カスタムリソースとは、CloudFormationのスタック作成・更新・削除のイベントをLambdaに投げ込み、その応答を待つことでリソースのライフサイクルを制御する仕組みだ。

これが必要になるのは、以下のような「AWSのAPI外」にある制御が必要なケースである。

  • 動的な外部システムとの同期: Auth0などの外部IDプロバイダーへの設定、あるいはオンプレミス側のスイッチング。
  • 初期化を要するリソース: RDS作成後のテーブル生成、あるいはElasticsearch(OpenSearch)のインデックスマッピング適用。
  • 高度な条件判定: 特定のタグ付けルールに基づいたリソースの動的な再配置や、クロスアカウントの複雑な権限設定。

これらを「手動のスクリプト」で実行しているならば、それはIaCの敗北である。カスタムリソースを使えば、これら全てを`CREATE/UPDATE/DELETE`という冪等なサイクルに収束させることができる。

—

2. 実装の極致:Lambda連携とライフサイクル制御

カスタムリソースのLambda実装において、最も避けるべきは「適当な実装」だ。CloudFormationは`cfn-response`モジュールを推奨するが、プロダクション環境では自前のレスポンスロジックを実装せよ。

なぜか?`cfn-response`はエラー時にスタックをハングさせるリスクがあり、デバッグ情報が極めて乏しいからだ。

堅牢なLambda実装のアーキテクチャ

import json
import logging

ロガーの初期化は必須。CloudWatchでの追跡効率を最大化する
logger = logging.getLogger()
logger.setLevel(logging.INFO)

def handler(event, context):
try:
request_type = event[‘RequestType’]
# リソースのユニークID(PhysicalResourceId)を適切に管理せよ
# 削除時に必要となるキーをResourcePropertiesから抽出する

if request_type == ‘Create’:
return on_create(event)
elif request_type == ‘Update’:
return on_update(event)
elif request_type == ‘Delete’:
return on_delete(event)

except Exception as e:
logger.error(f”Execution failed: {str(e)}”)
# 失敗時は必ずStatus: FAILEDを返すこと。さもなくばスタックが1時間ハングする
send_response(event, context, ‘FAILED’)

def send_response(event, context, status, reason=None):
# ここでPre-signed URLに対してHTTP PUTを行う自前のロジックを実装
# タイムアウトとリトライ戦略をここで定義するのがプロの仕事
pass

—

3. 実践:データベース初期化とAPI呼び出しのハック

例えば、Auroraのテーブル初期化をカスタムリソースで行う場合、単にクエリを投げるだけではいけない。「冪等性の保証」と「接続プールの最適化」が鍵となる。

  • 冪等性の担保: `CREATE TABLE IF NOT EXISTS`を必ず使い、実行前にスキーマのバージョンテーブルをチェックせよ。
  • メモリとタイムアウト: 複雑なDDLを流す場合、Lambdaのメモリ割り当ては最低でも512MB以上を確保せよ。DB接続のハンドシェイク中にメモリ不足でOOM Killerに殺されるのは、最も恥ずべき障害の一つだ。
  • 疎結合な設計: DBのパスワードは直接渡さず、SSM Parameter StoreまたはSecrets Managerから取得する設計をLambda内で完結させること。

—

4. エラーハンドリングとタイムアウトの深淵

カスタムリソースの最大の敵は「スタックのスタック(スタックロック)」である。Lambdaがタイムアウトした際、CloudFormationは状態を確定できず、スタックが「UPDATE_ROLLBACK_FAILED」などの地獄に陥る。

これを防ぐための「鉄則」:

1. タイムアウトは二重管理せよ:

  • Lambdaのタイムアウト値(例: 2分)よりも短い時間(例: 1分45秒)で、プログラム内部のタイムアウト処理を走らせ、適切に`FAILED`を返す。

2. 冪等な削除処理:

  • `Delete`イベントは、「対象が存在しなくてもエラーにしない」設計にせよ。既に削除されているリソースに対して削除コマンドを投げても、それは成功とみなすべきだ。

3. PhysicalResourceIdの固定:

  • カスタムリソースの更新時、`PhysicalResourceId`を変更するとリソースが再作成(削除→作成)される。これが望ましくない場合は、リソースのIDを固定し、更新処理のみを走らせる設計を徹底せよ。

—

結びに代えて:IaCの先にある世界

カスタムリソースを極めることは、AWSの制約を「拡張可能なAPI」に変えることと同義だ。

あなたが構築すべきは、単なるリソースの羅列ではない。インフラの変更がコードによって完全に管理され、たとえ標準外の制御が必要であっても、一つのボタンで全環境が整合性を保ってデプロイされる「自己修復するシステム」だ。

技術を単なるツールとして使うな。インフラそのものをコードで制御し尽くすという強い意志を持って、CloudFormationの限界を塗り替えていけ。その先には、エンジニアとして唯一無二の景色が待っている。

タイトルとURLをコピーしました