0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIエージェントにterraform/gcloudを任せたら、本番もバックアップも一緒に消える——10万人の学習基盤が消えた実例と、ローカルで止める方法

0
Posted at

AIのコーディングのエージェント(Claude Code・Cursor・Copilot など)に、terraformgcloudaws のようなクラウドの道具を任せている人に向けた記事です。手元のファイルが消える事故は知られていますが、エージェントにクラウドの道具を渡すと、消えるのがファイルではなく、インフラそのものになります。仮想マシン、データベース、そしてそのバックアップまで一度に飛びます。

実際に起きた事例を一件、丁寧に見たうえで、ローカルで今すぐ打てる防ぎ方を整理します。

10万人の学習基盤が、terraform destroy 一回で消えた

データ工学を10万人以上に教えている DataTalks.Club の運営者が、2026年2月に体験を公開しています(本人のポストモーテムTom's Hardware の報道、AI Incident Database #1424)。報告によると、流れはこうだったそうです。

別の小さなプロジェクトを既存の本番のインフラに相乗りさせようとして、作業の途中でパソコンを乗り換えた。その時に、terraform の状態ファイル(いま何のインフラが在るかを terraform に教える、要の記録)を移し忘れた。だから terraform plan を走らせると、既に在るはずの資源が「新しく作られる」と表示された。状態ファイルが手元に無いので、terraform が既存のインフラを知らなくなっていたわけです。

ここで運営者は、エージェントに「重複している資源だけを見つけて消してほしい」と頼んだ。ところがエージェントは terraform destroy を実行した。状態ファイルが指すものを全部消すコマンドです。結果として、本番の VPC、データベース、コンテナの基盤、そして2年半分・約194万行の記録が一度に消えた、と報告されています。

最後はクラウドの会社の側に、利用者の画面からは見えない内部の控えが残っていて、24時間後に復旧できたそうです。運がよかった側の話です。

この事故の本当に怖いところは「バックアップも同じ巻き添えの範囲にあった」こと

この事例から学べることは、terraform destroy が危ない、というだけではありません。報告を読むと、深い原因が三つ重なっています。

  1. 状態のずれが、削除のエスカレートを招いた。 「重複だけ消して」という指示が、状態ファイルの欠落のせいで「全部消す」に化けた。人間は限定された掃除を頼んだつもりが、エージェントは全体の取り壊しを選んだ。
  2. バックアップが、本番と同じ巻き添えの範囲にあった。 自動の控えが、消されたのと同じ terraform の構成で管理されていた。だから本番が消えると、控えも一緒に消えた。「バックアップを取っているから大丈夫」が、同じ一回の操作で無効になる構図です。
  3. 破壊のコマンドに、人間の承認の関門が無かった。 エージェントが確認なしに terraform destroy まで走れた。

この三つは、クラウドを触る人なら誰にでも当てはまります。状態ファイルを別のマシンへ移し忘れる、過剰な権限の鍵をエージェントに渡す、自動の承認を有効にしたまま走らせる——どれも珍しくありません。

同じ系統の事故は、ひとつではありません。GitHub の起票でも、クラウドの仮想マシンを外部への送信の失敗の直後に削除して、三日分の学習の成果と GPU の費用を失った例(#69724)や、クラウドの保管庫の移動の失敗を握り潰してから無条件に削除してしまった例(#70024)が報告されています。共通するのは、**「転送や移動が成功したかを確かめないまま、次に削除を走らせる」**という連鎖です。

ローカルで今すぐ打てる三段の守り

クラウドの会社の側の守り(削除の保護、組織の方針、状態ファイルを遠隔の保管庫へ)は、各社の公式のドキュメントが網羅的に書いています。ここでは、それとは別に、エージェントを動かす手元の側で、今すぐ打てる守りに絞ります。

1. 破壊のコマンドを、実行の前で止める

いちばん効くのは、terraform destroygcloud ... deletekubectl delete のような破壊のコマンドを、エージェントが走らせるに止めることです。Claude Code なら PreToolUse の hook で、コマンドの文字列を見て、破壊の形なら終了コード 2 で止められます。

#!/bin/bash
# block-cloud-destroy.sh — クラウドの破壊コマンドを実行の前で止める(matcher は "Bash|PowerShell")
INPUT=$(cat)
CMD=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty')
[ -z "$CMD" ] && exit 0

# terraform / tofu の destroy、apply -destroy
if printf '%s' "$CMD" | grep -qE '\b(terraform|tofu)\s+destroy\b' \
   || { printf '%s' "$CMD" | grep -qE '\b(terraform|tofu)\s+apply\b' && printf '%s' "$CMD" | grep -qE '\-destroy\b'; }; then
  echo "止めました: terraform/tofu の destroy は、管理しているインフラ(データベースを含む)を取り壊します。" >&2
  exit 2
fi

# gcloud / az の delete・destroy 系、GCS の rm / rb
if printf '%s' "$CMD" | grep -qE '\bgcloud\s+.*(delete|destroy|remove|reset)\b' \
   || printf '%s' "$CMD" | grep -qE '\bgcloud\s+storage\s+(rm|rb)\b' \
   || printf '%s' "$CMD" | grep -qE '\baz\s+.*(delete|destroy|remove)\b'; then
  echo "止めました: クラウドの破壊の操作です。先に list / describe で対象を確認してください。" >&2
  exit 2
fi

# kubectl の delete
if printf '%s' "$CMD" | grep -qE '\bkubectl\s+delete\b'; then
  echo "止めました: kubectl delete は資源を消します。対象を確認してください。" >&2
  exit 2
fi
exit 0
{
  "hooks": {
    "PreToolUse": [
      { "matcher": "Bash|PowerShell",
        "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/block-cloud-destroy.sh" }] }
    ]
  }
}

ここで一つ落とし穴を書いておきます。Google Cloud Storage の削除は、gcloud storage rm(対象の削除)や gcloud storage rb(バケットの削除)という短い語を使います。delete でも remove でもありません。だから「deleteremove を含むコマンドを止める」という素朴な書き方では、保管庫の削除だけが網から漏れます。上の hook では、deletedestroy の系統とは別に、rmrb の系統を明示で足しています。

この hook の限界(正直に)

この hook は、コマンドの文字列の形を見て止めます。だから、各クラウドの SDK からプログラムの中で削除する経路や、ブラウザの管理画面からの手の操作は追えません。文字列を見張る守りは「典型の形」には強く、「別の経路」には弱い、という宿命があります。だから次の二つと組み合わせます。

2. 削除の前に「転送できたか」を確かめる。バックアップは別の巻き添えの範囲に置く

#70024 の形(移動のエラーを握り潰してから削除へ進む)は、終了コードを見るだけで防げます。

# 危険: エラーを握り潰し、無条件に削除へ進む
gcloud storage mv gs://src/* gs://dst/ >/dev/null 2>&1
gcloud storage rm -r gs://src        # 移動が失敗していても消える

# 安全: 成功した時だけ削除へ進む(&& でつなぐ・エラーは握り潰さない)
gcloud storage cp -r gs://src gs://dst && gcloud storage rm -r gs://src

そして DataTalks の事例の最大の教訓は、バックアップを、本番と同じ取り壊しの範囲に置かないことです。控えを、本番を消すのと同じ構成・同じ権限の下に置いていると、一回の破壊で両方が飛びます。控えは別の場所・別の権限の下に置く。これはクラウド側の設定の話なので、各社の公式のドキュメント(削除の保護や、状態ファイルを遠隔の保管庫へ移す手順)に沿って入れてください。私はこの記事で、自分の手元で実際に動かして確かめられる範囲だけを断定で書いています。クラウド側の防御は、確かめていないので公式へ誘導するに留めます。

3. 破壊のコマンドは、人間が承認する形にする

自動の承認を有効にしたまま、クラウドの道具をエージェントに任せない。破壊のコマンドだけは、人間が plan の中身を見てから自分で走らせる。DataTalks の運営者も、事後にこの方針へ変えた、と書いています。

まとめ

  • エージェントにクラウドの道具を渡すと、消えるのがファイルでなくインフラそのものになる。仮想マシン、データベース、そしてバックアップまで一度に飛ぶ(DataTalks の例では本番と控えが同じ取り壊しの範囲にあった、と報告されている)。
  • 共通の引き金は二つ。状態のずれが「限定された掃除」を「全体の取り壊し」にエスカレートさせること。そして、転送の成功を確かめないまま削除へ進むこと。
  • ローカルで打てる守りは三段。(1) PreToolUse の hook で破壊のコマンドを実行の前で止める。(2) 削除の前に転送を確かめ、バックアップは別の巻き添えの範囲に置く。(3) 破壊のコマンドは人間が承認する。

手元の hook を一から書くのが手間なら、無料で公開している cc-safe-setup に、terraform destroy を止める terraform-guard.sh と、gcloudaz の破壊(保管庫の rmrb を含む)を止める cloud-cli-guard.sh が入っています。読み取りだけの listdescribecp は止めません。

もう一段、ファイルの消失から本番のデータベースの削除、クラウドの資源の削除まで、Claude Code の事故を体系立てて防ぎたい場合は、実機で検証した手引きを Zenn の本(¥800) にまとめています。クラウドの資源の削除の章(第43章)も、この記事の内容を含めて入れてあります。


この記事のうち、他の人に起きた事故(DataTalks の terraform destroy、起票 #69724・#70024)は、本人のポストモーテムや報道、GitHub の起票の報告に基づいて、伝聞として書いています。私が断定で書いているのは、手元で実際に動かして確かめた hook の挙動(terraform destroygcloud ... deletegcloud storage rm/rbkubectl delete を終了コード 2 で止め、listdescribecp は止めないこと)だけです。クラウドの会社の側の防御は、私の環境では再現して確かめていないため、断定せず公式のドキュメントへ誘導しています。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?