kubernetes/kops · error
cannot apply changes to ServiceAccount: %v
Error message
cannot apply changes to ServiceAccount: %v
What it means
After handling Description/DisplayName changes, RenderGCE nils them out and checks that no other fields remain in the changes struct (via reflect.DeepEqual against an empty ServiceAccount). If leftover changes remain, kOps has no API to apply them to an existing service account (GCP service accounts are nearly immutable), so it fails rather than silently ignoring drift.
Source
Thrown at upup/pkg/fi/cloudup/gcetasks/serviceaccount.go:160
if changes.Description != nil || changes.DisplayName != nil {
sa := &iam.ServiceAccount{
Email: email,
Description: fi.ValueOf(e.Description),
DisplayName: fi.ValueOf(e.DisplayName),
}
_, err := cloud.IAM().ServiceAccounts().Update(ctx, fqn, sa)
if err != nil {
return fmt.Errorf("error creating ServiceAccount %q: %w", fqn, err)
}
changes.Description = nil
changes.DisplayName = nil
}
empty := &ServiceAccount{}
if !reflect.DeepEqual(empty, changes) {
return fmt.Errorf("cannot apply changes to ServiceAccount: %v", changes)
}
}
return nil
}
type terraformServiceAccount struct {
AccountID *string `cty:"account_id"`
ProjectID *string `cty:"project"`
Description *string `cty:"description"`
DisplayName *string `cty:"display_name"`
}
func (_ *ServiceAccount) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *ServiceAccount) error {
shared := fi.ValueOf(e.Shared)
if shared {
// Not terraform owned / managed
return nilView on GitHub (pinned to 4c8573c808)
Solutions
- Identify the leftover changed field in the printed `changes` value; if it is Email, treat it as create-new/delete-old rather than an in-place rename.
- Create a new task (or new cluster add-on name) with the new email and remove the old service account explicitly.
- If Shared changed, revert the spec change or delete and recreate the task so Find/apply agree on lifecycle ownership.
- If the diff is spurious (fields kOps should have ignored), report/fix the Find method to normalize those fields (compare with serviceaccount.go:80-88).
Example fix
// before: trying to rename in place
Email: fi.String("new-robot@example-project.iam.gserviceaccount.com") // same task name
// after: new task / new email identity, delete old one separately
Name: fi.String("newrobot-sa"), Email: fi.String("new-robot@example-project.iam.gserviceaccount.com") Defensive patterns
Strategy: validation
Validate before calling
// Reject in-place email "rename" before applying
if existing != nil && existing.Email != nil && e.Email != nil && *existing.Email != *e.Email {
return fmt.Errorf("service account email is immutable; create a new task for %q and delete %q", *e.Email, *existing.Email)
} Type guard
func onlyMutableFieldsChanged(changes *ServiceAccount) bool {
return changes.Email == nil && changes.Shared == nil && changes.Name == nil
} Try / catch
if err := kopsUpdate(); err != nil {
if strings.Contains(err.Error(), "cannot apply changes to ServiceAccount") {
log.Print("unsupported field change; print changes and split into create-new/delete-old")
}
return err
} Prevention
- Never rename a service account email in place — create a new task and delete the old account.
- Don't toggle Shared on an existing task; recreate the task instead.
- Review `kops update cluster --dry-run` diffs before applying.
- If you see a spurious diff, compare against Find's normalization of Name/Shared/Lifecycle.
When it happens
Trigger: Applying a non-shared ServiceAccount task where `a != nil` and the computed `changes` still contains non-zero fields after Description/DisplayName are cleared — i.e. changes to immutable fields like Email, Name, Shared, or Lifecycle.
Common situations: Renaming the service account email in the cluster spec while reusing the same task name (email is the identity, it cannot be updated in place); changing Shared from false to true (or vice versa) without recreating; a Find-phase bug failing to copy back a field causing spurious diffs.
Related errors
- ServiceAccount with email %q not found
- error creating ServiceAccount %q: %w
- created ServiceAccount did not have expected email; got %q;
- error listing ServiceAccounts %w
- Invalid service account email '%s'
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/814974ad073eb84d.
Report an issue: GitHub.