困ったこと
CloudFormationスタックの削除を行っていたところ、とあるスタックがUPDATE_ROLLBACK_FAILED状態から抜け出せなくなってしまいました。
この状態になると、スタックの更新も削除もできなくなり、非常に厄介な状況に陥ります。
丸ごと消したいだけなのに〜という悲痛な思いと共にCloudformationと向き合うことにならないよう、こちらに記事を残します。
UPDATE_ROLLBACK_FAILEDとは
CloudFormationスタックの更新が失敗した際、自動的にロールバック(以前の状態に戻す)処理が実行されます。しかし、このロールバック自体も失敗するとUPDATE_ROLLBACK_FAILED状態になります。
よくある原因としては、以下が挙げられます。
- リソースが手動削除された: ロールバック対象のリソースが既に手動で削除されている
- IAM権限不足: ロールバックに必要な権限が不足している
- リソースの状態変更: ロールバック中に対象リソースの状態が変わった
- 循環依存: 複数のスタック間でExport/Importが相互参照している
私の場合は、「1. リソースが手動削除された」と「4. 循環依存」がダブルでのしかかってきましたw
実は先に手動で削除してしまったリソースがありそれが原因で詰まってしまったのですが、さらにそれを参照していた別のスタックも削除に失敗するという、非常に面倒な状況になってしまったのです。
手動削除してしまったケースでは、以下のエラーメッセージが表示されていました。
Resource handler returned message: "The security group 'sg-xxxxxxx'
does not exist (Service: Ec2, Status Code: 400, Request ID: xxx)"
(HandlerErrorCode: NotFound)
上記では、SecurityGroupが既に存在しないため、ロールバックが失敗しています。
解決方法
以下のCLIコマンドで助かりました...。
(CLIはローカルで設定してもいいですし、Cloudshellから実行するのも早いです)
ステップ1: 失敗したリソースを特定
まず、UPDATE_ROLLBACK中に失敗したリソースを確認します。
※テーブル形式で出力
aws cloudformation describe-stack-events \
--stack-name sample-stack-01 \
--max-items 100 \
--query 'StackEvents[?ResourceStatus==`UPDATE_ROLLBACK_FAILED`].[Timestamp,LogicalResourceId,ResourceType,ResourceStatusReason]' \
--output table
LogicalResourceId列に表示されるIDが、次のステップで使用する値になります
また、ResourceStatusReason列で失敗の原因を確認できます。
ステップ2: 失敗したリソースでロールバックを続行
ステップ1で確認したLogicalResourceIdを指定して、そのリソースをスキップしながらロールバックを続行させます。
aws cloudformation continue-update-rollback \
--stack-name sample-stack-01 \
--resources-to-skip <LogicalResourceId1> <LogicalResourceId2>
複数のリソースがある場合は、スペース区切りで指定します。
また、指定したリソースがUPDATE_ROLLBACK中に失敗していないものの場合、以下のエラーメッセージが表示されました。必ず失敗したリソースを正しく指定してください。
Only the resources failed during UpdateRollback are allowed to be skipped
ちなみに、スタックがUPDATE_ROLLBACK_FAILED状態ではない場合は以下のメッセージが返されます。
RollbackUpdatedStack cannot be called from current stack status
※全く同じ名前のリソースをAWS管理コンソールから手動で作成することで、
一時的にCloudformationスタックでの参照先でエラーがなくなり、
うまくいく、という方法で解決するケースもありました。
(かなり荒技だと思いますが...)
参考
AWS公式にも同じ対処法がありました。