Skip to content

Bug: Breaking change in v12.3.0 - IAM member resource iteration refactor #446

@bdashrad

Description

@bdashrad

TL;DR

Release v12.3.0 (#442) introduced a breaking change and an anti-pattern that doesn't follow current best practices for keying resources using for_each. The new change creates the same issues that count did, where the resource names are tied to the order of the input set, causing recreation on changes to the set contents or order.

This causes all existing permissions to need to be recreated on upgrade as well.

Expected behavior

Minor versions not introduce breaking changes, especially without documentation.

Observed behavior

  # module.bucket.google_storage_bucket_iam_member.members["0"] will be created
  + resource "google_storage_bucket_iam_member" "members" {
      + bucket = "bucket-aaaaaaaa-0000-bbbb-1111-cccccccccccc"
      + etag   = (known after apply)
      + id     = (known after apply)
      + member = "serviceAccount:sa@project.iam.gserviceaccount.com"
      + role   = "roles/storage.objectViewer"
    }

  # module.bucket.google_storage_bucket_iam_member.members["1"] will be created
  + resource "google_storage_bucket_iam_member" "members" {
      + bucket = "bucket-aaaaaaaa-0000-bbbb-1111-cccccccccccc"
      + etag   = (known after apply)
      + id     = (known after apply)
      + member = "serviceAccount:sa@project.iam.gserviceaccount.com"
      + role   = "roles/storage.objectAdmin"
    }

  # module.bucket.google_storage_bucket_iam_member.members["2"] will be created
  + resource "google_storage_bucket_iam_member" "members" {
      + bucket = "bucket-aaaaaaaa-0000-bbbb-1111-cccccccccccc"
      + etag   = (known after apply)
      + id     = (known after apply)
      + member = "group:group1@example.com"
      + role   = "roles/storage.objectAdmin"
    }

  # module.bucket.google_storage_bucket_iam_member.members["3"] will be created
  + resource "google_storage_bucket_iam_member" "members" {
      + bucket = "bucket-aaaaaaaa-0000-bbbb-1111-cccccccccccc"
      + etag   = (known after apply)
      + id     = (known after apply)
      + member = "group:group0@example.com"
      + role   = "roles/storage.objectAdmin"
    }

  # module.bucket.google_storage_bucket_iam_member.members["roles/storage.objectAdmin group:group0@example.com"] will be destroyed
  # (because key ["roles/storage.objectAdmin group:group0@example.com"] is not in for_each map)
  - resource "google_storage_bucket_iam_member" "members" {
      - bucket = "b/bucket-aaaaaaaa-0000-bbbb-1111-cccccccccccc" -> null
      - etag   = "CAg=" -> null
      - id     = "b/bucket-aaaaaaaa-0000-bbbb-1111-cccccccccccc/roles/storage.objectAdmin/group:group0@example.com" -> null
      - member = "group:group0@example.com" -> null
      - role   = "roles/storage.objectAdmin" -> null
    }

  # module.bucket.google_storage_bucket_iam_member.members["roles/storage.objectAdmin group:group1@example.com"] will be destroyed
  # (because key ["roles/storage.objectAdmin group:group1@example.com"] is not in for_each map)
  - resource "google_storage_bucket_iam_member" "members" {
      - bucket = "b/bucket-aaaaaaaa-0000-bbbb-1111-cccccccccccc" -> null
      - etag   = "CAg=" -> null
      - id     = "b/bucket-aaaaaaaa-0000-bbbb-1111-cccccccccccc/roles/storage.objectAdmin/group:group1@example.com" -> null
      - member = "group:group1@example.com" -> null
      - role   = "roles/storage.objectAdmin" -> null
    }

  # module.bucket.google_storage_bucket_iam_member.members["roles/storage.objectAdmin serviceAccount:sa@project.iam.gserviceaccount.com"] will be destroyed
  # (because key ["roles/storage.objectAdmin serviceAccount:sa@project.iam.gserviceaccount.com"] is not in for_each map)
  - resource "google_storage_bucket_iam_member" "members" {
      - bucket = "b/bucket-aaaaaaaa-0000-bbbb-1111-cccccccccccc" -> null
      - etag   = "CAg=" -> null
      - id     = "b/bucket-aaaaaaaa-0000-bbbb-1111-cccccccccccc/roles/storage.objectAdmin/serviceAccount:sa@project.iam.gserviceaccount.com" -> null
      - member = "serviceAccount:sa@project.iam.gserviceaccount.com" -> null
      - role   = "roles/storage.objectAdmin" -> null
    }

  # module.bucket.google_storage_bucket_iam_member.members["roles/storage.objectViewer serviceAccount:sa@project.iam.gserviceaccount.com"] will be destroyed
  # (because key ["roles/storage.objectViewer serviceAccount:sa@project.iam.gserviceaccount.com"] is not in for_each map)
  - resource "google_storage_bucket_iam_member" "members" {
      - bucket = "b/bucket-aaaaaaaa-0000-bbbb-1111-cccccccccccc" -> null
      - etag   = "CAg=" -> null
      - id     = "b/bucket-aaaaaaaa-0000-bbbb-1111-cccccccccccc/roles/storage.objectViewer/serviceAccount:sa@project.iam.gserviceaccount.com" -> null
      - member = "serviceAccount:sa@project.iam.gserviceaccount.com" -> null
      - role   = "roles/storage.objectViewer" -> null
    }

Terraform Configuration

resource "random_uuid" "bucket" {
  keepers = {
    env = local.env
  }
}

module "bucket" {
  source  = "terraform-google-modules/cloud-storage/google//modules/simple_bucket"
  version = "~> 12.0.0"

  name       = "bucket-${random_uuid.bucket.result}"
  project_id = var.gcp_project_id
  location   = "us-east1"

  bucket_policy_only       = true
  public_access_prevention = "enforced"
  versioning               = false

  autoclass = true

  iam_members = [
    {
      role   = "roles/storage.objectViewer"
      member = "serviceAccount:sa@project.iam.gserviceaccount.com"
    },
    {
      role   = "roles/storage.objectAdmin"
      member = "serviceAccount:sa@project.iam.gserviceaccount.com"
    },
    {
      role   = "roles/storage.objectAdmin"
      member = "group:group0@example.com"
    },
    {
      role   = "roles/storage.objectAdmin"
      member = "group:group1@example.com"
    },
  ]
}

Terraform Version

$  terraform version
Terraform v1.14.8
on darwin_arm64

Terraform Provider Versions

$  terraform providers

Providers required by configuration:
.
├── provider[registry.terraform.io/hashicorp/google] ~> 7.30
├── provider[registry.terraform.io/hashicorp/random] ~> 3.6
├── provider[terraform.io/builtin/terraform]
└── module.bucket
    ├── provider[registry.terraform.io/hashicorp/google] >= 6.37.0, < 8.0.0
    └── module.encryption_key
        └── provider[registry.terraform.io/hashicorp/google] >= 5.31.0, < 8.0.0

Providers required by state:

    provider[registry.terraform.io/hashicorp/random]

    provider[terraform.io/builtin/terraform]

    provider[registry.terraform.io/hashicorp/google]

Additional information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions