【実務・中級編】CloudFormation Registryを用いたプライベート拡張プロバイダーの自作:カスタムリソースを書かずに独自AWSリソースやSaaS連携をIaC化する裏技 – インフラ構成管理(IaC)活用バイブル

【SREの極み】CloudFormation Registryでプライベート拡張プロバイダーを自作し、カスタムリソース(Lambda地獄)を完全駆逐する裏技

こんにちは。大規模クラウドインフラの自動化とSREを統括しているテックリードだ。

日々のインフラ運用で、こんな絶望を味わったことはないだろうか?
「社内独自の認証基盤や、Terraformでは管理できてもCloudFormationだとSaaS連携(Datadog、PagerDuty、Auth0など)で公式リソースがない。仕方なく`AWS::CloudFormation::CustomResource`を切り、背後に汚いPython(Lambda)のボイラープレートをデプロイし、`urllib`でAPIを叩く泥臭いスクリプトを書いた……」

そのLambda、本当に正しくエラーハンドリングできているか? タイムアウト時にCloudFormationのスタックがROLLBACK_FAILEDで固まり、コンソールで手動リソース削除地獄(`NoEcho`や物理IDの迷子)に陥ったトラウマはないか?

もう、その「負債の温床」であるカスタムリソースはやめだ。
AWS公式が提供する CloudFormation Registry (CloudFormation CLI) を使えば、「独自のAWSリソース」や「サードパーティSaaS」を、完全な宣言的ネイティブ拡張プロバイダー(`MyCompany::SaaS::Project` のような形式)としてCloudFormationに組み込むことができる。

今回は、カスタムリソースの呪縛から解放され、真の冪等性と型安全を手に入れるための極限の知見を授けよう。

—

1. なぜ「カスタムリソース(Lambda連携)」を捨て、Registryを使うべきなのか?

従来の `Custom Resource` は、言ってみれば「場当たり的なスクリプトの押し付け」だ。比較表を見てほしい。

| 評価軸 | 従来のCustom Resource (Lambda) | CloudFormation Registry (拡張プロバイダー) |
| :— | :— | :— |
| スキーマ検証 | なし(JSON Schemaを自前で書くか、ノーチェック) | JSON Schemaによる厳格な型・構文チェック(パースエラーはデプロイ前に弾かれる) |
| 冪等性の担保 | Lambdaの実装依存(二重実行でバグりやすい) | リソースプロバイダーのライフサイクル(Create/Read/Update/Delete/List)で完全強制 |
| ドリフト検出 | 非対応(LambdaなのでステートをCloudFormationが追えない) | `aws cloudformation detect-stack-drift` で完全ネイティブ追跡 |
| エラーハンドリング | 独自実装のレスポンス(失敗時のロールバック制御が破綻しがち) | ProgressEventによる正確な非同期ステータスハンドリング |

カスタムリソースは、CloudFormationのライフサイクルエンジンから見れば「ブラックボックスの外部関数」に過ぎない。一方、Registryで作成したプロバイダーは、S3やEC2といったAWSネイティブのリソースと同等のファーストクラス市民としてCloudFormationに統合される。

—

2. 実践:CloudFormation CLIによるプライベート拡張プロバイダーの開発

ここからは、実際にプライベート拡張プロバイダー(例: 社内独自IPAMや外部SaaSを想定した `Acme::Networking::VpcPeering`)を構築する手順を、現場の知見を交えて解説する。

開発環境のセットアップ(プロの隠しコマンド)

まずは開発用CLIを導入する。Pythonの仮想環境を汚さないために、必ず独立した環境で行え。

必要なツールのインストール (Python 3.9+推奨)
pip install cloudformation-cli cloudformation-cli-python-plugin

動作確認
cfn –version

スキーマ定義(JSON Schema)の設計

プロバイダーの命はスキーマだ。`acme-networking-vpcpeering.json` を作成する。ここが厳格なバリデーションの源泉となる。

{
“typeName”: “Acme::Networking::VpcPeering”,
“description”: “社内ネットワーク基盤と外部VPCを接続するプライベートプロバイダー”,
“sourceUrl”: “https://github.com/my-company/acme-cloudformation-providers”,
“definitions”: {
“Tag”: {
“type”: “object”,
“properties”: {
“Key”: { “type”: “string” },
“Value”: { “type”: “string” }
},
“additionalProperties”: false,
“required”: [“Key”, “Value”]
}
},
“properties”: {
“VpcId”: {
“type”: “string”,
“pattern”: “^vpc-[0-9a-f]{8,17}$”,
“description”: “ローカル側のVPC ID”
},
“PeerVpcId”: {
“type”: “string”,
“pattern”: “^vpc-[0-9a-f]{8,17}$”,
“description”: “ピア先のVPC ID”
},
“Tags”: {
“type”: “array”,
“items”: { “$ref”: “#/definitions/Tag” },
“maxItems”: 50
}
},
“required”: [
“VpcId”,
“PeerVpcId”
],
“readOnlyProperties”: [
“/properties/VpcPeeringConnectionId”
],
“primaryIdentifier”: [
“/properties/VpcPeeringConnectionId”
],
“handlers”: {
“create”: { “permissions”: [“ec2:CreateVpcPeeringConnection”, “ec2:CreateTags”] },
“read”: { “permissions”: [“ec2:DescribeVpcPeeringConnections”] },
“update”: { “permissions”: [“ec2:ModifyVpcPeeringConnectionOptions”] },
“delete”: { “permissions”: [“ec2:DeleteVpcPeeringConnection”] },
“list”: { “permissions”: [“ec2:DescribeVpcPeeringConnections”] }
}
}

ボイラープレートの生成と実装

スキーマからハンドラーの骨組みを自動生成する。

cfn init –type resource
プロンプトでTypeName(Acme::Networking::VpcPeering)と言語(Python)を指定

生成された `handlers.py` に、実際のAPIロジック(Boto3などを用いた処理)を記述する。
ここでSREとして絶対に守るべきなのが、`ProgressEvent` を使った非同期処理と詳細な例外ハンドリングだ。

import logging
from cloudformation_cli_python_lib import (
Action,
HandlerErrorCode,
OperationStatus,
ProgressEvent,
Resource,
SessionProxy,
)

ロガーの設定(CloudWatch Logsに出力されるため構造化ログ推奨)
LOG = logging.getLogger(__name__)
LOG.setLevel(logging.INFO)

resource = Resource(type_name=”Acme::Networking::VpcPeering”)
test_entrypoint = resource.test_entrypoint

@resource.create_handler
def create(session: SessionProxy, request, callback_context) -> ProgressEvent:
model = request.desired_state
client = session.client(“ec2”)

try:
response = client.create_vpc_peering_connection(
VpcId=model.VpcId,
PeerVpcId=model.PeerVpcId
)
peering_id = response[“VpcPeeringConnection”][“VpcPeeringConnectionId”]
model.VpcPeeringConnectionId = peering_id

LOG.info(f”Successfully created VpcPeeringConnection: {peering_id}”)

return ProgressEvent(
status=OperationStatus.SUCCESS,
resource_model=model,
message=”Create complete”
)
except Exception as e:
LOG.error(f”Failed to create VpcPeeringConnection: {str(e)}”)
return ProgressEvent(
status=OperationStatus.FAILED,
errorCode=HandlerErrorCode.ServiceInternalError,
message=str(e)
)

read, update, delete, list も同様に実装する(省略)

アカウントへの登録(プライベートレジストリへの投入)

コードが書けたら、テストとビルドを行い、自分のAWSアカウントのプライベートレジストリに登録する。

バリデーション
cfn validate

ビルド
cfn generate
sam build

アカウントのプライベートレジストリへ登録(アカウントIDとリージョンは適宜変更)
cfn submit –region ap-northeast-1 –verbose

これで、このAWSアカウント(またはOrganizationsの全アカウント)内において、あなたの自作リソースがネイティブプロバイダーとして鎮座することになる。

—

3. 実用:CloudFormationテンプレートでの宣言的管理

登録が完了すれば、あとは通常のテンプレートから呼び出すだけだ。LambdaのARNを指定するような泥臭さは一切ない。

AWSTemplateFormatVersion: ‘2010-09-09’
Description: ‘プライベート拡張プロバイダーのテストスタック’

Resources:
MyPrivatePeering:
Type: ‘Acme::Networking::VpcPeering’
Properties:
VpcId: ‘vpc-0123456789abcdef0’
PeerVpcId: ‘vpc-0fedcba9876543210’
Tags:

  • Key: ‘Environment’

Value: ‘Production’

  • Key: ‘ManagedBy’

Value: ‘CloudFormation’

Outputs:
PeeringConnectionId:
Description: ‘生成されたVPCピアリングID’
Value: !GetAtt MyPrivatePeering.VpcPeeringConnectionId

これをデプロイすれば、CloudFormationのイベントストリームに `Acme::Networking::VpcPeering` の作成プロセスが美しく流れる。ドリフト検出も、スタック削除時のロールバックも、すべてCloudFormationのエンジンがネイティブに制御してくれる。

—

4. プロのチーム開発・生産性向上のための知見

この手法を組織にスケールさせるための、SRE直伝のベストプラクティスを授ける。

1. AWS CloudFormation Registry 組織全体での共有 (`AWS::Organizations::Organization`)
プライベートレジストリに登録したプロバイダーは、AWS Organizationsの機能を使って、全アカウントに自動でパブリッシュ(ExtensionのPublisher設定)できる。これにより、全社共通のSaaS連携プロバイダーを中央管理チームが一元メンテし、各プロダクトチームはテンプレートから呼び出すだけという理想郷が完成する。

2. CI/CDパイプラインへの組み込み
プロバイダーのスキーマ変更やPythonコードの改修は、GitHub Actions等のCIで `cfn validate` と単体テスト(`cfn test`)を必ず通すこと。テストコンテナがローカルでモック環境を立ち上げてライフサイクルを検証してくれる。

3. VS Codeの拡張機能・スニペットの共有
自作プロバイダーのJSON SchemaをVS Codeの `yaml.schemas` 設定に紐付けておけ。これだけで、開発者のエディタ上で自動補完(IntelliSense)とリアルタイムバリデーションが効くようになり、タイポによるデプロイ失敗が劇的にゼロになる。

—

結びにかえて

カスタムリソース(Lambda連携)は、拡張性の低いインフラにおける「最後の逃げ道」であり、同時に技術的負債の温床だ。
CloudFormation Registryを使いこなせば、自社製システムであれ、ニッチなSaaSであれ、すべてを「厳格なスキーマを持つファーストクラスのIaCリソース」へと昇華させることができる。

「IaCのコードは、美しく、宣言的で、信頼できるものでなければならない。」

この知見をあなたの現場に持ち帰り、泥臭いカスタムリソースを今すぐ駆逐してほしい。健闘を祈る。

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