はじめに
こんにちは! 今日はSandbox環境に溜まっていくCloudFormationスタックを自動で整理するStep Functionsワークフローを作った話をシェアします。
なぜ作ったのか?
Sandbox環境を使っていると、テスト用に作ったスタックがどんどん溜まっていきますよね? 一つずつ手動で消すのは面倒だし、かといって放置するとリソースの無駄遣いが気になる…。そこでStep Functionsのネイティブ機能だけで自動削除システムを作ってみました。
全体フロー
- ListStacksで特定の命名パターンのスタックを収集
- DescribeStacksで詳細情報を確認(タグ、削除保護の有無)
- 条件に合うスタックだけDeleteStack実行
- ポーリングで削除完了を確認
- Bedrockでエラーメッセージを整理
ステップごとに詳しく見ていきましょう
ステップ1: スタックリストの取得 (ListStacks)
まずListStacks APIを呼び出して、削除可能な状態のスタックを取得します。
- CREATE_COMPLETE
- UPDATE_COMPLETE
- ROLLBACK_COMPLETE
- UPDATE_ROLLBACK_COMPLETE
ここでのポイントはJSONataを使ったフィルタリング。
僕は「Sfn」で始まるスタックだけを抽出するように設定しました。
jsonata
$count($states.result.StackSummaries) > 0 ?
$states.result.StackSummaries[StackName ~> /Sfn/][] : []
該当するスタックがなければ即終了! 無駄な処理はしません。
ステップ2: 詳細情報の確認 (DescribeStacks)
各スタックに対してDescribeStacksを並列実行します。
MaxConcurrencyを40に設定して高速処理。
ここで重要なのは2つの条件をチェックすること
- 削除保護(EnableTerminationProtection)が無効になっているか?
- EnvironmentタグがSandboxか?
この2つの条件を満たすスタックだけをStackToDeleted配列に入れます。
削除保護が有効なスタックはStackNotToDeletedに分類して、後で確認できるようにしました。
条件を満たすスタックが一つもなければここでも終了!
ステップ3: スタック削除
さあ、本格的に各スタックを削除していきます。この部分が一番難しかったです。CloudFormationスタックの削除って、意外と複雑なケースが多いんですよ。
初期設定
retryCount: 0
maxRetryCount: 10
DeletionMode: STANDARD
削除プロセス
DeleteStackを呼ぶと非同期で削除が始まります。
だから30秒待機してからDescribeStacksで状態を確認するポーリングロジックを実装しました。
ここが面白いところ! スタックが完全に削除されるとDescribeStacksがCloudFormationExceptionを投げるんです。
僕はこのエラーを削除成功のシグナルとして活用しました。エラーが発生したらCatchして正常終了させるという仕組み。
ListStacksでDELETE_COMPLETEを確認する方法もありますが、それだとロジックが複雑になるので、エラーをトリガーにする方がスッキリしました。
ステップ4: 厄介なケースの処理
-
ケース1: ネストされたスタック
親スタックを削除すれば子スタックも自動削除されるので、順序を気にせず削除するだけ。シンプル! -
ケース2: 依存関係のあるスタック (これが重要!)
AスタックがBスタックに依存している場合、Bを先に消そうとすると失敗してロールバックされます。これを逆手に取りました!
削除失敗 → スタックが元の状態に復元される
30秒待機して再度削除を試行
依存していたスタック(A)が消えたら自然にBも削除成功
ポーリングのおかげで依存関係の順序を気にしなくても自動で整理されます! -
ケース3: 削除不可能なリソース
DELETE_FAILED状態が検出されたら、DeletionModeをFORCE_DELETE_STACKに変更して強制削除を試みます。このときエラーメッセージをerrmsg配列に保存しておきます。
jsonata
{
'StackName': $states.input.Stacks[0].StackName,
'ErrorMessage': $states.input.Stacks[0].StackStatusReason
}
-
ケース4: 無限ループ防止
retryCountを毎回1ずつ増やして、maxRetryCount(10回)に達したら再試行を停止します。タイムアウトもワークフロー全体で600秒に設定しています。
ステップ5: Bedrockでエラーメッセージを整理
すべてのスタック処理が終わるとfinal_errmsgにエラー情報が入ります。これをそのまま見るとJSON配列で読みづらいですよね。
そこで Amazon Bedrock (Nova Lite) を活用! エラーメッセージを自然言語で要約して、人間が読みやすくしてくれます。
どのスタックにどんなエラーが発生したか分かりやすく簡潔に説明してください。
エラーがなければ無事に作業が終了したことを伝えてください。
この出力結果をSESなどのメールサービスと連携すれば通知システムも完成! (今回の実装には含めていませんが、簡単に追加できます)
重要ポイントまとめ
✅ JSONataを活用した強力なデータフィルタリング
✅ Map Stateの並列処理でパフォーマンス最適化
✅ エラーを活用した削除完了検知 (スマートな方法!)
✅ ポーリングベースのリトライロジックで依存関係の問題を自動解決
✅ Bedrockでエラーメッセージを自動要約
まとめ
Step Functionsのネイティブ機能だけで、かなり複雑なロジックをLambdaなしで実装できるって面白くないですか? 特にJSONataの表現力が思ったより強力で驚きました。
Sandbox環境の管理で悩んでいたらぜひ試してみてください。EventBridge Schedulerと連携すれば、毎日自動で整理されるクリーンな環境が作れます!
