This check flags Compute Engine disk images that grant access to allAuthenticatedUsers or allUsers, exposing your golden images to the entire internet. Disk images often carry baked-in secrets, source code, and credentials. Fix it by removing the public IAM bindings on the image with gcloud compute images remove-iam-policy-binding.
Disk images are one of the most overlooked assets in a GCP environment. They sit quietly in your project, get referenced when someone spins up a new instance, and rarely show up in security reviews. But an image is a full snapshot of a boot disk, which means it can contain everything that was on that disk at capture time: application source, configuration files, SSH keys, API tokens, and database credentials. When an image is made public, all of that becomes downloadable by anyone with a Google account, or in the worst case, by anyone at all.
The VM Disk Image Is Publicly Accessible check looks at the IAM policy attached to each Compute Engine image and raises a finding when that policy includes either of the two special principals Google uses to represent the public.
What this check detects
Compute Engine images have their own resource-level IAM policy, separate from the project policy. The check inspects each image's policy bindings and flags the image if it finds either of these members:
allUsers— anyone on the internet, no authentication required.allAuthenticatedUsers— anyone with a valid Google account, including accounts that have nothing to do with your organization.
If one of those principals is granted a role like roles/compute.imageUser, that image can be used to launch instances by people outside your trust boundary, and depending on how it was shared, its contents can be inspected.
Note: Public sharing of images is a legitimate feature. Google itself publishes the base OS images (Debian, Ubuntu, COS) this way through public projects. The problem is when a private image, built from your own infrastructure, gets the same treatment by mistake.
Why it matters
An image is not just an OS install. It is whatever the source disk contained when the snapshot was taken. Teams build "golden images" precisely so they don't have to configure everything from scratch on every boot, which means images tend to accumulate the things you least want exposed.
What attackers actually do with this
Public disk images are a known reconnaissance target. An attacker who finds a publicly shared image in your project can launch their own instance from it in a project they control, then mount and explore the disk at their leisure. No noisy access to your environment is required. From there the typical loot includes:
- Hardcoded service account keys and API tokens in application config.
.envfiles,credentials.json, and CI/CD secrets that were never cleaned up.- Private SSH keys and known_hosts entries that map out internal infrastructure.
- Database connection strings with embedded passwords.
- Proprietary source code and internal documentation.
Even allAuthenticatedUsers, which sounds restrictive because it requires a login, is effectively public. Anyone can create a free Google account in under a minute. There is no meaningful gate.
Danger: A single leaked service account key inside a public image can be enough for full project compromise. If that key has broad IAM roles, the blast radius is your entire GCP project, not just the one VM the image came from.
Business impact
Beyond the direct breach risk, exposed images are a compliance failure. Standards like CIS GCP, SOC 2, and ISO 27001 all expect access controls on storage of system images. A publicly accessible golden image is the kind of finding that turns an audit into a remediation sprint.
How to fix it
Remediation has two parts: confirm the exposure, then remove the public binding. Start by checking the current IAM policy on the image.
gcloud compute images get-iam-policy IMAGE_NAME \
--project=PROJECT_ID \
--format=json
Look in the output for bindings that include allUsers or allAuthenticatedUsers:
{
"bindings": [
{
"members": [
"allUsers"
],
"role": "roles/compute.imageUser"
}
],
"etag": "BwXyZ..."
}
Remove the public binding
Strip the offending principal from the specific role it was granted. Run this once per public member you found.
Warning: If legitimate workflows depend on this image being shared, removing the binding will break their ability to launch new instances. Confirm there are no intentional cross-project consumers before you pull access, or replace the broad binding with a scoped grant to specific accounts.
# Remove public, unauthenticated access
gcloud compute images remove-iam-policy-binding IMAGE_NAME \
--project=PROJECT_ID \
--member='allUsers' \
--role='roles/compute.imageUser'
# Remove "any Google account" access
gcloud compute images remove-iam-policy-binding IMAGE_NAME \
--project=PROJECT_ID \
--member='allAuthenticatedUsers' \
--role='roles/compute.imageUser'
Grant access correctly, if sharing is intended
If another team or project genuinely needs the image, share it with the specific identity rather than the world:
gcloud compute images add-iam-policy-binding IMAGE_NAME \
--project=PROJECT_ID \
--member='serviceAccount:[email protected]' \
--role='roles/compute.imageUser'
Console steps
- Open Compute Engine → Images in the Google Cloud console.
- Select the affected image and open the Permissions panel (or the info pane).
- Find any principal listed as allUsers or allAuthenticatedUsers.
- Remove that principal and save.
Danger: Assume any image that was public has been copied. Removing the binding stops future access but does nothing about copies already taken. Rotate every credential, key, and token that could have been baked into the image. Treat the secrets as compromised, not merely exposed.
How to prevent it from happening again
Manual remediation is a one-time fix. To stop this from recurring, enforce the rule at the org level and catch it before deployment.
Org policy: block public IAM grants
The iam.allowedPolicyMemberDomains constraint restricts which domains can appear in IAM bindings. When enforced, attempts to add allUsers or allAuthenticatedUsers are rejected outright.
gcloud resource-manager org-policies allow \
iam.allowedPolicyMemberDomains \
YOUR_CUSTOMER_ID \
--organization=ORG_ID
Note: allowedPolicyMemberDomains takes your Cloud Identity customer ID, which scopes IAM members to your org's domains. Find it with gcloud organizations list and the Cloud Identity API. This one constraint blocks public sharing across nearly every GCP resource type, not just images.
Terraform: define images without public access
If you manage images as code, keep their IAM explicit and reviewable. Never grant imageUser to a public member in a module that could be reused.
resource "google_compute_image_iam_member" "builder_access" {
project = var.project_id
image = google_compute_image.golden.name
role = "roles/compute.imageUser"
member = "serviceAccount:${var.builder_sa}"
}
CI/CD policy gate
Add a policy-as-code check to your pipeline so a plan that introduces a public image binding fails review. With Conftest and OPA, a rule like this rejects the dangerous members:
package main
deny[msg] {
resource := input.resource_changes[_]
resource.type == "google_compute_image_iam_member"
member := resource.change.after.member
member == "allUsers"
msg := sprintf("Image %s grants public access via allUsers", [resource.address])
}
deny[msg] {
resource := input.resource_changes[_]
resource.type == "google_compute_image_iam_member"
member := resource.change.after.member
member == "allAuthenticatedUsers"
msg := sprintf("Image %s grants access to all Google accounts", [resource.address])
}
Tip: Pair the build-time gate with continuous monitoring. Lensix scans your live GCP environment for this check, so even images created outside Terraform (by Packer, manual snapshots, or another team) get caught after the fact. Defense in depth means catching it in the pipeline and in production.
Best practices
- Keep secrets out of images entirely. Use Secret Manager or inject credentials at boot through instance metadata and Workload Identity. An image with no baked-in secrets is far less dangerous if it ever leaks.
- Use a dedicated images project. Isolate golden images in a project with tight IAM and the public-member org policy enforced. This shrinks the surface where an accidental public grant can do damage.
- Audit image IAM regularly. Images outlive the people who created them. Run a scheduled scan over
get-iam-policyacross all projects, or rely on a continuous scanner to flag drift. - Apply least privilege on the share. When you must share, grant
roles/compute.imageUserto named service accounts, never to whole domains and certainly never to the public principals. - Set a retention and cleanup policy. Stale images accumulate risk. Use deprecation and deletion schedules so old images don't linger with forgotten access grants.
- Enable Cloud Audit Logs for Compute. If an image is ever made public, you want a log entry showing who did it and when, so you can scope the incident response.
Public disk images are a quiet failure mode: nothing breaks, no alert fires, and the exposure can sit unnoticed for months. The fix is cheap, the org-level prevention is a single constraint, and the payoff is closing off one of the easiest reconnaissance paths an attacker has into your GCP estate.

