【実務・中級編】巨大化しすぎたCloudFormationテンプレートの分割・移行アンチパターン:既存スタックの切り離しとインポートの全手順 – インフラ構成管理(IaC)活用バイブル

巨大化しすぎたCloudFormationテンプレートの分割・移行アンチパターン:既存スタックの切り離しとインポートの全手順

こんにちは。テックリードの私だ。
今日も君のプロジェクトのGitリポジトリを覗いてみようか。数千行に膨れ上がり、誰も全体像を把握できない「一枚岩(Monolithic)のCloudFormationテンプレート」、そしてデプロイのたびに数十分の沈黙を生み出す`UPDATE_IN_PROGRESS`のステータスに絶望していないだろうか?

「とりあえずVPCもECSもRDSもIAMも全部一つのファイルに書こう」――その魔の手に屈した瞬間から、インフラのスケーラビリティは死を迎える。1つのリソースの変更が、関係のないスタック全体の更新を引き起こし、最悪の場合は予期せぬリソースの置換(Replacement)によって本番環境が吹き飛ぶ。

今回は、サービス停止(Downtime)をゼロにし、既存の巨大スタックから安全にリソースを切り離してモジュール化する「極限の移行術」を、現場の修羅場をくぐり抜けてきた知見とともに授けよう。

—

1. 開発スピードを極限まで高める:VSCodeプロフェッショナル設定

巨大なテンプレートの分割・リファクタリングを制するには、エディタの機動力がすべてだ。手動でインデントを数えたり、リソースの論理名を目視で追うような愚行は今すぐやめろ。

絶対に入れるべき神プラグイン

1. AWS CloudFormation Linter (cfn-lint): 文法エラーだけでなく、AWSのベストプラクティスに反する記述をリアルタイムで検知する。
2. YAML / Ansible support: インデントの崩壊を防ぎ、高度な折りたたみ(Folding)を実現する。
3. AWS Toolkit for Visual Studio Code: テンプレートのバリデーションやスタックの状態をエディタ内から直感的に把握できる。

チームで統一すべき `.vscode/settings.json` のベストプラクティス

以下の設定をプロジェクトのルートに強制し、チーム全体のコード品質を機械的に担保しろ。

{
“editor.tabSize”: 2,
“editor.insertSpaces”: true,
“editor.formatOnSave”: true,
“files.associations”: {
“.yaml”: “yaml”,
“.yml”: “yaml”,
“template..yaml”: “cloudformation”
},
“yaml.schemas”: {
“https://raw.githubusercontent.com/awslabs/cosign/main/…”: “/”,
“https://raw.githubusercontent.com/aws-cloudformation/cfn-lint/main/conf/cfn-lint-json-schema.json”: “.yaml”
},
“yaml.format.enable”: true,
“yaml.validate”: true
}

—

2. 移行の現場で陥る「3大アンチパターン」

リソースを分割する際、多くのエンジニアが以下の罠にハマり、本番障害を引き起こしている。

アンチパターン A: リソースの削除と再作成(The Delete-and-Recreate Trap)

「テンプレートからリソースを消して、新しいテンプレートで作り直せばいいや」――これ犯罪級のミスだ。RDSやS3をこれをやると、データが物理的に消滅する。リソースのライフサイクルをCloudFormationに握らせたままでの力技の移行は厳禁である。

アンチパターン B: 循環参照の呪い(The Circular Dependency Loop)

VPCスタックがECSスタックを参照し、ECSスタックがVPCスタックのセキュリティグループを参照する……。`Fn::ImportValue`でこれをやると、スタック間の依存関係がループし、更新も削除もできないデッドロック状態(`UPDATE_ROLLBACK_FAILED`の沼)に陥る。

アンチパターン C: 依存関係の暗黙的結合(Implicit Dependency)

物理リソース名(NameタグやハードコードされたID)に依存し、CloudFormationのグラフ構造外で結合している状態。テンプレートを分割した瞬間に、依存関係の解決順序が崩れ、デプロイがランダムに失敗するようになる。

—

3. サービス停止ゼロ:既存スタックからリソースを安全に切り離す「4ステップ移行術」

ここからが本題だ。数千行の `Monolithic-Stack` から、例えば `Database-Stack` を安全に切り離す手順を、AWSの Resource Import 機能と `Fn::ImportValue` を駆使して完全解説する。

前提条件

  • 移行対象:`Monolithic-Stack` 内で稼働中の RDS SubnetGroup および RDS DBInstance。
  • 目標:既存リソースを一切停止・削除せず、新規作成する `Database-Stack` へ安全に所有権(Ownership)を移管する。

—

ステップ1: 移行元テンプレートからの「安全な除外」 (`Retain` の活用)

まずは、既存の `Monolithic-Stack` から切り離したいリソースを削除する。ただし、そのまま消すとAWSリソースが消滅するため、`DeletionPolicy: Retain` を付与してCloudFormationの管理下から外すのが鉄則だ。

修正前の `Monolithic-Stack.yaml` (一部抜粋):

Resources:
MyDatabase:
Type: AWS::RDS::DBInstance
Properties:
DBInstanceIdentifier: production-db
# … 複雑な設定 …

修正後の `Monolithic-Stack.yaml`:

Resources:
MyDatabase:
Type: AWS::RDS::DBInstance
DeletionPolicy: Retain # ← これが命綱。スタックから切り離しても物理リソースは残る
Properties:
DBInstanceIdentifier: production-db
# …

この状態で `aws cloudformation update-stack` を実行する。
実行後、スタックのイベントログでリソースが「削除(実際には保持)」されたことを確認せよ。これで物理リソースはフリーの状態になり、どのテンプレートからもインポート可能な状態になる。

—

ステップ2: 新規テンプレートの作成と `Resource Import` の定義

次に、切り出したリソース専用の `Database-Stack.yaml` を作成する。ここで重要なのは、新規作成(`Create`)ではなく、既存リソースのインポート(`Import`)を前提とした記述にすることだ。

AWSTemplateFormatVersion: ‘2010-09-09’
Description: ‘Extracted Database Stack with Import Support’

Resources:
MyDatabase:
Type: AWS::RDS::DBInstance
DeletionPolicy: Retain
Properties:
DBInstanceIdentifier: production-db # 既存の物理名と完全に一致させること
AllocatedStorage: 50
DBInstanceClass: db.t3.medium
Engine: postgres
MasterUsername: dbadmin
# 注: パスワードなどは既存の状態を維持するため、必要に応じてNoEchoやSecretsManager連携を考慮

Outputs:
DatabaseEndpoint:
Description: ‘Endpoint of the extracted database’
Value: !GetAtt MyDatabase.Endpoint.Address
Export:
Name: !Sub ‘${AWS::StackName}-DatabaseEndpoint’

—

ステップ3: インポートファイル(Mapping File)の作成と実行

AWS CLIの `create-change-set` または `import-resources` を用いて、既存の物理リソースと新しいテンプレート内の論理名を紐付ける。

ここで、以下のようなインポートマップファイル `import-map.json` を用意する。

[
{
“ResourceType”: “AWS::RDS::DBInstance”,
“LogicalResourceId”: “MyDatabase”,
“ResourceIdentifier”: {
“DBInstanceIdentifier”: “production-db”
}
}
]

いよいよインポートを実行する。マネジメントコンソールでも可能だが、SREたるものAWS CLIで一撃で仕留めるべきだ。

aws cloudformation create-change-set \
–stack-name Database-Stack \
–template-body file://Database-Stack.yaml \
–changeset-type IMPORT \
–resources-to-import file://import-map.json \
–capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM

チェンジセットのステータスが `CREATE_COMPLETE` になったことを確認したら、トドメの実行だ。

aws cloudformation execute-change-set –stack-name Database-Stack

これで、既存のデータベースを一切止めることなく、新しいCloudFormationスタックの配下へ安全にモジュール移管が完了した。

—

ステップ4: 依存関係の再構築(`Fn::ImportValue` の適用)

データベースの切り離しに成功したら、元の `Monolithic-Stack`(今や `App-Stack` などに名前を変えているはずだ)から、先ほどのエクスポート値を参照するように書き換える。

修正後の `App-Stack.yaml` (一部抜粋):

Resources:
WebService:
Type: AWS::ECS::Service
Properties:
# …
TaskDefinition:
ContainerDefinitions:

  • Name: app

Environment:

  • Name: DB_HOST

Value: !ImportValue Database-Stack-DatabaseEndpoint # クロススタック参照の美しさ

これにより、スタック間の緩やかな結合(Loose Coupling)が実現され、アプリケーション層とデータ層を完全に独立してデプロイできるようになる。

—

節度あるモジュール分割は、インフラエンジニアの品格を示すバロメーターだ。
「動いているから触らない」という技術的負債の放置は、やがてシステム全体の硬直化を招く。今回紹介した `Retain` と `Resource Import` のコンビネーションを武器に、君のプロジェクトのインフラストラクチャを美しく、そして強靭にリファクタリングして見せろ。

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