{"record":{"id":"e50808c49c9d7161","repo":"kubernetes/kops","slug":"error-updating-gcs-bucket-iam-for-gs-s-v","errorCode":null,"errorMessage":"error updating GCS bucket IAM for gs://%s: %v","messagePattern":"error updating GCS bucket IAM for gs://(.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/gcetasks/storagebucketiam.go","lineNumber":126,"sourceCode":"\tmember := \"serviceAccount:\" + fi.ValueOf(e.MemberServiceAccount.Email)\n\trole := fi.ValueOf(e.Role)\n\n\tklog.V(2).Infof(\"Creating GCS bucket IAM for gs://%s for %s as %s\", bucket, member, role)\n\n\tpolicy, err := t.Cloud.Storage().Bucket(bucket).IAM().V3().Policy(ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error creating IAM policy for bucket gs://%s: %w\", bucket, err)\n\t}\n\n\tchanged := patchPolicy(policy, member, role)\n\n\tif !changed {\n\t\tklog.Warningf(\"did not need to change policy (concurrent change?)\")\n\t\treturn nil\n\t}\n\n\tif err := t.Cloud.Storage().Bucket(bucket).IAM().V3().SetPolicy(ctx, policy); err != nil {\n\t\treturn fmt.Errorf(\"error updating GCS bucket IAM for gs://%s: %v\", bucket, err)\n\t}\n\n\treturn nil\n}\n\n// terraformStorageBucketIAM is the model for a terraform google_storage_bucket_iam_member rule\ntype terraformStorageBucketIAM struct {\n\tBucket string                   `cty:\"bucket\"`\n\tRole   string                   `cty:\"role\"`\n\tMember *terraformWriter.Literal `cty:\"member\"`\n}\n\nfunc (_ *StorageBucketIAM) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *StorageBucketIAM) error {\n\ttf := &terraformStorageBucketIAM{\n\t\tBucket: fi.ValueOf(e.Bucket),\n\t\tRole:   fi.ValueOf(e.Role),\n\t\tMember: e.MemberServiceAccount.TerraformLink_Member(),\n\t}","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/gcetasks/storagebucketiam.go#L108-L144","documentation":"StorageBucketIAM.RenderGCE wraps a failed IAM().V3().SetPolicy call. After patching the fetched policy with the desired member binding, kOps writes the whole policy back; a failure here means the binding was not applied to the bucket.","triggerScenarios":"SetPolicy(ctx, policy) returns an error after patchPolicy modified the bindings, during RenderGCE of a StorageBucketIAM task.","commonSituations":"kOps credentials lack storage.buckets.setIamPolicy; the policy changed concurrently and the write conflicts (retry will re-read); bucket deleted mid-apply; org policy domain-restricted-sharing rejects binding an out-of-domain service account member.","solutions":["Read the wrapped error: 403 => grant roles/storage.admin / storage.buckets.setIamPolicy; 409/conflict => re-run update (policy is re-fetched each run).","Check org policy constraints/iam.allowedPolicyMemberDomains if the member is outside allowed domains.","Ensure the member service account actually exists (a deleted SA email can be rejected on write).","Retry on transient 429/5xx; verify with `gcloud storage buckets get-iam-policy gs://<bucket>` after a successful run."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"if !hasPerm(\"storage.buckets.setIamPolicy\") {\n    return fmt.Errorf(\"identity lacks storage.buckets.setIamPolicy on gs://%s\", bucket)\n}\nif fi.ValueOf(e.MemberServiceAccount.Email) == \"\" {\n    return fmt.Errorf(\"member service account email must be set\")\n}","typeGuard":null,"tryCatchPattern":"if err := kopsUpdate(); err != nil {\n    if strings.Contains(err.Error(), \"error updating GCS bucket IAM\") {\n        if strings.Contains(err.Error(), \"403\") {\n            log.Print(\"grant storage.buckets.setIamPolicy\")\n        } else if strings.Contains(err.Error(), \"409\") || isTransient(err) {\n            log.Print(\"policy changed concurrently or transient error; re-run update\")\n        }\n    }\n    return err\n}","preventionTips":["Grant storage.buckets.setIamPolicy (roles/storage.admin) to the kOps identity.","Confirm the member service account exists before binding it to the policy.","Re-run update on conflict errors — the policy is re-fetched each run.","Verify the binding afterwards with `gcloud storage buckets get-iam-policy`."],"tags":["gcs","gcp","storage","iam","policy","api-error"],"backgroundTag":"gcs-bucket-api-error","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}