VPC Service Controls

Why this matters for data engineering

Data engineers usually secure the “who” side of the problem first:

  • service account roles
  • bucket and dataset IAM
  • secret access

That is necessary, but not sufficient. Once an identity can read data, a second question appears:

Where is that data allowed to go?

VPC Service Controls is the answer for Google-managed data planes. It helps protect against:

  • compromised service accounts copying BigQuery or GCS data to another project
  • accidental exports to a non-approved destination
  • trusted identities using valid IAM to exfiltrate data outside the allowed boundary

It does not replace:

  • IAM least privilege
  • row- and column-level data security inside BigQuery
  • compute firewalling for your own VMs and private IP services

Conceptual Model

flowchart LR
    A["Caller<br/>user, workload, CI"] --> B["IAM allows request"]
    B --> C{"Inside perimeter rules?"}
    C -->|yes| D["Restricted service call succeeds"]
    C -->|no| E["VPC-SC denies data movement"]
    F["Access level"] --> C
    G["Ingress policy"] --> C
    H["Egress policy"] --> C
    I["Perimeter bridge"] --> C

The order matters:

  • IAM can say “yes”
  • VPC-SC can still say “no”

That is why VPC-SC incidents often confuse teams that only inspect IAM.

Archived Boundary in bq-wh-nb

The archived environment could not author a real perimeter because there was no visible organization scope. That is the first thing to prove before designing any rollout.

PowerShell / Linux | gcloud organizations and projects | prove the current perimeter-authoring boundary

This subsection establishes why actual perimeter creation is out of scope for this project-only credential context.

List visible organizations for the current credentials

Before any VPC-SC design work. It is typically triggered by you need to know whether the current project is attached to an organization visible to the current principal. Read-only organization inventory lookup. Determine whether Access Context Manager policy objects are even available.

gcloud organizations list --format=json
[]

No visible organization means there is no visible Access Context Manager policy boundary to administer from this credential context.

Describe the current project

Immediately after the organization check. It is typically triggered by you need to confirm the project identity and whether parent data is visible. Read-only project metadata lookup. Show the project number that would be added to a perimeter and prove that no parent is visible in the returned metadata.

gcloud projects describe \
  bq-wh-nb \
  --format=json
{
  "createTime": "2026-03-22T16:26:19.672Z",
  "lifecycleState": "ACTIVE",
  "name": "BQ Database",
  "projectId": "bq-wh-nb",
  "projectNumber": "348557092514"
}

The project exists and is active, but there is no visible parent object in this response. That is the live reason real perimeter creation is not shown in this environment.

Important conceptual note not safely executed here

Real VPC-SC creation requires:

  • an organization-level Access Context Manager policy
  • organization-level permissions such as Access Context Manager admin roles
  • a deliberate rollout plan across real projects

None of those prerequisites are visible in the current bq-wh-nb credential context.

Current product note: scoped policies and metadata limits

Current VPC Service Controls documentation distinguishes between the main organization-level access policy model and scoped policies that can delegate perimeter administration lower in the hierarchy. The same documentation also stresses that VPC-SC is designed to control data movement, not every possible metadata path, so IAM still carries part of the protection burden.

Supported Services and Product Limits

VPC-SC only works where the product supports it. Unsupported services and partially supported permission bundles are one of the most common sources of bad perimeter assumptions.

PowerShell / Linux | gcloud access-context-manager | inspect supported services and partial support signals

This subsection verifies which important data-platform APIs are listed as VPC-SC-supported and shows a live partial-support signal for roles/bigquery.admin.

List selected VPC-SC-supported services

Before drafting the perimeter’s restricted-services list. It is typically triggered by you need to confirm whether the APIs you care about participate in VPC-SC. Read-only lookup against the supported-services catalog. Validate the perimeter relevance of common data-platform APIs.

gcloud access-context-manager supported-services list \
  --filter='name:(bigquery.googleapis.com OR storage.googleapis.com OR secretmanager.googleapis.com OR run.googleapis.com)' \
  --format='table(name,title)'
NAME                          TITLE
bigquery.googleapis.com       BigQuery API
run.googleapis.com            Cloud Run Admin API
secretmanager.googleapis.com  Secret Manager API
storage.googleapis.com        Cloud Storage API

This confirms that BigQuery, Cloud Storage, Secret Manager, and Cloud Run Admin are within the supported-services catalog.

Check support status for a broad BigQuery admin role

When you want to know whether a role’s permission set aligns cleanly with VPC-SC support. It is typically triggered by A design assumes “BigQuery admin” implies uniform VPC-SC behavior. Read-only supported-permissions lookup. Surface the compatibility warning that broad roles often contain a mix of supported and unsupported permissions.

gcloud access-context-manager supported-permissions describe \
  roles/bigquery.admin \
  --format='yaml(support_status)'
---
support_status: PARTIALLY_SUPPORTED

This is the important operator signal: do not assume a large predefined role maps perfectly onto VPC-SC behavior.

Access Levels, Perimeters, Bridges, and VPC Accessible Services

The environment cannot create a real perimeter here, but the local SDK can still prove the current control-plane shape through its live help surfaces.

PowerShell / Linux | gcloud access-context-manager help | inspect the real perimeter authoring surface

This subsection uses the CLI help output as a live control-plane map.

Show what an access level evaluates

Before designing trusted-ingress conditions. It is typically triggered by you need to separate access levels from IAM and understand what attributes they use. CLI help inspection. Show the raw request attributes access levels classify.

gcloud access-context-manager levels --help | Select-String -Pattern 'IP address|device identity|time of day'
that request (e.g. IP address, device identity, time of day, etc.).

Access levels classify requests by contextual attributes. They are not role bindings and they do not grant permissions by themselves.

Show the perimeter-create surface, including bridges and VPC accessible services

Before writing Terraform or CLI rollout steps. It is typically triggered by you need to verify the current SDK surface for regular perimeters, bridge perimeters, restricted services, and VPC accessible services. CLI help inspection. Confirm that the local SDK supports the flags required by the design.

gcloud access-context-manager perimeters create --help | Select-String -Pattern '--perimeter-type|--resources|--restricted-services|--enable-vpc-accessible-services|--vpc-allowed-services' -Context 0,1
--perimeter-type=PERIMETER_TYPE; default="regular"
--resources=[RESOURCES,...] [--restricted-services=[SERVICE,...]]
--enable-vpc-accessible-services
  --vpc-allowed-services=[VPC_SERVICE,...]
--resources=[RESOURCES,...]
  Comma-separated list of resources (currently only projects, in the form
--restricted-services=[SERVICE,...]
  Comma-separated list of services to which the perimeter boundary does
--enable-vpc-accessible-services
  Whether to restrict API calls within the perimeter to those in the
--vpc-allowed-services=[VPC_SERVICE,...]
  Comma-separated list of APIs accessible from within the Service

This is the exact live surface for:

  • choosing regular or bridge
  • adding project resources
  • restricting services
  • turning on VPC accessible services

Show the bridge-versus-regular perimeter behavior

When cross-project data exchange between protected projects is part of the design. It is typically triggered by you need to know whether a regular perimeter or a bridge is the right construct. CLI help inspection. Confirm the current SDK description of bridge semantics.

gcloud access-context-manager perimeters create --help | Select-String -Pattern 'bridge|regular' -Context 0,1
--perimeter-type=PERIMETER_TYPE; default="regular"
Type of the perimeter. PERIMETER_TYPE must be one of:
bridge
  Allows resources in different regular service perimeters to import
  and export data between each other.
  A project may belong to multiple bridge service perimeters (only if
  it also belongs to a regular service perimeter).
regular
  Allows resources within this service perimeter to import and export
  A project may belong to at most one regular service perimeter.

The important design takeaway is that a bridge is for controlled exchange between regular perimeters, not a replacement for the main perimeter itself.

Show the ingress and egress policy update surface

Before planning rollout exceptions for external callers or partner delivery. It is typically triggered by you need to verify how the current SDK models ingress and egress policy updates. CLI help inspection. Show the exact update flags used to manage policy files.

gcloud access-context-manager perimeters update --help | Select-String -Pattern 'ingress|egress|--clear-ingress-policies|--set-ingress-policies|--clear-egress-policies|--set-egress-policies' -Context 0,1
[--clear-egress-policies | --set-egress-policies=YAML_FILE]
[--clear-ingress-policies | --set-ingress-policies=YAML_FILE]
These flags modify the enforced EgressPolicies of this ServicePerimeter.
--clear-egress-policies
  Empties existing enforced Egress Policies.
--set-egress-policies=YAML_FILE
  Path to a file containing a list of Egress Policies.
These flags modify the enforced IngressPolicies of this ServicePerimeter.
--clear-ingress-policies
  Empties existing enforced Ingress Policies.
--set-ingress-policies=YAML_FILE
  Path to a file containing a list of Ingress Policies.

This is the live reminder that most real VPC-SC rollouts depend on external YAML policy files, not just one short CLI command.

Show the current dry-run control surface

Before any planned cutover into enforce mode. It is typically triggered by you want to confirm whether the SDK supports dry-run configuration and explicit promotion into enforce mode. CLI help inspection. Verify the live dry-run workflow surface.

gcloud access-context-manager perimeters dry-run --help
NAME
    gcloud access-context-manager perimeters dry-run - enable management of
        dry-run mode configuration for Service Perimeters
 
DESCRIPTION
    A dry-run mode configuration (also known as the Service Perimeter
    `spec`) makes it possible to understand the impact of any changes to a
    VPC Service Controls policy change before committing the change to the
    enforcement mode configuration.
 
    Note: For Service Perimeters without an explicit dry-run mode
    configuration, the enforcement mode configuration is used as the dry-run
    mode configuration, resulting in no audit logs being generated.
gcloud access-context-manager perimeters dry-run enforce --help
DESCRIPTION
    Copies a Service Perimeter's dry-run mode configuration to its enforcement
    mode configuration and unsets the explicit dry-run spec.

This is why dry run is operationally important. Without an explicit dry-run spec, there is no preview-only signal to analyze.

Current product note: explicit dry-run specs

Current rollout guidance treats dry run as an explicit spec you manage deliberately, not as a vague preview mode. The operational goal is to observe dry-run evidence across a representative workload cycle before promoting any configuration into enforced perimeter behavior.

Confirm the perimeter membership boundary

Before assuming buckets, datasets, or service accounts can be added directly to a perimeter. It is typically triggered by A design proposes adding something other than projects as perimeter members. CLI help inspection. Prove the membership rule enforced by the current control plane.

gcloud access-context-manager perimeters --help | Select-String -Pattern 'Currently, the only allowed members of a service perimeter are projects'
Currently, the only allowed members of a service perimeter are projects.

This is a hard design constraint. Perimeters wrap projects, not individual buckets or datasets.

Current Logging State

There is no active perimeter in this environment, so the correct current logging check is “look for VPC-SC denial evidence and confirm that none exists.”

PowerShell / Linux | gcloud logging | check for current VPC-SC denial evidence

During incident triage or after a dry-run or enforce rollout. It is typically triggered by you need to know whether current audit logs show VPC-SC metadata. Read-only Cloud Logging query. Distinguish “no perimeter denials exist” from “the query itself failed.”.

gcloud logging read \
  'protoPayload.metadata.@type="type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata" AND resource.labels.project_id="bq-wh-nb"' \
  --limit=5 \
  --format=json
[]

That is the correct current-state answer for this project: there is no visible VPC-SC denial evidence because there is no visible perimeter rollout here.

Rollout Guidance for Real Environments

When the organization boundary exists, the safest rollout order is:

  1. Confirm the access policy and trusted projects.
  2. Identify supported services and known unsupported or partially supported paths.
  3. Create explicit dry-run specs first.
  4. Observe dry-run audit evidence over a full pipeline cycle.
  5. Add missing ingress and egress exceptions deliberately.
  6. Promote dry-run config into enforce mode only after the dry-run logs are clean.

Current product note: Cloud Run perimeter compliance

Cloud Run inside a service perimeter has additional runtime requirements beyond IAM. Current guidance requires Direct VPC egress or a Serverless VPC Access connector, all egress routed through the VPC path, and internal-only ingress; setting ingress to all disables VPC Service Controls enforcement for the service.

Data-Engineering Design Patterns

ScenarioCorrect VPC-SC patternWhy it worksMain risk if skipped
BigQuery extract must stay internalRestrict BigQuery and Cloud Storage, no general egressBlocks export to arbitrary projects or bucketsAny granted reader can exfiltrate data
Partner delivery is requiredRegular perimeter plus explicit egress policy or bridge to an approved projectMakes the export path intentional and reviewableOperators bypass controls with ad hoc copy jobs
Airflow runs outside the protected data perimeterEither move Airflow inside the perimeter or add narrowly scoped ingress/egress rulesSeparates orchestration access from data exfiltration protectionAirflow succeeds in dev and breaks in prod cutover
GitHub Actions deploys Cloud Run but should not read dataKeep CI outside the data perimeter, use WIF for control plane, and avoid data-plane permissionsDeployment path stays separate from data pathCI accidentally becomes a data reader
Cloud Run inside the perimeter needs only a small API setUse VPC accessible services plus narrow runtime IAMReduces internal API surfaceInternal workloads can still call too many services
Two protected projects must exchange dataUse regular perimeters plus a perimeter bridgeKeeps both projects protected while allowing approved sharingTeams flatten everything into one oversized perimeter

Troubleshooting and Incident Response

SymptomMost likely layerFirst checkNext action
IAM looks correct but BigQuery export still failsVPC-SC or egressCheck audit logs for VpcServiceControlAuditMetadataInspect restricted services and egress rules
External trusted user cannot reach a protected serviceAccess level or ingressReview access levels and ingress policiesFix trusted context or ingress spec
Cross-project protected-to-protected traffic failsMissing bridge or egress ruleCheck whether the projects share a regular perimeter or bridgeAdd bridge or explicit egress path
Dry-run generated no useful evidenceNo explicit dry-run specReview perimeters dry-run stateCreate an explicit dry-run spec before observing
A design assumes every BigQuery admin action is perimeter-governedSupported-product misunderstandingReview supported-permissionsBreak broad workflows into supported service and permission paths

Quick Reference

NeedUseAvoid
Preview change impactExplicit dry-run perimeter specFirst change in enforce mode
Allow cross-perimeter exchangePerimeter bridge or narrow egress policyOne giant flat perimeter
Reduce internal API surfaceVPC accessible servicesAssuming perimeter restriction already limits every internal API
Admit only trusted external contextsAccess levels plus ingress policyTreating IAM role membership as equivalent to trusted context
Diagnose denialsAudit log query for VpcServiceControlAuditMetadataLooking only at IAM bindings

GCP VPC Service Controls References