はじめに
こんにちは。 アプリケーションサービス部の保田(ほだ)です。
今日は AWS CloudFormation (以下、CloudFormation, CFn) で作成した Amazon S3 (以下、 S3) バケットを作ったときのお話です。
問題提起
いきなりですが、CFn で作成した S3 バケットに適当なファイルをアップロードした上で、CFn テンプレート内のバケット名を変更してスタックを更新したらどうなるのでしょうか?
つまり、こういうことをします。
- CFn で S3 バケットを作る
- 作ったバケットにオブジェクトをアップロード
- CFn でバケット名を変更しようとする
さてどうなるでしょうか?
- 新しいバケットが作られ、古いバケットがアップロードしたファイルとともに削除され、スタック更新が正常終了する
- 新しいバケットが作られるが、古いバケットの削除に失敗してスタック更新がロールバックし、新しいバケットは削除される
- 新しいバケットが作られ、古いバケットの削除に失敗する。が、それはそれとしてスタック更新が正常終了する
- 新しいバケットが作られるが、なぜかマネジメントコンソールが虹色に変化して元に戻らなくなる
忙しい人向け
正解は
3. 新しいバケットが作られ、古いバケットの削除に失敗する。が、それはそれとしてスタック更新が正常終了する
です。
実際にやってみた
まずは準備
s3.yml を用意します。
Resources: MyBucket: Type: AWS::S3::Bucket Properties: BucketName: hoda-bucket-20230922
マネジメントコンソールからスタックを作成しましょう。
もちろん S3 のコンソールからも存在が確認できます。
念のため確認として、まずは中身が空の状態でバケット名を変えてみます。
Resources: MyBucket: Type: AWS::S3::Bucket Properties: BucketName: hoda-bucket-20230922-1
バケット名の末尾に -1
を付けました。
これでスタックを更新します。
すると、リソースが置換されます。つまりバケットが再作成されます。 この辺はリファレンスに記載があります。
BucketName
Update requires: Replacement
AWS CloudFormation は更新の際にリソースを再作成し、新しい物理 ID も生成されます。AWS CloudFormation は、通常まず置換リソースを作成し、他の従属するリソースからの参照が置換リソースを指すように変更してから、古いリソースを削除します。
バケット名は作成後変更できないので、新しいものを作ってから古いものを消している訳です。
本題
ではようやく本題です。
このバケットに適当なファイルをアップロードしましょう。
$ echo "foo" > foo.txt $ cat foo.txt foo $ aws s3 cp foo.txt s3://hoda-bucket-20230922-1/ upload: ./foo.txt to s3://hoda-bucket-20230922-1/foo.txt $ aws s3 ls hoda-bucket-20230922-1 2023-09-22 19:22:58 4 foo.txt
再びテンプレートを書き換えます。
Resources: MyBucket: Type: AWS::S3::Bucket Properties: BucketName: hoda-bucket-20230922-2
バケット名末尾の -1
を -2
にしました。
再びスタックを更新します。
よく知られた仕様として、バケットは中にオブジェクトが入っていると、削除できません。 今回のケースでは、果たしてどうなるでしょうか?
予想通り古いバケットの削除に失敗して DELETE_FAILED が出ています。
3回削除が試行されたあと、削除できないままなんか正常終了扱いになっちゃいました。
ただし、「状況の理由」には「Update successful. One or more resources could not be deleted.」と出ています。 まぁその通りのことが起きていますね。
バケットはどうなったのでしょうか?
残っていますね。
というわけで正解は
3. 新しいバケットが作られ、古いバケットの削除に失敗する。が、それはそれとしてスタック更新が正常終了する
でした!
古いバケットは消えるのか
さて、この古いバケットは今回対象にしている CloudFormation スタックの管理からはずれてしまっているように見えます。
実際どうなのでしょう。
スタックを削除してみます。
スタックは削除できました。
しかし古いバケットはいまだに残っています。
ちなみに予め古いバケットの中を空っぽにしても、同じように削除されずに残ります。
別にこれは隠れた仕様ではない
この挙動ですが、ちゃんとドキュメントに記載があります(同僚の方が教えて下さいました)。
CloudFormation は、古いリソースを 3 回削除しようとします。CloudFormation が古いリソースを削除できない場合、古いリソースをスタックから削除し、スタックの更新を続行します。スタックの更新が完了すると、CloudFormation は UPDATE_COMPLETE スタックイベントを発行しますが、1 つ以上のリソースを削除できなかったことを示す StatusReason が含まれます。CloudFormation は、特定のリソースに対して DELETE_FAILED イベントを発行し、対応する StatusReason は CloudFormation がリソースの削除に失敗した理由の詳細を提供します。
この状況を解決するには、基盤となるサービスのコンソールまたは API を使用してリソースを直接削除します。
うむ。
まとめ
大事なことはドキュメントにだいたい書いてある。