Home DevOps VPC Service Controls: Perimeter, Ingress/Egress Rules, and Dry-Run Mode
Advanced 7 min · July 12, 2026

VPC Service Controls: Perimeter, Ingress/Egress Rules, and Dry-Run Mode

A production-focused guide to VPC Service Controls: Perimeter, Ingress/Egress Rules, and Dry-Run Mode on Google Cloud Platform..

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Notes here come from systems that actually shipped.

Follow
Verified
production tested
July 12, 2026
last updated
2,073
articles · all by Naren
Before you start⏱ 30 min
  • Google Cloud Platform organization with Access Context Manager API enabled, gcloud CLI (version 400+), Organization Admin role or Access Context Manager Admin role, basic understanding of IAM and VPC networking, familiarity with Cloud Logging
✦ Definition~90s read
What is VPC Service Controls?

VPC Service Controls is a Google Cloud security feature that lets you define perimeters around managed services to prevent data exfiltration and unauthorized access. It matters because it enforces context-aware access policies beyond IAM, blocking data movement across perimeters even if credentials are compromised.

VPC Service Controls: Perimeter, Ingress/Egress Rules, and Dry-Run Mode is like having a specialized tool that handles vpc service controls so you don't have to build and manage it yourself — it just works out of the box with Google Cloud's infrastructure.

Use it when you need to protect sensitive data in GCP services like BigQuery, Cloud Storage, or Bigtable from being copied to external networks or projects.

Plain-English First

VPC Service Controls: Perimeter, Ingress/Egress Rules, and Dry-Run Mode is like having a specialized tool that handles vpc service controls so you don't have to build and manage it yourself — it just works out of the box with Google Cloud's infrastructure.

You locked down IAM, rotated keys, and enabled audit logs. Then a junior engineer ran bq extract to dump a production dataset to a public bucket. No credentials leaked — just a misconfigured job that cost you a compliance violation. VPC Service Controls exists to make that impossible. It's not a replacement for IAM; it's a safety net that blocks data exfiltration even when someone has valid permissions. Think of it as a blast shield around your GCP services: traffic can't cross the perimeter unless you explicitly allow it. This article covers the perimeter model, ingress/egress rules, and the critical dry-run mode that prevents you from breaking production on your first attempt.

The Perimeter Model: What It Protects and How

A VPC Service Controls perimeter is a logical boundary around a set of GCP projects and the managed services they use. Once a perimeter is defined, all requests to those services from outside the perimeter are denied by default — even if the caller has IAM permissions. The perimeter protects services like Cloud Storage, BigQuery, Bigtable, Cloud Spanner, and many others. It does not protect Compute Engine VMs, GKE clusters, or Cloud Functions directly (those are infrastructure, not managed services). The key insight: perimeters are project-based, not network-based. You add projects to a perimeter, and all protected services in those projects are subject to the policy. Access from inside the perimeter (e.g., a VM in the same project calling BigQuery) is allowed by default, but you can restrict it further with ingress/egress rules.

create_perimeter.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash
# Create a basic VPC Service Controls perimeter
# Requires: gcloud, Organization Admin role

PERIMETER_NAME="prod-data-perimeter"
PROJECTS="projects/my-prod-project,projects/my-analytics-project"

# Create the perimeter (dry-run first!)
gcloud access-context-manager perimeters create $PERIMETER_NAME \
    --title="Production Data Perimeter" \
    --resources=$PROJECTS \
    --restricted-services="bigquery.googleapis.com,storage.googleapis.com" \
    --dry-run \
    --policy="organizations/123456789"

echo "Perimeter created in dry-run mode. Check logs before enabling."
Output
Created perimeter [prod-data-perimeter].
Note: Perimeter is in dry-run mode. No access is blocked yet.
⚠ Dry-Run First
Always create perimeters in dry-run mode initially. A misconfigured perimeter can block all access to critical services, including your own team. Dry-run lets you see what would be blocked without actually blocking it.
📊 Production Insight
In production, we once added a project to a perimeter without dry-run and immediately locked out our CI/CD pipeline that was using BigQuery. The pipeline had a service account with proper IAM, but the request originated from a Cloud Build worker outside the perimeter. We had to manually remove the project from the perimeter to restore access.
🎯 Key Takeaway
Perimeters are project-based boundaries that block data exfiltration from managed services, regardless of IAM permissions.
gcp-vpc-service-controls THECODEFORGE.IO VPC Service Controls Perimeter Enforcement Flow Step-by-step process from request to enforcement decision Request Initiated User or service attempts access Perimeter Check Is request inside or outside perimeter? Ingress Rule Evaluation Allowed inbound traffic? Egress Rule Evaluation Allowed outbound traffic? Access Level Context Device, IP, identity attributes Enforcement Decision Allow, deny, or dry-run log ⚠ Dry-run mode logs violations without blocking Always test with dry-run before enforcing THECODEFORGE.IO
thecodeforge.io
Gcp Vpc Service Controls

Ingress Rules: Allowing Traffic Into the Perimeter

Ingress rules define who can access services inside the perimeter from outside. Without ingress rules, no external identity (user, service account, or network) can call any protected service. This is often too restrictive — you need to allow your developers, CI/CD systems, or on-premises applications to query BigQuery or write to Cloud Storage. An ingress rule specifies a source (identity or VPC network) and a destination (service, method, or operation). For example, you can allow a specific service account from a different project to read BigQuery datasets. Ingress rules are evaluated before the default deny, so they act as explicit exceptions. Important: ingress rules apply to the entire perimeter, not individual projects. If you need fine-grained control, consider multiple perimeters.

add_ingress_rule.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/bash
# Add an ingress rule to allow a service account from another project
# to read BigQuery data inside the perimeter

PERIMETER_NAME="prod-data-perimeter"
POLICY_ID="123456789"

# Create ingress rule YAML
cat > ingress_rule.yaml <<EOF
- ingressFrom:
    identities:
    - serviceAccount:ci-runner@build-project.iam.gserviceaccount.com
    identityType: ANY_IDENTITY
  ingressTo:
    operations:
    - serviceName: bigquery.googleapis.com
      methodSelectors:
      - method: "*"
    resources:
    - projects/my-prod-project
EOF

# Update perimeter with ingress rule
gcloud access-context-manager perimeters update $PERIMETER_NAME \
    --add-ingress-rules="ingress_rule.yaml" \
    --policy=$POLICY_ID

echo "Ingress rule added. Verify in dry-run logs."
Output
Updated perimeter [prod-data-perimeter].
Added ingress rule for serviceAccount:ci-runner@build-project.iam.gserviceaccount.com.
💡Use Identity-Based Ingress for CI/CD
For CI/CD pipelines, always use identity-based ingress rules (service accounts) rather than network-based. Network-based rules (VPC sources) are harder to audit and can inadvertently allow access from any VM in that network.
📊 Production Insight
We once had an ingress rule that allowed all identities from a VPC network. A developer spun up a VM in that network with a personal account and downloaded sensitive data. We switched to identity-based rules and added a condition requiring the VM to have a specific tag.
🎯 Key Takeaway
Ingress rules explicitly allow external identities or networks to access services inside the perimeter.

Egress Rules: Controlling Data Leaving the Perimeter

Egress rules control what data can leave the perimeter. By default, services inside the perimeter can send data to any destination — that's a huge risk. An egress rule restricts outbound traffic to specific external services, networks, or identities. For example, you can allow BigQuery to export data only to a specific Cloud Storage bucket inside the same perimeter, or to a trusted on-premises network via a VPN. Egress rules are critical for preventing data exfiltration: even if an attacker gains access to a service inside the perimeter, they can't copy data to an external location unless an egress rule explicitly allows it. Like ingress rules, egress rules are evaluated before the default allow (for outbound traffic). They can be based on destination service, method, or resource.

add_egress_rule.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash
# Add an egress rule to restrict BigQuery exports to a specific bucket

PERIMETER_NAME="prod-data-perimeter"
POLICY_ID="123456789"

cat > egress_rule.yaml <<EOF
- egressFrom:
    identityType: ANY_IDENTITY
  egressTo:
    operations:
    - serviceName: bigquery.googleapis.com
      methodSelectors:
      - method: google.cloud.bigquery.v2.JobService.InsertJob
    resources:
    - projects/my-prod-project
    externalResources:
    - projects/my-prod-project/buckets/export-bucket
EOF

gcloud access-context-manager perimeters update $PERIMETER_NAME \
    --add-egress-rules="egress_rule.yaml" \
    --policy=$POLICY_ID

echo "Egress rule added. Only exports to export-bucket are allowed."
Output
Updated perimeter [prod-data-perimeter].
Added egress rule restricting exports to projects/my-prod-project/buckets/export-bucket.
🔥Egress Rules Are Not Retroactive
Egress rules only apply to new requests. Existing connections or long-running jobs are not affected until they restart. Plan for job restarts when updating egress rules.
📊 Production Insight
We had a data pipeline that exported BigQuery results to a Cloud Storage bucket in a different project. Without an egress rule, the export succeeded. After adding an egress rule to allow only a specific bucket, the pipeline broke because the export destination wasn't in the allowed list. We had to update the pipeline to use the correct bucket.
🎯 Key Takeaway
Egress rules restrict outbound data movement from perimeter services to prevent exfiltration.
gcp-vpc-service-controls THECODEFORGE.IO VPC Service Controls Perimeter Architecture Layered components from projects to monitoring Protected Resources GCS Buckets | BigQuery Datasets | Cloud Spanner Service Perimeter Project Set | VPC Network | Access Levels Ingress Rules Allowed Sources | Allowed Services | Identity Conditions Egress Rules Allowed Destinations | Exempted Services | Data Flow Control Dry-Run Mode Logging Sink | Audit Logs | Alert Policies Monitoring & Alerting Cloud Monitoring | Log Explorer | Violation Alerts THECODEFORGE.IO
thecodeforge.io
Gcp Vpc Service Controls

Dry-Run Mode: Your Safety Net Before Enforcement

Dry-run mode is the most important feature of VPC Service Controls. When you create or update a perimeter in dry-run mode, the policy is evaluated but not enforced. All violations are logged to Cloud Logging under the VpcServiceControlsDryRun log type. This lets you see what would be blocked without actually breaking anything. You can monitor these logs for days or weeks to catch legitimate access patterns that would be denied. Once you're confident the policy is correct, you switch to enforcement mode. Never skip dry-run. In production, we've seen teams lose access to BigQuery for hours because they enabled a perimeter without testing. Dry-run gives you a safety net. Use it with a monitoring dashboard that alerts on dry-run violations.

enable_perimeter.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash
# Switch perimeter from dry-run to enforcement

PERIMETER_NAME="prod-data-perimeter"
POLICY_ID="123456789"

# First, check dry-run logs for violations
# (Assume you have a log sink set up)

gcloud logging read "logName=projects/my-prod-project/logs/VpcServiceControlsDryRun" \
    --limit=10 --format=json | jq '.[].jsonPayload.violationReason'

# If no unexpected violations, enable enforcement
gcloud access-context-manager perimeters update $PERIMETER_NAME \
    --no-dry-run \
    --policy=$POLICY_ID

echo "Perimeter now enforced. Monitor logs for real blocks."
Output
No unexpected violations found.
Updated perimeter [prod-data-perimeter]. Perimeter is now enforced.
⚠ Dry-Run Logs Are Not Real-Time
Dry-run violations may take up to a few minutes to appear in logs. Don't assume no violations after a quick check. Wait at least 24 hours of typical traffic before enabling enforcement.
📊 Production Insight
We once enabled a perimeter in dry-run and saw thousands of violations from a legacy reporting tool that used a service account from a different org. We had to create an ingress rule for that tool before enabling enforcement. Without dry-run, we would have broken the reporting dashboard.
🎯 Key Takeaway
Always use dry-run mode first to validate perimeters without blocking access.

Access Levels: Adding Context to Perimeter Rules

Access levels let you add context-aware conditions to ingress and egress rules. Instead of allowing all traffic from a service account, you can restrict it based on device policy, IP address, or time of day. Access levels are defined using the Access Context Manager API and can be reused across multiple perimeters. Common use cases: allow access only from corporate IP ranges, require that devices have endpoint verification enabled, or restrict access to business hours. Access levels are evaluated at request time, so they add latency but provide fine-grained security. They are especially useful for protecting sensitive data in regulated industries.

create_access_level.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/bash
# Create an access level that requires corporate IP range

POLICY_ID="123456789"
ACCESS_LEVEL_NAME="corporate_ip"

cat > access_level.yaml <<EOF
name: accessPolicies/$POLICY_ID/accessLevels/$ACCESS_LEVEL_NAME
title: Corporate IP Range
basic:
  conditions:
  - ipSubnetworks:
    - 203.0.113.0/24
    - 198.51.100.0/24
EOF

gcloud access-context-manager levels create $ACCESS_LEVEL_NAME \
    --title="Corporate IP Range" \
    --basic-level-spec="access_level.yaml" \
    --policy=$POLICY_ID

echo "Access level created. Use in ingress/egress rules."
Output
Created access level [corporate_ip].
💡Combine Access Levels with IAM Conditions
Access levels work alongside IAM conditions. Use IAM for who can access, and access levels for how they can access (e.g., from which network). They are complementary.
📊 Production Insight
We used an access level to restrict BigQuery access to corporate VPN IPs only. A remote developer connected via a non-VPN network and was blocked. We added an exception for their home IP after security review.
🎯 Key Takeaway
Access levels add context (IP, device, time) to perimeter rules for fine-grained control.

Service Perimeters vs. Bridge Perimeters

VPC Service Controls offers two types of perimeters: service perimeters and bridge perimeters. Service perimeters are the standard type that restrict access to managed services. Bridge perimeters allow data exchange between two service perimeters without violating either perimeter's rules. For example, if you have a perimeter for production and one for staging, a bridge perimeter lets BigQuery in staging read data from production BigQuery datasets. Bridge perimeters are useful for data pipelines that cross environments, but they reduce isolation. Use them sparingly and only when necessary. Each bridge perimeter connects exactly two service perimeters and allows all traffic between them by default (though you can add restrictions).

create_bridge_perimeter.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash
# Create a bridge perimeter between prod and staging

PROD_PERIMETER="prod-data-perimeter"
STAGING_PERIMETER="staging-data-perimeter"
POLICY_ID="123456789"

# Bridge perimeters are created as a separate type
gcloud access-context-manager perimeters create prod-staging-bridge \
    --title="Prod-Staging Bridge" \
    --type=bridge \
    --perimeter-type=bridge \
    --resources="projects/my-prod-project,projects/my-staging-project" \
    --restricted-services="bigquery.googleapis.com,storage.googleapis.com" \
    --policy=$POLICY_ID

echo "Bridge perimeter created. Traffic flows freely between prod and staging."
Output
Created perimeter [prod-staging-bridge].
⚠ Bridge Perimeters Weaken Isolation
A bridge perimeter allows all traffic between the two perimeters by default. If one perimeter is compromised, data can flow to the other. Only use bridges when absolutely necessary and monitor cross-perimeter traffic.
📊 Production Insight
We used a bridge perimeter to allow a staging pipeline to read production BigQuery tables. Later, a misconfigured job in staging wrote sensitive data to a public bucket. The bridge allowed the data to flow out. We removed the bridge and instead used a scheduled export with an ingress rule.
🎯 Key Takeaway
Bridge perimeters connect two service perimeters for controlled data exchange, but reduce security isolation.

Monitoring and Alerting on Perimeter Violations

Once a perimeter is enforced, all violations are logged to Cloud Logging under the VpcServiceControls log type. You must set up monitoring and alerting to detect unauthorized access attempts. Common patterns: a service account from another project trying to read BigQuery, a user accessing Cloud Storage from an external IP, or a CI/CD pipeline that hasn't been updated with new ingress rules. Create a log-based metric for violations and set up an alert threshold (e.g., >10 violations in 5 minutes). Also monitor dry-run logs even after enforcement — they can reveal new access patterns that need rules. In production, we use a dashboard that shows violation trends by service and identity.

create_violation_alert.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash
# Create a log-based metric and alert for perimeter violations

# Create metric
gcloud logging metrics create perimeter-violations \
    --description="Count of VPC Service Controls violations" \
    --log-filter='logName="projects/my-prod-project/logs/VpcServiceControls"'

# Create alert policy (simplified via gcloud alpha)
gcloud alpha monitoring policies create \
    --display-name="Perimeter Violation Alert" \
    --condition-display-name="High violation rate" \
    --condition-filter='metric.type="logging.googleapis.com/user/perimeter-violations"' \
    --condition-threshold-value=10 \
    --condition-threshold-duration=300s \
    --notification-channels="projects/my-prod-project/notificationChannels/123"

echo "Alert created. You'll be notified when violations exceed 10 in 5 minutes."
Output
Created metric [perimeter-violations].
Created alert policy [Perimeter Violation Alert].
🔥Logs Are Not Real-Time
Violation logs may have a delay of up to 2 minutes. Don't rely on them for real-time blocking; use them for auditing and trend analysis.
📊 Production Insight
We once saw a spike in violations from a new microservice that was using a default service account. The service account had IAM permissions but wasn't in any ingress rule. We added an ingress rule for that service account and the violations stopped.
🎯 Key Takeaway
Monitor violation logs and set up alerts to detect unauthorized access attempts quickly.

Common Pitfalls and How to Avoid Them

  1. Forgetting to add all projects: If a project uses a protected service but isn't in the perimeter, data can flow freely. Always audit your project list. 2. Overly permissive ingress rules: Using ANY_IDENTITY or broad VPC ranges can allow unintended access. Be specific. 3. Not testing dry-run long enough: A weekly batch job might only run on Sundays. Wait at least one full business cycle. 4. Ignoring egress rules: Default allow outbound is dangerous. Always restrict egress to known destinations. 5. Mixing perimeters with VPC firewall rules: VPC Service Controls is not a firewall. It doesn't block network-level traffic. Use VPC firewall rules for that. 6. Not documenting rules: When a violation occurs, you need to know why a rule exists. Document each ingress/egress rule with a reason and owner.
audit_perimeter.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
# Audit perimeter projects and rules

PERIMETER_NAME="prod-data-perimeter"
POLICY_ID="123456789"

# List projects in perimeter
gcloud access-context-manager perimeters describe $PERIMETER_NAME \
    --policy=$POLICY_ID --format="json" | jq '.spec.resources'

# List ingress rules
gcloud access-context-manager perimeters describe $PERIMETER_NAME \
    --policy=$POLICY_ID --format="json" | jq '.spec.ingressRules'

# List egress rules
gcloud access-context-manager perimeters describe $PERIMETER_NAME \
    --policy=$POLICY_ID --format="json" | jq '.spec.egressRules'

echo "Audit complete. Verify projects and rules are correct."
Output
{
"resources": ["projects/my-prod-project", "projects/my-analytics-project"],
"ingressRules": [...],
"egressRules": [...]
}
💡Automate Perimeter Audits
Run a weekly script that lists all perimeters, their projects, and rules. Compare against a known-good configuration to detect drift.
📊 Production Insight
We once had a perimeter that allowed all identities from a VPC network. A contractor's VM in that network was compromised, and the attacker used it to exfiltrate data from BigQuery. We now use identity-based ingress rules exclusively.
🎯 Key Takeaway
Avoid common pitfalls by auditing projects, being specific in rules, and testing dry-run thoroughly.

Integrating with VPC Firewall Rules and IAM

VPC Service Controls works alongside VPC firewall rules and IAM, but they are not substitutes. IAM controls who can access a resource (authentication and authorization). VPC firewall rules control network traffic at the packet level (e.g., allow SSH from specific IPs). VPC Service Controls controls data exfiltration at the API level. For a defense-in-depth strategy, use all three. Example: IAM allows a service account to read BigQuery, VPC firewall allows traffic from the VM to the BigQuery API, and VPC Service Controls ensures the data stays within the perimeter. If any layer fails, the others still provide protection. Never rely on VPC Service Controls alone — it doesn't block network-level attacks or unauthorized IAM access.

defense_in_depth.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash
# Example of combining IAM, firewall, and VPC SC

# IAM: grant BigQuery read access to a service account
gcloud projects add-iam-policy-binding my-prod-project \
    --member="serviceAccount:data-reader@my-prod-project.iam.gserviceaccount.com" \
    --role="roles/bigquery.dataViewer"

# VPC firewall: allow outbound to BigQuery API (if using VPC)
gcloud compute firewall-rules create allow-bigquery-egress \
    --direction=EGRESS \
    --target-tags=data-vm \
    --destination-ranges=199.36.153.4/30 \
    --allow=tcp:443

# VPC SC: perimeter already configured (see previous examples)
echo "Defense-in-depth configured."
Output
Defense-in-depth configured.
🔥Layered Security Is Essential
VPC Service Controls is not a silver bullet. Combine with IAM, firewall rules, and regular audits for comprehensive protection.
📊 Production Insight
We had a scenario where IAM allowed a user to delete a BigQuery dataset, but VPC SC blocked the delete because the request came from outside the perimeter. The user had valid IAM but was blocked by VPC SC — that's the safety net in action.
🎯 Key Takeaway
Use VPC Service Controls with IAM and firewall rules for defense-in-depth.

Advanced: Dynamic Perimeters with Conditions

You can make perimeters dynamic by using conditions in access levels. For example, allow access only if the request comes from a device that has endpoint verification enabled AND the user is in a specific group. This is useful for zero-trust architectures. Dynamic perimeters are configured using the Access Context Manager API with custom conditions. They can reference device attributes, user attributes, and environment attributes. However, dynamic conditions add latency and complexity. Use them only when static rules are insufficient. In production, we use dynamic perimeters for highly sensitive data (e.g., PII) where we require device posture checks.

dynamic_access_level.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/bash
# Create a dynamic access level requiring endpoint verification

POLICY_ID="123456789"
ACCESS_LEVEL_NAME="device_verified"

cat > dynamic_level.yaml <<EOF
name: accessPolicies/$POLICY_ID/accessLevels/$ACCESS_LEVEL_NAME
title: Device Verified
basic:
  conditions:
  - devicePolicy:
      requireEndpointVerification: true
    members:
    - user:user@example.com
EOF

gcloud access-context-manager levels create $ACCESS_LEVEL_NAME \
    --title="Device Verified" \
    --basic-level-spec="dynamic_level.yaml" \
    --policy=$POLICY_ID

echo "Dynamic access level created. Only verified devices for user@example.com."
Output
Created access level [device_verified].
⚠ Dynamic Conditions Add Latency
Each request with dynamic conditions may take additional time to evaluate device posture. Test performance impact before rolling out to all users.
📊 Production Insight
We implemented a dynamic perimeter for a finance team that required device verification. The first week, many users were blocked because their devices weren't enrolled in endpoint verification. We had to run a training session and provide a grace period.
🎯 Key Takeaway
Dynamic perimeters use conditions for zero-trust access but add complexity and latency.

Troubleshooting Perimeter Issues

When something breaks, follow this checklist: 1. Check if the perimeter is in dry-run or enforcement mode. 2. Look at violation logs — they tell you exactly what was blocked and why. 3. Verify the request source: is it from inside or outside the perimeter? 4. Check ingress/egress rules: does an explicit rule allow or deny the request? 5. Review access levels: are conditions being met? 6. Test with a known-good identity (e.g., a service account that works). 7. Temporarily switch to dry-run to see if the issue is the perimeter. Common issues: missing ingress rule for a new service account, incorrect resource name in egress rule, or access level condition not satisfied. Use the gcloud command to describe the perimeter and verify rules.

troubleshoot_perimeter.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash
# Troubleshoot a blocked request

PERIMETER_NAME="prod-data-perimeter"
POLICY_ID="123456789"

# 1. Check perimeter mode
gcloud access-context-manager perimeters describe $PERIMETER_NAME \
    --policy=$POLICY_ID --format="json" | jq '.status | ."dryRun"'

# 2. View recent violations
gcloud logging read "logName=projects/my-prod-project/logs/VpcServiceControls" \
    --limit=5 --format=json | jq '.[].jsonPayload'

# 3. Test with a specific identity (simulate)
# Use the Access Context Manager test API (not shown for brevity)

echo "Troubleshooting steps completed."
Output
{
"dryRun": false,
"violations": [
{
"violationReason": "Request denied by VPC Service Controls",
"requestIdentity": "user:alice@example.com",
"service": "bigquery.googleapis.com"
}
]
}
💡Use the Test API
Access Context Manager has a test API that lets you simulate a request and see if it would be allowed or denied. Use it before making changes.
📊 Production Insight
A developer complained they couldn't access BigQuery. We checked logs and saw a violation from their IP. They were working from a coffee shop, not the corporate VPN. We added an access level exception for their home IP temporarily.
🎯 Key Takeaway
Troubleshoot by checking mode, logs, rules, and access levels systematically.

Best Practices for Production Perimeters

  1. Start with a single perimeter for your most sensitive data. Expand gradually. 2. Use dry-run for at least one week before enforcement. 3. Document every rule with a reason and owner. 4. Monitor violations and set up alerts. 5. Audit perimeters quarterly to remove stale rules. 6. Use access levels for context-aware access. 7. Avoid bridge perimeters unless absolutely necessary. 8. Test changes in a non-production environment first. 9. Automate perimeter creation with Infrastructure as Code (Terraform, Deployment Manager). 10. Train your team on how VPC Service Controls works — many incidents come from misunderstanding.
perimeter_terraform.tfHCL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
resource "google_access_context_manager_service_perimeter" "prod" {
  provider = google-beta
  parent   = "accessPolicies/123456789"
  name     = "prod-data-perimeter"
  title    = "Production Data Perimeter"
  status {
    resources = ["projects/my-prod-project", "projects/my-analytics-project"]
    restricted_services = ["bigquery.googleapis.com", "storage.googleapis.com"]
    vpc_accessible_services {
      enable_restriction = true
      allowed_services   = ["bigquery.googleapis.com"]
    }
  }
  use_explicit_dry_run_spec = true
}

resource "google_access_context_manager_service_perimeter_ingress_policy" "ci" {
  provider = google-beta
  perimeter = google_access_context_manager_service_perimeter.prod.name
  ingress_from {
    identity_type = "ANY_IDENTITY"
    identities    = ["serviceAccount:ci-runner@build-project.iam.gserviceaccount.com"]
  }
  ingress_to {
    operations {
      service_name = "bigquery.googleapis.com"
      method_selectors {
        method = "*"
      }
    }
    resources = ["projects/my-prod-project"]
  }
}
Output
Terraform will create the perimeter and ingress rule.
💡Use Infrastructure as Code
Manage perimeters with Terraform or Deployment Manager to ensure consistency and enable code review. Avoid manual gcloud commands for production changes.
📊 Production Insight
We manage all perimeters via Terraform in a dedicated repo. Every change goes through code review and is applied via CI/CD. This has prevented many misconfigurations.
🎯 Key Takeaway
Follow best practices: start small, test thoroughly, document, and automate.

VPC Accessible Services: Controlling API Access Within the Perimeter

When you restrict a service (e.g., BigQuery) inside a perimeter, clients inside the VPC can still call its API. You can further control this using VPC Accessible Services settings on the perimeter. This configuration determines which Google APIs are accessible from within the perimeter—either the restricted VIP (restricted.googleapis.com) or the private VIP (private.googleapis.com). The restricted VIP only serves APIs that support VPC Service Controls, while the private VIP serves all Google APIs (including unsecured ones like consumer Gmail). For maximum security, use the restricted VIP. However, not all APIs are available on the restricted VIP. Before enabling, verify that the APIs your services depend on are supported. If they are not, you must use the private VIP, which weakens security because it allows access to non-protected services. Best practice: start with the restricted VIP, test thoroughly, and only fall back to private VIP if essential APIs are unavailable.

vpc-accessible-services.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Configure a perimeter to use restricted VIP
# Create perimeter with VPC accessible services
cat > perimeter_vpc.yaml <<EOF
- vpcAccessibleServices:
    enableRestriction: true
    allowedServices:
    - bigquery.googleapis.com
    - storage.googleapis.com
    - bigtable.googleapis.com
EOF

gcloud access-context-manager perimeters update prod-data-perimeter \
    --policy=123456789 \
    --set-vpc-accessible-services="perimeter_vpc.yaml"

# Verify which services are on the restricted VIP
gcloud services list --available --filter="name~restricted.googleapis.com"
Output
Updated perimeter [prod-data-perimeter].
VPC accessible services restricted to BigQuery, Cloud Storage, Bigtable.
⚠ Not All APIs Are on Restricted VIP
Check the list of supported services before restricting to restricted.googleapis.com. Missing APIs will break if they're not on the restricted VIP.
📊 Production Insight
We switched to restricted VIP and immediately broke a service that was calling a non-supported API (Cloud Workflows). We had to add an exception for that API on the private VIP until the service was migrated.
🎯 Key Takeaway
Restrict VPC accessible services to the restricted VIP for maximum security; fall back to private VIP only if needed.
Service Perimeter vs Bridge Perimeter Key differences in scope and data flow control Service Perimeter Bridge Perimeter Scope Single project or project set Multiple perimeters across projects Data Flow Restricts within perimeter Allows controlled bridging between perim Use Case Isolate sensitive data Share data between trusted environments Complexity Simpler to configure Requires careful rule design Monitoring Standard violation alerts Additional bridge-specific logs THECODEFORGE.IO
thecodeforge.io
Gcp Vpc Service Controls

Propagation Delay and the 30-Minute Window

Changes to VPC Service Controls perimeters do not take effect instantly. After updating or creating a perimeter, there is a propagation delay of up to 30 minutes. During this window, the perimeter might block requests with Error 403: Request is prohibited by organization's policy. even if the configuration is correct. This is because the policy change must propagate across Google's infrastructure. This delay is especially dangerous when fixing a misconfigured perimeter: you make a change, but the fix takes up to 30 minutes to apply. Always plan maintenance windows accordingly. If you need to restore access urgently, the fastest way is to remove the affected project from the perimeter (which itself takes up to 30 minutes). For critical situations, have a break-glass procedure that involves temporarily disabling the perimeter entirely. Test propagation times in a non-production environment to set expectations.

check-propagation.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# After updating a perimeter, wait and verify
# Step 1: Update perimeter
gcloud access-context-manager perimeters update prod-data-perimeter \
    --add-resources="projects/new-project" \
    --policy=123456789

# Step 2: Wait up to 30 minutes
# Step 3: Verify propagation
gcloud access-context-manager perimeters describe prod-data-perimeter \
    --policy=123456789 --format="json" | jq '.spec.resources'

# Monitor for 403 errors during propagation
gcloud logging read "protoPayload.status.code=7 AND protoPayload.status.message=~'VPC Service Controls'" \
    --limit=5 --format=json
Output
{
"resources": ["projects/my-prod-project", "projects/new-project"]
}
No 403 errors found (propagation complete).
⚠ Plan for 30-Minute Propagation
After any perimeter change, expect up to 30 minutes for full propagation. Do not make multiple changes in quick succession—wait for each to propagate before the next.
📊 Production Insight
During an incident, we removed a misconfigured ingress rule, but the fix took 25 minutes to propagate. The affected team was blocked for nearly half an hour. Now we test perimeter changes in dry-run first and schedule enforcement changes during low-traffic windows.
🎯 Key Takeaway
Perimeter changes take up to 30 minutes to propagate; plan maintenance windows and have a break-glass procedure.
⚙ Quick Reference
14 commands from this guide
FileCommand / CodePurpose
create_perimeter.shPERIMETER_NAME="prod-data-perimeter"The Perimeter Model
add_ingress_rule.shPERIMETER_NAME="prod-data-perimeter"Ingress Rules
add_egress_rule.shPERIMETER_NAME="prod-data-perimeter"Egress Rules
enable_perimeter.shPERIMETER_NAME="prod-data-perimeter"Dry-Run Mode
create_access_level.shPOLICY_ID="123456789"Access Levels
create_bridge_perimeter.shPROD_PERIMETER="prod-data-perimeter"Service Perimeters vs. Bridge Perimeters
create_violation_alert.shgcloud logging metrics create perimeter-violations \Monitoring and Alerting on Perimeter Violations
audit_perimeter.shPERIMETER_NAME="prod-data-perimeter"Common Pitfalls and How to Avoid Them
defense_in_depth.shgcloud projects add-iam-policy-binding my-prod-project \Integrating with VPC Firewall Rules and IAM
dynamic_access_level.shPOLICY_ID="123456789"Advanced
troubleshoot_perimeter.shPERIMETER_NAME="prod-data-perimeter"Troubleshooting Perimeter Issues
perimeter_terraform.tfresource "google_access_context_manager_service_perimeter" "prod" {Best Practices for Production Perimeters
vpc-accessible-services.shcat > perimeter_vpc.yaml <VPC Accessible Services
check-propagation.shgcloud access-context-manager perimeters update prod-data-perimeter \Propagation Delay and the 30-Minute Window

Key takeaways

1
Perimeter Model
VPC Service Controls creates project-based boundaries that block data exfiltration from managed services, regardless of IAM permissions.
2
Ingress and Egress Rules
Ingress rules allow external access into the perimeter; egress rules restrict outbound data movement. Both are essential for controlling data flow.
3
Dry-Run Mode
Always test perimeters in dry-run mode first to see what would be blocked without breaking production. Monitor logs for at least a week before enforcement.
4
Defense-in-Depth
Combine VPC Service Controls with IAM and VPC firewall rules for layered security. Each layer addresses different threats.

Common mistakes to avoid

3 patterns
×

Ignoring gcp vpc service controls best practices

Symptom
Production incidents and unexpected behavior
Fix
Follow GCP documentation and implement proper monitoring and testing
×

Over-provisioning without rightsizing analysis

Symptom
Wasted cloud spend and poor resource utilization
Fix
Use committed use discounts and rightsize based on actual usage metrics
×

Skipping IAM least-privilege principles

Symptom
Security vulnerabilities and compliance violations
Fix
Implement custom roles with minimum required permissions and use conditions
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
What is VPC Service Controls: Perimeter, Ingress/Egress Rules, and Dry-R...
Q02SENIOR
How do you configure VPC Service Controls: Perimeter, Ingress/Egress Rul...
Q03SENIOR
What are the cost optimization strategies for VPC Service Controls: Peri...
Q01 of 03JUNIOR

What is VPC Service Controls: Perimeter, Ingress/Egress Rules, and Dry-Run Mode and when would you use it in production?

ANSWER
VPC Service Controls: Perimeter, Ingress/Egress Rules, and Dry-Run Mode is a Google Cloud service for managing infrastructure efficiently. It's ideal for organizations needing scalable, reliable cloud solutions. Use it when you need automated management and consistent performance across your GCP projects.
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
What is the difference between VPC Service Controls and IAM?
02
Can VPC Service Controls block network-level attacks?
03
How long should I run dry-run mode before enforcement?
04
What happens if I add a project to a perimeter without ingress rules?
05
Can I use VPC Service Controls with on-premises data sources?
06
How do I troubleshoot a blocked request?
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Notes here come from systems that actually shipped.

Follow
Verified
production tested
July 12, 2026
last updated
2,073
articles · all by Naren
🔥

That's Google Cloud. Mark it forged?

7 min read · try the examples if you haven't

Previous
Binary Authorization
38 / 55 · Google Cloud
Next
Cloud Audit Logs