In this Article
- What Is VPC Service Controls?
- The Problem VPC SC Was Built to Solve
- The Core Building Blocks
- How a Perimeter Actually Works
- Access Levels: Who Gets In
- Controlling the Edges: Ingress and Egress Rules
- 5 Configuration Gotchas
- VPC Service Controls Best Practices and Next Steps
I've worked on client projects where VPC SC was either already enforced or needed to be rolled out from scratch. Developers were blocked from accessing resources they expected to reach. CI/CD service accounts failed to create or modify infrastructure. Sometimes I got blocked in the environment myself. This article covers what I learned so you can come prepared when working with VPC SC.
What Is VPC Service Controls?
GCP VPC Service Controls (VPC SC) is a Google Cloud security feature that lets you define a service perimeter: a logical boundary around a set of GCP projects and services. Requests that cross that boundary are evaluated against a set of rules before being allowed through.
VPC SC operates at the Google Cloud API layer, not the network level. It doesn't inspect packets or route traffic; it intercepts API calls to GCP services and decides whether they're allowed based on context: who is making the request, where they're coming from, and whether they're inside or outside the perimeter.
That distinction matters because VPC SC can protect cloud-native services (like BigQuery or Cloud Spanner) that have no concept of a network interface. You can't put a firewall in front of BigQuery's API endpoint, but you can wrap it in a VPC SC perimeter.
The Problem VPC SC Was Built to Solve
Imagine a developer at your company has their GCP credentials stolen. The attacker now has an access token scoped to BigQuery. Your IAM policies are solid; they can only read datasets they're supposed to. But nothing stops them from running a query and piping the results to their own GCP project. This is precisely the threat VPC SC was designed to prevent.
IAM alone cannot close this gap. BigQuery, Cloud Storage, Pub/Sub, and dozens of other Google Cloud services are API-driven. By default, those APIs are reachable from anywhere on the internet, by anyone with valid credentials. IAM controls who can call an API. VPC SC controls from where and under what context that call is even allowed to reach the service in the first place.
Together, they implement a zero-trust security model for Google Cloud APIs: evaluating both the identity and the origin of every single request.
The Core Building Blocks
VPC SC is organized around three main concepts. Understanding each one before looking at the full picture makes the rest much easier.
Access Policy
An Access Policy is the top-level container for all your VPC SC configurations within a Google Cloud organization. Think of it as the rulebook: all perimeters and access levels live inside it.
There is exactly one organization-level Access Policy per organization. This is a hard platform constraint, not a convention. It is managed centrally by a security or platform team.
Organizations that need to delegate VPC SC administration can also create up to 50 scoped access policies, each targeting a specific folder or project. Note that a scoped policy's scope cannot be changed after creation.
Service Perimeter
A Service Perimeter is the security boundary itself. It wraps one or more GCP projects and specifies which GCP services (APIs) inside those projects are protected.
You define a perimeter by listing:
- Which projects are inside it
- Which GCP services (APIs) are restricted within it, such as
bigquery.googleapis.comandstorage.googleapis.com
Once a service is restricted inside a perimeter, calls to that service that originate from outside the perimeter are blocked by default, even if the caller has the right IAM permissions. To allow outside access, you need explicit exceptions. You'll configure these after understanding how the perimeter evaluates requests.
Access Level
An Access Level defines a set of conditions that, when met, grant a request special trust, allowing it to cross the perimeter boundary even from outside. Conditions can be based on IP ranges, device state, or identity. Access Levels are reusable: define them once in the Access Policy and reference them across multiple perimeters.
These three concepts work as a single evaluation system; the next section shows what that looks like when a real request hits the boundary.
How a Perimeter Actually Works
Here is the mental model that explains how VPC SC works.
When a request hits a GCP API that is protected by a perimeter, Google evaluates a simple question: Is this request allowed to cross the boundary?
It is allowed if any of the following is true:
The request originates from inside the perimeter: the caller (user or service account) and the resource are both within the same perimeter. This is the happy path for internal workloads.
The request meets the conditions of an explicit exception rule: an ingress rule or a perimeter-level access level allows traffic from a specific external source. For example, an on-premises network connected via Cloud VPN can be granted access by routing API calls through the Restricted Google APIs virtual IP (
restricted.googleapis.com) and referencing that network in an access level or ingress rule. The access level satisfies the network-origin condition; the calling identity is still evaluated independently.
If neither condition is met, the request is denied, even if IAM would have allowed it.
The Corporate VPN entry point is labeled "ingress rule + access level." The access level is the mechanism that determines whether that source qualifies as trusted.
Access Levels: Who Gets In
Access Levels let you express trust based on context, not just identity. They answer the question: even if you know who is making a request, do you trust the conditions from which they're making it?
IP-Based Access Levels
The most common type. You define a list of trusted IP ranges (your corporate office, VPN exit nodes, CI/CD infrastructure) and requests from those ranges are granted the access level.
Save the following as a YAML file and apply it with:
gcloud access-context-manager levels create corporate_vpn \
--policy=POLICY_ID \
--basic-level-spec=access-level.yaml \
--title="Corporate VPN"
- ipSubnetworks:
- 203.0.113.0/24 # Corporate office
- 198.51.100.0/24 # VPN exit node
This access level can then be referenced in a perimeter's ingress rules, or set as a required condition for accessing the perimeter at all.
Device-Based Access Levels
Device-based access levels are more powerful but require Endpoint Verification. You can require that the device making the request:
- Is managed by your organization
- Has an encrypted disk
- Runs a current OS version
- Has screen lock enabled
This is the foundation of a Zero Trust posture for GCP access: identity + device context instead of just identity.
Combining Conditions
Access levels can combine conditions with AND or OR logic. For example: "the request must come from the corporate IP range AND from a managed device." This means an attacker with stolen credentials still needs a managed device on the corporate network to get through.
Access levels applied directly to a perimeter independently authorize external requests that meet their conditions. Ingress and egress rules are a complementary mechanism that adds control over which identities, services, and API methods can cross the boundary.
Controlling the Edges: Ingress and Egress Rules
The perimeter boundary has two directions, and each is controlled independently.
Ingress Rules
An Ingress Rule allows traffic from outside the perimeter to reach a protected resource inside it.
Example scenario: Your data analysts connect from on-premises via Cloud VPN and need to query BigQuery, which lives inside the perimeter.
An ingress rule for this would say:
- From: Requests originating from your corporate VPN IP range, or from a specific project outside the perimeter
- To: The BigQuery API in the protected project
-
Identity: Only members of the
data-analysts@yourcompany.comgroup
Save the following as ingress.yaml and apply it with gcloud access-context-manager perimeters update PERIMETER_NAME --policy=POLICY_ID --set-ingress-policies=ingress.yaml:
Note: Replace
POLICY_IDwith your Access Policy numeric ID,
andPROJECT_NUMBERwith the numeric project ID (not project name).
- ingressFrom:
identities:
- group:data-analysts@yourcompany.com
sources:
- accessLevel: accessPolicies/POLICY_ID/accessLevels/corporate_vpn
ingressTo:
operations:
- serviceName: bigquery.googleapis.com
methodSelectors:
- method: "*"
resources:
- projects/PROJECT_NUMBER
Egress Rules
An Egress Rule allows traffic from inside the perimeter to reach something outside it.
Example scenario: A Dataflow job inside the perimeter writes results to a Cloud Storage bucket that lives in a separate, unprotected project (perhaps a shared reporting bucket).
Without an egress rule, that write would be blocked. The egress rule says:
- From: The Dataflow service account inside the perimeter
- To: The Cloud Storage API in the external project
Note: Replace
EXTERNAL_PROJECT_NUMBERwith the numeric project ID (not project name),
andPROJECT_IDwith the string project ID of the service account's project.
- egressFrom:
identities:
- serviceAccount:dataflow-sa@PROJECT_ID.iam.gserviceaccount.com
egressTo:
operations:
- serviceName: storage.googleapis.com
methodSelectors:
- method: "google.storage.objects.create"
resources:
- projects/EXTERNAL_PROJECT_NUMBER
The key point: by default, everything crossing the perimeter boundary is denied. Ingress and egress rules are the explicit exceptions you carve out.
5 Configuration Gotchas
VPC SC is powerful, but misconfiguration can silently block workloads you depend on. Here are the five gotchas that trip up teams most often.
1. Service Agents and Management Tools Get Blocked by Default
Two categories of callers get blocked when enforcement is enabled, and most teams don't account for them.
GCP service agents: When you submit a Dataflow job, Dataflow calls Cloud Storage, BigQuery, and Pub/Sub using a Google-managed service account. If those services are inside your perimeter and the service agent is not in your ingress rules, jobs start failing with cryptic errors. Check which service agents a protected service relies on before enforcing; Google's documentation lists service dependencies per product.
Management plane tools: Cloud Console and managed services like Cloud Composer make API calls to GCP services as part of normal operation. When you enforce a perimeter, the Console UI for a protected service returns errors, and orchestration services lose access to protected buckets.
The fix: Enable dry run mode and let it run long enough to cover all workload paths, including infrequent batch jobs, before switching to enforced mode. This is the single biggest cause of production incidents during initial rollout. To audit what would be blocked, use this Cloud Logging filter:
log_id("cloudaudit.googleapis.com/policy") AND severity="error" AND protoPayload.metadata.dryRun="true"
Dry run catches both categories before they become production incidents.
2. Service Account Chains Are Tricky
In multi-step workflows, every service account that directly calls a protected API needs its own ingress access, not just the final one.
A Cloud Function (running as SA A) that triggers a BigQuery export (running as SA B) involves two separate API calls: SA A calls the trigger endpoint, SA B calls BigQuery. If only SA B is in your ingress rules, SA A's call is blocked before the export ever starts. Add an ingress rule for each service account that touches a protected service.
3. There Is No Perimeter-Level Override
Unlike IAM, there is no "super-admin" that bypasses VPC SC by default. Even Organization Admins are blocked by perimeter rules unless explicitly granted access via an access level. This is by design (it prevents privilege escalation), but it surprises teams who assume admin access is always unrestricted.
4. Restricting a Service Affects All Projects in the Perimeter
If you list bigquery.googleapis.com as a restricted service in a perimeter containing five projects, VPC SC applies to all of them. There is no per-project granularity within a single perimeter; if you need different rules for different projects, use separate perimeters.
For cross-perimeter communication, use ingress and egress rules: they provide fine-grained control over which identities, services, and projects can communicate.
5. Violations Only Show Up in Cloud Audit Logs
VPC SC violations appear in Cloud Audit Logs under the cloudaudit.googleapis.com/policy log type. Set up a log-based alert for these from day one, even before enforcement. You'll catch misconfigurations early and have visibility into exactly which calls are hitting the perimeter boundary.
VPC Service Controls Best Practices and Next Steps
A working VPC SC setup ties together three controls: perimeters that name the protected services, access levels that define trusted sources, and ingress/egress rules that specify who can cross the boundary.
For teams storing sensitive data in GCP services like BigQuery or Cloud Storage, VPC SC addresses access control requirements that IAM alone does not address. Google lists VPC Service Controls as a covered service under its HIPAA BAA program, and recommends it for teams working toward FedRAMP High or DoD IL4 compliance.
Ingress and egress rules, access level conditions, service dependencies - there's a lot to get right. But the incremental approach works: start with dry run mode, add one perimeter at a time, and treat every violation log entry as a signal to investigate rather than a fire to put out.
To get started:
- Read the VPC Service Controls overview to understand available service restrictions and quotas
- Create a perimeter in dry run mode on a non-production project
-
Define your first access level using
gcloud access-context-manager levels create - Review violation logs before enforcing, and work through any unexpected denials systematically
A solid VPC SC setup won't eliminate all security risks. But it closes the two attack paths that raw IAM leaves open: credential theft leading to exfiltration, and token reuse from untrusted networks.














