This check flags GCP VPC subnets without Private Google Access enabled, which forces instances that lack public IPs to route Google API traffic over the public internet or fail outright. Turn it on per subnet with gcloud compute networks subnets update to keep internal-only workloads reaching Google services privately.
Private Google Access is one of those settings that quietly determines whether half your internal infrastructure can talk to Google APIs at all. It is off by default on every new subnet, and most teams never notice until an instance without an external IP suddenly cannot reach Cloud Storage, Artifact Registry, or the logging API. This check catches that gap before it turns into a production incident.
What this check detects
The vpc_noprivategoogleaccess check inspects every subnet in your GCP VPC networks and reports any subnet where the Private Google Access property is set to false.
Private Google Access controls how VM instances without external IP addresses reach Google APIs and services. When it is enabled on a subnet, those instances can send requests to public Google endpoints (like storage.googleapis.com) using their internal IPs, with the traffic staying on Google's network. When it is disabled, an instance with no public IP has no route to those endpoints, so the calls time out.
Note: Private Google Access is a subnet-level setting, not a project or VPC setting. You can have it enabled on one subnet and disabled on another within the same network. The check evaluates each subnet independently.
It is worth being precise about what this is and is not. Private Google Access is not the same as Private Service Connect or VPC Service Controls. It does not change permissions or encrypt anything that was not already encrypted. It simply provides a route to default Google API domains and selected Google-managed services for instances that have no public-facing network path.
Why it matters
The most common reason teams hit this is a deliberate security decision that backfires. A good hardening practice is to deploy VMs without external IP addresses so they cannot be reached directly from the internet and cannot initiate arbitrary outbound connections. That is exactly the posture you want for application servers, batch workers, and database hosts.
But those same instances usually still need Google APIs. Consider what breaks when Private Google Access is off and an instance has no public IP:
- Cloud Logging and Monitoring agents cannot ship logs or metrics, so the host goes dark in your observability stack.
- Container pulls fail when nodes try to fetch images from Artifact Registry or Container Registry, breaking GKE node startup and Cloud Run deployments backed by private networking.
- Cloud Storage access stops for backup jobs, data pipelines, and anything reading config from a bucket.
- OS package and update commands hang when they reach for Google-hosted package mirrors.
From a security standpoint, the failure mode is what makes this risky. When engineers discover that an internal VM cannot reach an API, the fastest fix they reach for is often the wrong one: they assign the instance an external IP "just to get it working." Now you have a directly addressable VM on the public internet, which is precisely the exposure you removed the external IP to avoid. The missing subnet setting becomes the root cause of a much larger attack surface.
Warning: A subnet without Private Google Access does not produce an obvious error in the console. Instances launch fine. The breakage only shows up at runtime when something tries to reach a Google API, which makes these failures slow to diagnose during an incident.
There is also a cost and reliability angle. If your VMs do have external IPs but you would rather keep API traffic off the public internet, leaving Private Google Access off means that traffic egresses publicly and can incur internet egress charges, and it depends on the public path staying healthy. Enabling it keeps that traffic on Google's backbone.
How to fix it
Enabling Private Google Access is a single, non-disruptive update to the subnet. It does not require recreating the subnet or restarting instances, and existing connectivity is unaffected.
Using the gcloud CLI
First, find which subnets have it disabled across a region:
gcloud compute networks subnets list \
--filter="privateIpGoogleAccess=false" \
--format="table(name, region, network, privateIpGoogleAccess)"
Then enable it on the target subnet. You must specify the region:
gcloud compute networks subnets update SUBNET_NAME \
--region=REGION \
--enable-private-ip-google-access
Confirm the change took effect:
gcloud compute networks subnets describe SUBNET_NAME \
--region=REGION \
--format="value(privateIpGoogleAccess)"
A return value of True means you are done.
Using the Google Cloud Console
- Go to VPC network and select VPC networks.
- Click the network that contains the subnet, then click the subnet name.
- Click Edit.
- Set Private Google Access to On.
- Click Save.
Note: Private Google Access covers the default domains for most Google APIs and the standard googleapis.com endpoints. If your instances also need to resolve those domains to the right addresses, make sure your DNS returns the documented restricted or private VIP ranges, or configure private DNS zones for googleapis.com if you are using VPC Service Controls.
Using Terraform
If you manage subnets as code, set private_ip_google_access on the resource:
resource "google_compute_subnetwork" "app" {
name = "app-subnet"
ip_cidr_range = "10.10.0.0/20"
region = "us-central1"
network = google_compute_network.main.id
private_ip_google_access = true
}
Run terraform plan to confirm the only change is the access flag, then apply.
Tip: If you have many subnets to update at once, loop over the list output with gcloud rather than clicking through the console. Pipe the names and regions into a short script: gcloud compute networks subnets list --filter="privateIpGoogleAccess=false" --format="csv[no-heading](name,region)" and feed each pair into the update command.
How to prevent it from happening again
Since the setting is off by default, every new subnet is a fresh chance to ship the misconfiguration. The fix is to make "enabled" the default in whatever creates your subnets.
Bake it into your IaC modules
If you provision networking through a shared Terraform module, set private_ip_google_access = true as the module default rather than a per-call input. Teams consuming the module then get the correct behavior without thinking about it.
Gate it with policy as code
Add an Open Policy Agent / Conftest rule to your CI pipeline so a Terraform plan that creates a subnet with the flag off fails the build:
package terraform.gcp.subnet
deny[msg] {
resource := input.resource_changes[_]
resource.type == "google_compute_subnetwork"
resource.change.after.private_ip_google_access == false
msg := sprintf(
"Subnet '%s' must enable private_ip_google_access",
[resource.change.after.name],
)
}
Enforce it at runtime with Organization Policy and continuous checks
Policy as code in CI only catches resources created through that pipeline. To catch click-ops and out-of-band changes, run a continuous scan against the live environment. This is where the Lensix vpc_noprivategoogleaccess check fits, flagging any subnet that drifts back to the disabled state regardless of how it was created.
Tip: Pair the CI gate with the runtime scan. The pipeline blocks the obvious cases early and cheaply, and the scheduled scan acts as the backstop for everything the pipeline never saw.
Best practices
- Default new subnets to enabled. There is rarely a reason to leave Private Google Access off, and the cost of leaving it on is effectively zero. Treat "on" as the standard.
- Pair it with no-external-IP instances. Private Google Access is most valuable as the enabler for a stricter posture where workload VMs have no public IPs at all. Use the two together rather than in isolation.
- Document the DNS expectations. If you adopt VPC Service Controls or restricted API access, your instances need DNS that resolves Google domains to the right private VIPs. Note this in your network runbook so the next engineer does not chase a phantom outage.
- Do not confuse it with broader controls. Private Google Access is about reachability, not authorization. You still need IAM and, where appropriate, VPC Service Controls to govern what instances are allowed to do once they can reach an API.
- Audit per region. Subnets are regional, so a clean us-central1 tells you nothing about europe-west1. Sweep all regions you operate in.
Enabling Private Google Access is one of the lowest-effort, lowest-risk fixes in GCP networking. It removes a common reason engineers reach for public IPs, keeps internal workloads functioning, and quietly keeps your API traffic off the public internet. Turn it on everywhere, default it on in code, and let a continuous check make sure it stays that way.

