Review GCP VM service accounts and effective permissions

Attach an account suited to the workload and minimize its actual IAM permissions.

Description

The default Compute Engine service account can have broad permissions depending on IAM policy, but being the default account does not itself grant access to every API. A dedicated account limited to the resources a workload needs makes permission reviews easier.

Changing the account name alone does not establish least privilege. Review its IAM roles and who can use the account.

Potential impact

  • A compromised VM can affect resources within its attached account’s permissions.
  • Sharing an account across workloads can make permission changes and attribution harder to assess.

Remediation

  • For VMs that need Google Cloud APIs, specify a suitable dedicated account through service_account.email and grant minimal IAM roles. If API access is unnecessary, consider whether an account needs to be attached at all.
  • Review actual broad roles such as Editor and dependent workloads before reducing unnecessary permissions on the default account. Avoid disrupting existing services by immediately deleting or disabling it.

Examples

Replace the historical image, network and account emails with actual deployment values. IAM roles are omitted; scopes do not grant permissions by themselves. Using cloud-platform with least-privilege IAM roles is also a recommended configuration.

Before

hcl
resource "google_compute_instance" "vm" {
  name         = "test"
  machine_type = "e2-medium"
  zone         = "us-central1-a"

  boot_disk {
    initialize_params {
      image = "debian-cloud/debian-9"
    }
  }

  network_interface {
    network = "default"

    access_config {}
  }

  service_account {
    email  = "123456789-compute@developer.gserviceaccount.com"
    scopes = ["cloud-platform"]
  }
}

After

hcl
resource "google_compute_instance" "vm" {
  name         = "test"
  machine_type = "e2-medium"
  zone         = "us-central1-a"

  boot_disk {
    initialize_params {
      image = "debian-cloud/debian-9"
    }
  }

  network_interface {
    network = "default"

    access_config {}
  }

  service_account {
    email  = "app-runtime@my-project.iam.gserviceaccount.com"
    scopes = ["userinfo-email", "compute-ro", "storage-ro"]
  }
}

Explanation:

  • Before: The default account and cloud-platform scope are used. Actual permissions also depend on IAM roles and other controls.
  • After: A dedicated account and narrower scopes are specified. Review the IAM roles required by the workload separately.

References