kubernetes/kops · error

error updating GCS bucket IAM for gs://%s: %v

Error message

error updating GCS bucket IAM for gs://%s: %v

What it means

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.

Source

Thrown at upup/pkg/fi/cloudup/gcetasks/storagebucketiam.go:126

	member := "serviceAccount:" + fi.ValueOf(e.MemberServiceAccount.Email)
	role := fi.ValueOf(e.Role)

	klog.V(2).Infof("Creating GCS bucket IAM for gs://%s for %s as %s", bucket, member, role)

	policy, err := t.Cloud.Storage().Bucket(bucket).IAM().V3().Policy(ctx)
	if err != nil {
		return fmt.Errorf("error creating IAM policy for bucket gs://%s: %w", bucket, err)
	}

	changed := patchPolicy(policy, member, role)

	if !changed {
		klog.Warningf("did not need to change policy (concurrent change?)")
		return nil
	}

	if err := t.Cloud.Storage().Bucket(bucket).IAM().V3().SetPolicy(ctx, policy); err != nil {
		return fmt.Errorf("error updating GCS bucket IAM for gs://%s: %v", bucket, err)
	}

	return nil
}

// terraformStorageBucketIAM is the model for a terraform google_storage_bucket_iam_member rule
type terraformStorageBucketIAM struct {
	Bucket string                   `cty:"bucket"`
	Role   string                   `cty:"role"`
	Member *terraformWriter.Literal `cty:"member"`
}

func (_ *StorageBucketIAM) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *StorageBucketIAM) error {
	tf := &terraformStorageBucketIAM{
		Bucket: fi.ValueOf(e.Bucket),
		Role:   fi.ValueOf(e.Role),
		Member: e.MemberServiceAccount.TerraformLink_Member(),
	}

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Read the wrapped error: 403 => grant roles/storage.admin / storage.buckets.setIamPolicy; 409/conflict => re-run update (policy is re-fetched each run).
  2. Check org policy constraints/iam.allowedPolicyMemberDomains if the member is outside allowed domains.
  3. Ensure the member service account actually exists (a deleted SA email can be rejected on write).
  4. Retry on transient 429/5xx; verify with `gcloud storage buckets get-iam-policy gs://<bucket>` after a successful run.
Defensive patterns

Strategy: retry

Validate before calling

if !hasPerm("storage.buckets.setIamPolicy") {
    return fmt.Errorf("identity lacks storage.buckets.setIamPolicy on gs://%s", bucket)
}
if fi.ValueOf(e.MemberServiceAccount.Email) == "" {
    return fmt.Errorf("member service account email must be set")
}

Try / catch

if err := kopsUpdate(); err != nil {
    if strings.Contains(err.Error(), "error updating GCS bucket IAM") {
        if strings.Contains(err.Error(), "403") {
            log.Print("grant storage.buckets.setIamPolicy")
        } else if strings.Contains(err.Error(), "409") || isTransient(err) {
            log.Print("policy changed concurrently or transient error; re-run update")
        }
    }
    return err
}

Prevention

When it happens

Trigger: SetPolicy(ctx, policy) returns an error after patchPolicy modified the bindings, during RenderGCE of a StorageBucketIAM task.

Common situations: 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.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/e50808c49c9d7161. Report an issue: GitHub.