♾️

【AWS】TerraformでS3バケット作成時にハマった点を深堀りしてみる

に公開

はじめに

先日、業務の一環で、Terraformで定義されたAWS環境のテストを実施していました。
そのテストでは、「リージョンを指定している変数の値を変更して、別リージョンでApplyを流しても正しく環境が構築されるか」という項目があったため、TerraformでS3バケットを作成・削除後、別リージョンで同名のS3バケットを再作成する、というオペレーションを行いました。
その時、Applyが終わらず再作成が完了しない、という現象が発生し、原因特定に苦難したので、備忘と共有の目的で、当時調査した内容をまとめます。

まずは結論

AWSのre:Postに、以下のような記述があります。

Amazon S3 は大規模な分散システムであるため、バケットの削除などの変更は、すべての AWS リージョンで結果整合性が取れるまでに時間がかかります。
多くの場合、バケットを作成できるのは、前のバケットを削除してから数時間以内です。ただし、変更の整合性が取れるまでには 48~72 時間かかる場合があります。S3 バケットを再作成するには、少なくとも 48 時間待つことをお勧めします。
(中略)
バケットが Amazon S3 によって完全に削除されるまで、同じバケット名を使用することはできません。

https://repost.aws/ja/knowledge-center/s3-conflicting-conditional-operation

結果整合性とは

結果整合性とは、分散コンピューティングにおいて高可用性を実現するために用いられる整合性モデルです。S3ではこの結果整合性を採用しています。
この仕組みにより、データの更新が全てのノード(今回の場合はすべてのAWSリージョン)に伝播するまでに時間がかかるものの、最終的には全てのノードが同じデータを保持することを保証します。

S3バケットの再作成に失敗した理由

re:Postでの公式アナウンスの通り、S3バケットに対する変更処理が全リージョン内で整合性が取れる状態になるまでに、48~72時間かかる可能性があります。その間は同じバケット名でのS3バケットの作成ができません。
今回は、この結果整合性の仕組みにより、S3バケットの再作成に失敗したと考えられます。

また、毎回発生するわけではありませんが、情報の整合性がとれるまで、削除したはずのS3バケットがAWSマネジメントコンソールのS3管理画面上で以下のように「AWS Region: Not found」と表示されることもあります。

このような表示になった際も、しばらく時間を置くとS3管理画面から表示が消えるので、結果整合性によるものと推測できます。
(参考)https://repost.aws/questions/QU0AtycsypSzCJzTON9mX1PA/s3-bucket-shows-aws-region-not-found-while-trying-to-deleteあ

結論にたどり着いた過程と今回の検証内容

実は今回、原因調査をする過程ですんなりこのre:Postの記事にたどり着けたわけではありません。
一通りの検証を行い、自分なりの答えが出た後、たまたま記事を発見し、答え合わせのような感覚で確認をしました。

結論は既に出ていますが、せっかく検証した経緯と結果があるので、以降紹介していきます。
※上記re:Postの「MAX72時間程度はリージョン間の整合性確保のためS3が正常に作成されないケースがある」という内容が公式からの正式なアナウンスであり、正となります。本検証結果はあくまで参考としてください。

【検証環境】

  • Terraform実行環境:AWS Cloud9
    • Region:ap-northeast-1
    • Instance type:t2.micro
    • Platform:Amazon Linux 2023

Apply失敗の現象を再現してみる

まず、現象を再現するために、発生当時と同様に以下の流れで作業を進めました。

【現象再現の流れ】

  1. Terraformソースコードの準備
  2. Terraformで定義したS3バケットをApply
  3. 2でApplyしたS3バケットをDestroy
  4. providerのリージョン設定のみを変更してS3バケットを再Apply

1. Terraformソースコードの準備

今回はTerraformでS3を作成するため、以下のようなソースコード(main.tf)を用意し、Cloud9に保存しました。
providerのリージョンはap-northeast-1に設定しています。

main.tf
terraform {
  required_version = ">=1.6.0"
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = ">= 5.22.0"
    }
  }
}

provider "aws" {
  profile = ""
  region  = "ap-northeast-1"
}

// S3のbucketを作成する
resource "aws_s3_bucket" "this" {
  bucket = "terraform-s3-test-20250108-12345"
}

resource "aws_s3_bucket_public_access_block" "this" {
  bucket                  = aws_s3_bucket.this.bucket
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

2. Terraformで定義したS3バケットをApply

早速、main.tfをApplyしてS3バケットを作成します。

terraform apply

Applyは数秒で完了し、問題なくS3バケットが作成されることを確認しました。

3. 2でApplyしたS3バケットをDestroy

後続の検証のために、2. Terraformで定義したS3のApplyで作成したS3バケットは、一旦このタイミングでDestroyします。

terraform destroy

DestroyもApply同様に数秒で完了し、S3バケットが削除されることを確認しました。

4. providerのリージョン設定のみを変更してS3バケットを再Apply

次に、main.tfを編集し、providerのリージョンをus-east-1に変更します。

main.tf
provider "aws" {
  profile = ""
  region  = "us-east-1"
}

その後、main.tfを再Applyしますが、50分経ってもApplyが完了しません。当時の現象を再現することが出来ました。

現象の再現ができたため、一度[Ctrl]+[C]でApplyを中止します。

Apply失敗の原因調査

次に原因調査を行います。

調査1. S3バケット名を変更して再Applyしてみる

S3バケット名は、グローバルで固有である必要があります。S3バケットを作成する際、このバケット名の制約が原因でエラーが発生することも多いです。
この制約が影響を及ぼしているか確認するため、S3バケット名を一部変更(末尾に6を追加)し、リージョンはus-east-1で再Applyしてみました。

main.tf
provider "aws" {
  profile = ""
  region  = "us-east-1"
}

resource "aws_s3_bucket" "this" {
  bucket = "terraform-s3-test-20250108-123456"
}

すると、今度は問題なくApplyが完了しました。S3バケット名が異なる場合は、Applyが完了しない現象は起こらないようです。

調査2. 同じ名前のS3バケットを同一リージョンで再Applyしてみる

完全にS3バケット名だけの問題であれば、同じ名前のS3バケットを同一リージョンで再ApplyしてもApplyが完了しない現象が起こるはずなので、実際に試してみます。

調査1. S3バケット名を変更して再Applyしてみるで作成したS3バケットを1度Destroyし、providerのリージョンはus-east-1のまま変更せずに再Applyします。

すると、このパターンではApplyが正常に完了し、S3バケットが作成されることが確認できました。

調査1、調査2の結果から、Applyが完了しない現象の発生条件としては以下の2点ということが分かりました。

  • 1回目のApplyとは異なるリージョンにS3バケットを再Applyする
  • 1回目のApplyと同じ名前のS3バケットを再Applyする

調査3. 直前のApply・Destroyから、時間をあけて異なるリージョンに同じ名前のS3バケットを再Applyしてみる

これまでの検証では、直前のDestroyが完了した後、1分以内に再Applyを実行していました。
上記の発生条件とApplyの実行間隔から、本事象の発生原因として、「AWS内部で、リージョン間のS3の情報同期にタイムラグがあり、同名のS3バケットを短期間で削除・作成するとS3バケット名の重複が発生して正常にApplyが完了しない」、という仮説を立て、調査を進めます。

上記の仮説を検証するために、今度は直前のApply、及びDestroyから、時間をあけて異なるリージョンに同じ名前のS3バケットを再Applyしてみました。
結果は以下の通りです。

1回目Destroyと2回目Applyの間隔 Apply結果
10分 失敗
30分 失敗
60分 成功

リージョン間の情報の同期のタイミングにも左右されるかと思いますが、私が検証した時は、1回目のApply・Destroyの後に大体60分程度時間を置くと、異なるリージョンに同じ名前のS3バケットを再Applyすることができました。

時間を置くことで正常にApplyできることが確認できたため、「AWS内部で、リージョン間のS3の情報同期にタイムラグがあり、同名のS3バケットを短期間で削除・作成するとS3バケット名の重複が発生して正常にApplyが完了しない」という仮説を立証する要素になるかと思います。

調査4. AWSマネジメントコンソールのS3管理画面での挙動を確認してみる

これまで、Terraformで現象の再現と原因調査を行ってきましたが、AWSマネジメントコンソール S3管理画面から同じ作業を行うとどうなるのか、念のため確認しました。

AWSマネジメントコンソール S3管理画面でS3バケットを一度作成・削除し、異なるリージョンで同じ名前のS3バケットを再度作成しようとすると、やはり下図の通りエラーが発生してS3バケットが作成できません。

このケースでも、60分程度間隔をあけて再度[Create bucket]ボタンを押下したところ、S3バケットが正常に作成されることを確認できました。

結論・考察

  • S3バケットを一度作成・削除し、直後に同名のS3バケットを別リージョンで再作成しようとすると、結果整合性の影響を受けて失敗する。
  • 公式としては最大72時間間隔を置くことが推奨されているが、手元での検証では、60分程度時間を置いた後に再作成すると問題なく作成が完了する。
  • S3バケットを一度作成・削除し、直後に同名のS3バケットを同リージョンで再作成しようとすると、問題なく再作成される。
  • 別リージョンへの再作成と、同リージョンへの再作成の違いについて:
    S3バケットを削除した直後は、リージョン内にその履歴情報が残っており、「この名前のS3バケットは既に削除されている」ということがリージョン内の情報のみで判断できるため、同リージョンで再作成した際はApplyが問題なく完了する。(これはあくまで私の考察です)

さいごに

今回は、TerraformでS3バケットを作成・削除後、別リージョンで同じ名前のS3バケットを再作成するとApplyが終わらず作成が完了しない件について、原因と検証の結果をご紹介しました。

★追記:2025/03/25★

本ブログ記事を公開した後、TerraformでS3バケットを作成しようとした際にまた別の事象が2件発生したので、追記します。

事象1. S3バケットライフサイクルを定義している箇所で「filterの指定がない」と警告が出る。

内容

以下の通りS3バケットライフサイクルを定義し、Planを実行しました。

resource "aws_s3_bucket_lifecycle_configuration" "this" {
  bucket = aws_s3_bucket.this.id
  rule {
    id     = var.lifecycle_config_id
    status = "Enabled"
    expiration {
      days = var.expiration_days
    }
    abort_incomplete_multipart_upload {
      days_after_initiation = 7
    }
  }
  depends_on = [aws_s3_bucket_versioning.this]
}

すると「filterの指定がない」という意図のWarningが確認されました。

Warning: Invalid Attribute Combination

with module.vpc_flow_logs_bucket.aws_s3_bucket_lifecycle_configuration.this,
on xxx/s3/main.tf line 44, in resource "aws_s3_bucket_lifecycle_configuration" "this":
44:   rule {

No attribute specified when one (and only one) of [rule[0].prefix.<.filter]
is required

This will be an error in a future version of the provider

原因

AWSProviderのバージョンアップに伴う仕様変更により、Prefix属性がDepricatedになったことが原因と分かりました。
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration#filter-1

対応方法

現在の仕様では、Filter属性を指定しないとPrefix属性が使われ、自動的にDepricatedな状態になってしまうため、明示的にFilterの指定する必要があります。

そこで、以下の通りブランクのFilter属性を指定します。

resource "aws_s3_bucket_lifecycle_configuration" "this" {
  bucket = aws_s3_bucket.this.id
  rule {
    id     = var.lifecycle_config_id
    status = "Enabled"
    filter {}
    expiration {
      days = var.expiration_days
    }
    abort_incomplete_multipart_upload {
      days_after_initiation = 7
    }
  }
  depends_on = [aws_s3_bucket_versioning.this]
}

しかし、上記コードでも別のWarningが発生します。

Warning: Invalid Attribute Combination

with module.ssm_bastion_logs_bucket.aws_s3_bucket_lifecycle_configuration.this,
on xxx/modules/s3/main.tf line 53, in resource "aws_s3_bucket_lifecycle_configuration" "this":
53:     filter {}

No attribute specified when one (and only one) of [rule[0].filter[0].prefix.<.object_size_greater_than,rule[0].filter[0].prefix.<.object_size_less_than,rule[0].filter[0].prefix.<.and,rule[0].filter[0].prefix.<.tag] is required

This will be an error in a future version of the provider

このWarningについては、AWSProvider5.90のバージョンアップに伴うバグである可能性が出ています。
(参考)
https://github.com/hashicorp/terraform-provider-aws/issues/41782
https://github.com/hashicorp/terraform-provider-aws/issues/41710

そのため、今現在ユーザー側で対応できることは無く、開発コミュニティにて対応されることを待つ状況となっています。

事象2. S3バケットライフサイクルを定義している箇所でエラーが発生し、Applyが止まることがある

内容

事象1と同様のソースコードを利用し、Applyを実行しました。

resource "aws_s3_bucket_lifecycle_configuration" "this" {
  bucket = aws_s3_bucket.this.id
  rule {
    id     = var.lifecycle_config_id
    status = "Enabled"
    expiration {
      days = var.expiration_days
    }
    abort_incomplete_multipart_upload {
      days_after_initiation = 7
    }
  }
  depends_on = [aws_s3_bucket_versioning.this]
}

すると「リソースが見つからない」という意図のErrorが発生し、Applyが失敗することが確認されました。これは毎回発生するわけではなく、発生した場合でも、もう1度Applyを実行するとリソースの作成は成功します。

Error: creating S3 Bucket (xxx) Lifecycle Configuration

with module.vpc_front_flow_logs_bucket.aws_s3_bucket_lifecycle_configuration.this,
on xxx/modules/s3/main.tf line 42, in resource "aws_s3_bucket_lifecycle_configuration" "this":
42: resource "aws_s3_bucket_lifecycle_configuration" "this" {

couldn't find resource

原因

AWSProvider5.86のバージョンアップに伴うバグである可能性が出ています。
https://github.com/hashicorp/terraform-provider-aws/issues/41363

対応方法

既にGitHub上にIssueとして上がっており、PullRequestが3/11に作られているため、近日中に対応がなされると推測します。
そのため、こちらも今現在ユーザー側で対応できることは無く、開発コミュニティにて対応されることを待つ状況となっています。

新規に見つかった事象は2件ともTerraformのバグである可能性が高く、いま私たちにできることは少ないため、コミュニティにて早急に対応されることを願います。

本記事が、だれかの一助になれば幸いです。


Accenture Japan - ICE (有志) により固定
 Cloudのキャリア・採用情報 | アクセンチュア
 女性が活躍する職場|採用情報 | アクセンチュア

Accenture Japan (有志)

Discussion