中身の入った S3 バケットの名前を、CloudFormation で変えようとする会

記事タイトルとURLをコピーする

はじめに

こんにちは。 アプリケーションサービス部の保田(ほだ)です。

今日は AWS CloudFormation (以下、CloudFormation, CFn) で作成した Amazon S3 (以下、 S3) バケットを作ったときのお話です。

問題提起

いきなりですが、CFn で作成した S3 バケットに適当なファイルをアップロードした上で、CFn テンプレート内のバケット名を変更してスタックを更新したらどうなるのでしょうか?

つまり、こういうことをします。

  1. CFn で S3 バケットを作る
  2. 作ったバケットにオブジェクトをアップロード
  3. CFn でバケット名を変更しようとする

さてどうなるでしょうか?

  1. 新しいバケットが作られ、古いバケットがアップロードしたファイルとともに削除され、スタック更新が正常終了する
  2. 新しいバケットが作られるが、古いバケットの削除に失敗してスタック更新がロールバックし、新しいバケットは削除される
  3. 新しいバケットが作られ、古いバケットの削除に失敗する。が、それはそれとしてスタック更新が正常終了する
  4. 新しいバケットが作られるが、なぜかマネジメントコンソールが虹色に変化して元に戻らなくなる

忙しい人向け

正解は

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 を付けました。

これでスタックを更新します。

置換が True になっていることに注目

すると、リソースが置換されます。つまりバケットが再作成されます。 この辺はリファレンスに記載があります。

BucketName

Update requires: Replacement

docs.aws.amazon.com

AWS CloudFormation は更新の際にリソースを再作成し、新しい物理 ID も生成されます。AWS CloudFormation は、通常まず置換リソースを作成し、他の従属するリソースからの参照が置換リソースを指すように変更してから、古いリソースを削除します。

docs.aws.amazon.com

置換されている

バケット名は作成後変更できないので、新しいものを作ってから古いものを消している訳です。

本題

ではようやく本題です。

このバケットに適当なファイルをアップロードしましょう。

$ 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_COMPLETE になっちゃった

ただし、「状況の理由」には「Update successful. One or more resources could not be deleted.」と出ています。 まぁその通りのことが起きていますね。

バケットはどうなったのでしょうか?

古いバケットは残っている

残っていますね。

というわけで正解は

3. 新しいバケットが作られ、古いバケットの削除に失敗する。が、それはそれとしてスタック更新が正常終了する

でした!

古いバケットは消えるのか

さて、この古いバケットは今回対象にしている CloudFormation スタックの管理からはずれてしまっているように見えます。

リソースタブには古いバケットはいない

実際どうなのでしょう。

スタックを削除してみます。

DELETE_COMPLETE になっちゃった

スタックは削除できました。

しかし古いバケットはいまだに残っています。

まだいる

ちなみに予め古いバケットの中を空っぽにしても、同じように削除されずに残ります。

別にこれは隠れた仕様ではない

この挙動ですが、ちゃんとドキュメントに記載があります(同僚の方が教えて下さいました)。

CloudFormation は、古いリソースを 3 回削除しようとします。CloudFormation が古いリソースを削除できない場合、古いリソースをスタックから削除し、スタックの更新を続行します。スタックの更新が完了すると、CloudFormation は UPDATE_COMPLETE スタックイベントを発行しますが、1 つ以上のリソースを削除できなかったことを示す StatusReason が含まれます。CloudFormation は、特定のリソースに対して DELETE_FAILED イベントを発行し、対応する StatusReason は CloudFormation がリソースの削除に失敗した理由の詳細を提供します。

この状況を解決するには、基盤となるサービスのコンソールまたは API を使用してリソースを直接削除します。

docs.aws.amazon.com

うむ。

まとめ

大事なことはドキュメントにだいたい書いてある。

保田 和馬 (記事一覧)

アプリケーションサービス部

ボールペンで字を書くことがあります。(書かないこともある)