【テクニカル・上級編】CloudFormationスタックのロールバック失敗(Rollback Completeの罠)からの手動復旧とスタック削除の完全手順 – インフラ構成管理(IaC)活用バイブル

CloudFormationの深淵:`Rollback_Failed`という悪夢からの脱出と、リソース切り離しの極限レシピ

クラウドインフラをコードで統べる者にとって、AWS CloudFormationは両刃の剣だ。宣言的構成管理の美しさは、ひとたびリソースの競合、IAMの権限剥奪、あるいはサードパーティAPIのタイムアウトによって「ロールバックの失敗(`ROLLBACK_FAILED`)」という修羅場を引き起こした瞬間、悪夢へと変わる。

コンソールから「削除」ボタンを叩いても「Stack [xxx] cannot be deleted while in state ROLLBACK_FAILED」という冷徹なエラーメッセージが返ってくる。スタックは完全にフリーズし、パイプラインは停止し、手動で作成した実リソースとCloudFormationの状態(State)が乖離して行く。

本稿では、この極限状態からシステムを完全に生還させ、二度とこの罠に嵌らないための低レイヤな知見と、AWS CLIを駆使した破壊的かつ安全な復旧手順を解き明かす。

—

1. なぜ `ROLLBACK_FAILED` は発生するのか?(内部アーキテクチャの理解)

CloudFormationのステートマシンは、トランザクションの原子性(Atomicity)を担保しようと必死にロールバックを試みる。しかし、以下の条件が揃ったとき、システムはデッドロックに陥る。

1. 外部からの手動干渉: ロールバック中に、オペレーターがコンソールや別スクリプトで依存リソース(例: Security GroupやIAM Role)を先に削除してしまった。
2. 循環依存とプロバイダの制約: VPCとENI、あるいはRDSの最終スナップショット設定などにより、AWS API側が削除を拒絶した。
3. IAM権限の喪失: ロールバックを実行するためのCloudFormationサービスロール(`ExecutionRole`)の権限が、スタック更新の過程で変更・削除された。

結果として、CloudFormationは「最後にロールバックすべきリソース」の削除に失敗し、スタックを `ROLLBACK_FAILED` で凍結する。この状態では、通常の `UpdateStack` も `DeleteStack` も受け付けられなくなる。

—

2. 復旧の基本方針:何が許され、何をすべきか

手動復旧の原則はただ一つ:「CloudFormationのステートと、物理リソースの現実を強制的に一致させる」 ことだ。

アプローチとしては大きく分けて2つある。
1. リソースのスキップ(Skip/Retain)を伴うスタック削除: 厄介なリソースをCloudFormationの管理下から切り離し(`Retain`)、スタックを強制削除する。
2. リソース状態の偽装(Drift Alignment): 失敗しているリソースの物理ID(PhysicalResourceId)を書き換え、すでに削除されている(あるいは存在しない)状態に見せかけてロールバックを完遂させる。

次章より、実務で使える極限のコマンドライン手順を解説する。

—

3. 実践:AWS CLIを用いた強制的スタック削除の完全手順

コンソールが使い物にならない時、頼れるのはAWS CLIとAPIの生データだけだ。以下のステップに従い、デッドロックを物理的に粉砕する。

ステップ 1: 障害リソースの特定

まずは、どのリソースがロールバックを阻害しているのかを正確に特定する。

aws cloudformation describe-stack-resource-drifts \
–stack-name \
–stack-resource-drift-status-filters MODIFIED DELETED NOT_CHECKED

あるいは、直近のエラーイベントをJSONで全取得し、`RESOURCE_FAILED` を吐いている論理ID(LogicalResourceId)を抽出する。

aws cloudformation describe-stack-events \
–stack-name \
–query “StackEvents[?ResourceStatus==’UPDATE_ROLLBACK_FAILED’ || ResourceStatus==’CREATE_FAILED’]” \
–output json

ステップ 2: `aws cloudformation delete-stack` の `–retain-resources` ハック

これが最もクリーンで推奨されるアプローチだ。削除をブロックしているリソースを「保持(Retain)」させながら、スタック本体を強制削除する。

削除をブロックしているリソースの論理IDを特定している前提
RETAINED_RESOURCES=”DanglingSecurityGroup DatabaseSubnetGroup”

aws cloudformation delete-stack \
–stack-name \
–retain-resources $RETAINED_RESOURCES

【重要】 このコマンドを実行すると、スタックのステータスは `DELETE_IN_PROGRESS` に移行し、指定したリソース以外のすべてのリソース(およびスタック定義)が消し去られる。保持されたリソースは、AWS上に残骸として残るため、必要に応じて手動でクリーンアップするか、別のスタックにインポートする。

—

4. 最終手段:リソースの「切り離し」ができない場合の劇薬

もし、 `–retain-resources` を使っても削除が失敗する場合、またはスタックが完全に膠着している場合、AWS サポートへの連絡…の前に試すべき「API直叩き」による最終手段がある。

自作自動化スクリプト:強制ステート同期スクリプト

CloudFormationの内部メタデータを強制的にバイパスすることはできないが、「すでにリソースが存在しない」と誤認させる ことで、スタックの状態を前進させることが可能な場合がある。

以下のPython(Boto3)スクリプトは、特定のスタックで立ち往生しているリソースのステータスを強制的に無視、またはスタックのロックを解除するための概念実証(PoC)だ。

import boto3
import sys

def force_unblock_stack(stack_name):
cf = boto3.client(‘cloudformation’)

print(f”[] Analyzing stack: {stack_name}”)
try:
resources = cf.describe_stack_resources(StackName=stack_name)
except Exception as e:
print(f”[!] Error fetching stack resources: {e}”)
sys.exit(1)

for res in resources[‘StackResources’]:
status = res[‘ResourceStatus’]
logical_id = res[‘LogicalResourceId’]
physical_id = res.get(‘PhysicalResourceId’, ‘N/A’)

print(f”Logical: {logical_id} | Physical: {physical_id} | Status: {status}”)

if ‘FAILED’ in status:
print(f” -> Target for intervention: {logical_id}”)
# 注意: boto3から直接リソースステータスを書き換えるAPIは存在しない。
# ここでは、該当リソースの実体がAWS上に存在しない場合、
# 外部からダミーのリソースを作成してPhysicalIdを一致させるか、
# あるいはAWSサポートに連絡する前の最終境界線となる。

if __name__ == “__main__”:
if len(sys.argv) < 2: print("Usage: python force_unblock.py “)
sys.exit(1)
force_unblock_stack(sys.argv[1])

> アーキテクトからの警告: 上記のような低レイヤの操作は、誤るとリソースの孤立(Orphaned Resources)や課金の暴走を招く。本番環境で実行する際は、事前にAWS OrganizationsのSCPやIAM境界を確認し、影響範囲を完全に把握した上で行うこと。

—

5. 予防原則:二度と `ROLLBACK_FAILED` を起こさないための設計思想

場当たり的な復旧手順を覚えるよりも、この状態に「させない」パイプライン設計こそがSREの真骨頂である。

1. `OnFailure` パラメータの戦略的活用:
CI/CDパイプラインからのデプロイでは、単にロールバックに頼るのではなく、意図しない変更が入った場合は即座にスタック作成を失敗させ、ドライラン(ChangeSet)の段階で検知する仕組みを徹底する。

aws cloudformation deploy \
–template-file template.yaml \
–stack-name prod-core \
–no-execute-changeset # 変更セットを必ず目視・自動検証してから実行

2. Deletable / Retain ポリシーのコード化:
本番環境のデータベースやS3バケットなど、絶対に消えてはならないリソースには、必ず `DeletionPolicy: Retain` または `UpdateReplacePolicy: Retain` を付与する。これにより、万が一のスタック削除やロールバック時にも、データ資産が物理的に保護される。

3. ネストされたスタック(Nested Stacks)の導入:
巨大な一枚岩(Monolithic)のテンプレートは、一部の障害で全体が巻き込まれる。責務ごとにスタックを分離し、Blast Radius(影響範囲)を最小限に抑える構造を強制せよ。

—

結び

CloudFormationの `ROLLBACK_FAILED` は、インフラストラクチャ・アズ・コード(IaC)の冷徹な現実を我々に突きつける。しかし、その内部機構(ステートマシンとAPIの依存関係)を完全に理解していれば、恐るるに足りない。

CLIを武器にリソースを切り離し、ステートの整合性を奪還する。その手腕こそが、真のインフラストラクチャ・エンジニアの証なのだ。コンソール画面を閉じ、ターミナルを開け。インフラの主導権は、常にコードと、それを操るあなたの手の中にあるべきだ。

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