Skip to content

aws_s3_bucket wants to recreate the bucket due to name changes, when the name doesn't change. #43306

@rtkmhart

Description

@rtkmhart

Terraform and AWS Provider Version

Terraform v1.12.2
on linux_arm64
+ provider registry.opentofu.org/hashicorp/archive v2.7.1
+ provider registry.opentofu.org/hashicorp/aws v6.2.0
+ provider registry.opentofu.org/hashicorp/external v2.3.5
+ provider registry.opentofu.org/integrations/github v6.6.0

Affected Resource(s) or Data Source(s)

  • aws_s3_bucket

Expected Behavior

I have a custom module "grafana_managed" used to configure Grafana in AWS, which pulls in my S3 module. The S3 module is used in multiple places successfully, and used to create buckets across multiple AWS accounts and regions.

I expect the S3 bucket name to never change, and the bucket should never be recreated.

Actual Behavior

The provider thinks the name has changed and wants to recreate the bucket.

Relevant Error/Panic Output

-/+ resource "aws_s3_bucket" "default" {
      + acceleration_status         = (known after apply)
      + acl                         = (known after apply)
      ~ arn                         = "arn:aws:s3:::739275454630-global-us-east-2-grafana-dashboards" -> (known after apply)
      ~ bucket                      = "739275454630-global-us-east-2-grafana-dashboards" # forces replacement -> (known after apply) # forces replacement
...

Sample Terraform Configuration

Click to expand configuration

The terraform used to create the bucket is:

variable "environment" {
  description = "Environment name, used for tagging AWS resources, and in the bucket name." 
  type        = string
}

variable "name" {
  description = "Name of the bucket"
  type        = string
}

locals {
    bucket_name = "${data.aws_caller_identity.current.account_id}-${lower(var.environment)}-${data.aws_region.current.name}-${lower(var.name)}"
}

resource "aws_s3_bucket" "default" {
    bucket = local.bucket_name
}

Steps to Reproduce

I can't reproduce it in a test case. I saw this a few months ago in production in two different AWS accounts using the same Grafana -> S3 module, but I don't see it anywhere else using the S3 module. The first time it happened I ended up recreating the buckets, but that's not a good solution.

Note that when I did allow terraform to recreate the bucket, it comes back with the same name.

Debug Logging

I added some debug output in the provider to try track down why terraform thinks the name has changed. In the output the old/new names are different. Note that the plan includes three existing S3 buckets, only the *-grafana-dashboards suffers this problem.

Click to expand
diff --git a/internal/service/s3/bucket.go b/internal/service/s3/bucket.go
index 8a6f678aad..afc6b2ff6f 100644
--- a/internal/service/s3/bucket.go
+++ b/internal/service/s3/bucket.go
@@ -48,6 +48,14 @@ const (
        bucketPropagationTimeout = 2 * time.Minute
 )
 
+func mikesDiagnostics(k, old, new string, d *schema.ResourceData) bool {
+       log.Printf("[WARN] MDH: k=%s", k)
+       log.Printf("[WARN] MDH: old=%s", old)
+       log.Printf("[WARN] MDH: new=%s", new)
+       log.Printf("[WARN] MDH: schema=%v", d)
+       return true
+}
+
 // @SDKResource("aws_s3_bucket", name="Bucket")
 // @Tags(identifierAttribute="bucket", resourceType="Bucket")
 // @IdentityAttribute("bucket")
@@ -66,6 +74,7 @@ func resourceBucket() *schema.Resource {
                                if err := importer.RegionalSingleParameterized(ctx, rd, names.AttrBucket, meta.(importer.AWSClient)); err != nil {
                                        return nil, err
                                }
+                               fmt.Printf("MDH: Just after importer.RegionalSingleParameterized, rd=%v, names.AttrBucket=%s", rd, names.AttrBucket)
 
                                rd.Set(names.AttrForceDestroy, false)
 
@@ -100,12 +109,14 @@ func resourceBucket() *schema.Resource {
                                Type:     schema.TypeString,
                                Computed: true,
                        },
-                       names.AttrBucket: {
-                               Type:          schema.TypeString,
-                               Optional:      true,
-                               Computed:      true,
-                               ForceNew:      true,
-                               ConflictsWith: []string{names.AttrBucketPrefix},
+                       names.AttrBucket: { // == bucket
+                               Type:                  schema.TypeString,
+                               Optional:              true,
+                               Computed:              true,
+                               ForceNew:              true,
+                               ConflictsWith:         []string{names.AttrBucketPrefix}, // == bucket_prefix
+                               DiffSuppressFunc:      mikesDiagnostics,
+                               DiffSuppressOnRefresh: true,
                                ValidateFunc: validation.All(
                                        validation.StringLenBetween(0, 63),
                                        validation.StringDoesNotMatch(directoryBucketNameRegex, `must not be in the format [bucket_name]--[azid]--x-s3. Use the aws_s3_directory_bucket resource to manage S3 Express buckets`),
@@ -792,6 +803,7 @@ func resourceBucketCreate(ctx context.Context, d *schema.ResourceData, meta any)
 }
 
 func resourceBucketRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
+       log.Printf("[WARN] MDH: resourceBucketRead(id=%s)", d.Id())
        var diags diag.Diagnostics
        c := meta.(*conns.AWSClient)
        conn := c.S3Client(ctx)
@@ -803,6 +815,7 @@ func resourceBucketRead(ctx context.Context, d *schema.ResourceData, meta any) d
                d.SetId("")
                return diags
        }
+       log.Printf("[WARN] MDH: Found bucket %s", d.Id())
 
        if err != nil {
                return sdkdiag.AppendErrorf(diags, "reading S3 Bucket (%s): %s", d.Id(), err)
@@ -1138,6 +1151,7 @@ func resourceBucketRead(ctx context.Context, d *schema.ResourceData, meta any) d
 
        d.Set("bucket_region", region)
        d.Set("bucket_regional_domain_name", bucketRegionalDomainName(d.Id(), region))
+       log.Printf("MDH: region=%s", region)
 
        hostedZoneID, err := hostedZoneIDForRegion(region)
        if err != nil {
@@ -1602,6 +1616,7 @@ func resourceBucketDelete(ctx context.Context, d *schema.ResourceData, meta any)
 }
 
 func findBucket(ctx context.Context, conn *s3.Client, bucket string, optFns ...func(*s3.Options)) (*s3.HeadBucketOutput, error) {
+       log.Printf("[WARN] MDH: findBucket()")
        input := s3.HeadBucketInput{
                Bucket: aws.String(bucket),
        }
@@ -1620,7 +1635,6 @@ func findBucket(ctx context.Context, conn *s3.Client, bucket string, optFns ...f
        if output == nil {
                return nil, tfresource.NewEmptyResultError(input)
        }
-
        return output, nil
 }

The output in TF_LOG when running TF_LOG_PROVIDER=INFO terraform plan:

Click to expand ``` $ grep MDH tf.log 2025-07-08T17:36:46.816Z [WARN] provider.terraform-provider-aws: [WARN] MDH: resourceBucketRead(id=739275454630-workloadconfig-us-east-2-load-balancer-logs) 2025-07-08T17:36:46.817Z [WARN] provider.terraform-provider-aws: [WARN] MDH: findBucket() 2025-07-08T17:36:46.820Z [WARN] provider.terraform-provider-aws: [WARN] MDH: resourceBucketRead(id=739275454630-workloadconfig-us-east-2-lambda-artifacts) 2025-07-08T17:36:46.820Z [WARN] provider.terraform-provider-aws: [WARN] MDH: findBucket() 2025-07-08T17:36:46.979Z [WARN] provider.terraform-provider-aws: [WARN] MDH: Found bucket 739275454630-workloadconfig-us-east-2-lambda-artifacts 2025-07-08T17:36:46.990Z [WARN] provider.terraform-provider-aws: [WARN] MDH: Found bucket 739275454630-workloadconfig-us-east-2-load-balancer-logs 2025-07-08T17:36:50.597Z [WARN] provider.terraform-provider-aws: [WARN] MDH: resourceBucketRead(id=739275454630-global-us-east-2-grafana-dashboards) 2025-07-08T17:36:50.600Z [WARN] provider.terraform-provider-aws: [WARN] MDH: findBucket() 2025-07-08T17:36:50.761Z [WARN] provider.terraform-provider-aws: [WARN] MDH: Found bucket 739275454630-global-us-east-2-grafana-dashboards 2025-07-08T17:36:51.615Z [WARN] provider.terraform-provider-aws: [WARN] MDH: k=bucket 2025-07-08T17:36:51.615Z [WARN] provider.terraform-provider-aws: [WARN] MDH: old=739275454630-global-us-east-2-grafana-dashboards 2025-07-08T17:36:51.615Z [WARN] provider.terraform-provider-aws: [WARN] MDH: new=74D93920-ED26-11E3-AC10-0800200C9A66 ```

GenAI / LLM Assisted Development

n/a

Important Facts and References

This isn't limited to one version, I can reproduce the plan requiring recreating the bucket with combinations of:

  • Terraform 1.12
  • OpenTofu 1.7
  • AWS Provider versions 5.86 to 6.2

Would you like to implement a fix?

No

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugAddresses a defect in current functionality.needs-triageWaiting for first response or review from a maintainer.service/s3Issues and PRs that pertain to the s3 service.waiting-responseMaintainers are waiting on response from community or contributor.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions