This check flags GCP projects that have no log-based alert watching for VPC network changes. Without it, someone can rewire your network, open firewall paths, or peer to an attacker-controlled VPC and you find out far too late. Create a logging metric and an alert policy that fire on gce_network activity log entries.
VPC networks are the foundation of everything you run in GCP. Subnets, routes, peering connections, and the network objects that firewall rules attach to all live here. A change at this layer can quietly expose workloads that you assumed were private, or hand a path to an attacker who already has a foothold. The trouble is that these changes look routine in the audit logs, and nobody is paging on them. This check exists to confirm that someone, or something, is actually watching.
What this check detects
The check inspects your GCP project for a log-based metric and an associated alerting policy that trigger on VPC network modification events. GCP records these events in the Admin Activity audit logs whenever the Compute Engine API mutates network resources. The relevant log entries carry method names like:
v1.compute.networks.insertv1.compute.networks.patchv1.compute.networks.deletev1.compute.networks.removePeeringv1.compute.networks.addPeeringv1.compute.networks.updatePeering
If no log-based metric matches these events, and no alert policy is wired to notify a channel, the check fails. In short, it confirms the difference between "the change was logged" and "a human was told about it."
Note: Admin Activity audit logs are always on and cannot be disabled, so the raw evidence of a VPC change is already being captured. This check is purely about whether you turned that evidence into a signal. The data exists either way.
Why it matters
VPC changes are high-leverage. A single API call can change the blast radius of an entire project. Here are the scenarios that keep this check on the list.
Malicious peering for data exfiltration
An attacker with compute.networks.addPeering permission can peer your VPC with one they control in another project. Once peered, internal traffic can route to their environment without ever crossing the public internet, which means your egress controls and many of your detections never see it. This is a quiet, effective exfiltration path, and without an alert the peering connection can sit there for weeks.
Network deletion causing an outage
A delete on a network, or on the resources hanging off it, can take down connectivity for every workload attached. Sometimes this is a fat-fingered Terraform apply, sometimes it is destructive intent. Either way, an alert at the moment of the change turns a multi-hour incident investigation into a five-minute response.
Quiet privilege expansion before the real attack
Network changes are often a setup move. An attacker modifies routing or peering to position themselves, then comes back later to act. Catching the network change is your chance to intervene during the staging phase instead of after the damage.
Warning: Compliance frameworks expect this control explicitly. CIS Google Cloud Foundations Benchmark 2.9 requires a log metric filter and alert for VPC network changes. A failing check here is a direct audit finding, not just a nice-to-have.
How to fix it
The fix has two parts: a log-based metric that counts the relevant events, and an alert policy that notifies you when the metric goes above zero. You also need a notification channel so the alert reaches a human.
Step 1: Create the log-based metric
Using the gcloud CLI, create a counter metric with a filter that matches network API calls:
gcloud logging metrics create vpc-network-changes \
--project=YOUR_PROJECT_ID \
--description="Counts VPC network modification events" \
--log-filter='resource.type="gce_network"
AND (protoPayload.methodName:"compute.networks.insert"
OR protoPayload.methodName:"compute.networks.patch"
OR protoPayload.methodName:"compute.networks.delete"
OR protoPayload.methodName:"compute.networks.removePeering"
OR protoPayload.methodName:"compute.networks.addPeering"
OR protoPayload.methodName:"compute.networks.updatePeering")'
Step 2: Create a notification channel
If you do not already have one, create a channel so the alert has somewhere to go. An email channel is the simplest starting point:
gcloud beta monitoring channels create \
--project=YOUR_PROJECT_ID \
--display-name="Network Security Alerts" \
--type=email \
[email protected]
Grab the channel ID from the output, you will need it in the next step.
Step 3: Create the alert policy
Define the policy in a JSON file. This fires whenever the metric records any events in a rolling window.
{
"displayName": "Alert on VPC Network Changes",
"combiner": "OR",
"conditions": [
{
"displayName": "VPC network change detected",
"conditionThreshold": {
"filter": "metric.type=\"logging.googleapis.com/user/vpc-network-changes\" AND resource.type=\"gce_network\"",
"comparison": "COMPARISON_GT",
"thresholdValue": 0,
"duration": "0s",
"aggregations": [
{
"alignmentPeriod": "60s",
"perSeriesAligner": "ALIGN_COUNT"
}
]
}
}
],
"notificationChannels": [
"projects/YOUR_PROJECT_ID/notificationChannels/CHANNEL_ID"
]
}
Apply it:
gcloud alpha monitoring policies create \
--project=YOUR_PROJECT_ID \
--policy-from-file=vpc-network-alert-policy.json
Console alternative
- Go to Logging > Log-based Metrics and click Create Metric.
- Choose Counter, paste the filter from Step 1, and name it
vpc-network-changes. - From the metric, click Create alert from metric, which carries you into Cloud Monitoring.
- Set the threshold to greater than 0 with an alignment period of 1 minute, attach a notification channel, and save.
Tip: Set this up at the organization or folder level using an aggregated log sink instead of repeating it per project. One metric and policy backed by a project that holds an org-wide sink saves you from configuring hundreds of projects by hand and stops new projects from slipping through uncovered.
How to prevent it from happening again
Manual setup drifts. Bake the metric and alert into infrastructure as code so every project ships with it from day one.
Terraform
resource "google_logging_metric" "vpc_network_changes" {
name = "vpc-network-changes"
project = var.project_id
filter = <<-EOT
resource.type="gce_network"
AND (protoPayload.methodName:"compute.networks.insert"
OR protoPayload.methodName:"compute.networks.patch"
OR protoPayload.methodName:"compute.networks.delete"
OR protoPayload.methodName:"compute.networks.addPeering"
OR protoPayload.methodName:"compute.networks.removePeering"
OR protoPayload.methodName:"compute.networks.updatePeering")
EOT
metric_descriptor {
metric_kind = "DELTA"
value_type = "INT64"
}
}
resource "google_monitoring_alert_policy" "vpc_network_changes" {
display_name = "Alert on VPC Network Changes"
project = var.project_id
combiner = "OR"
conditions {
display_name = "VPC network change detected"
condition_threshold {
filter = "metric.type=\"logging.googleapis.com/user/${google_logging_metric.vpc_network_changes.name}\" AND resource.type=\"gce_network\""
comparison = "COMPARISON_GT"
threshold_value = 0
duration = "0s"
aggregations {
alignment_period = "60s"
per_series_aligner = "ALIGN_COUNT"
}
}
}
notification_channels = [var.security_channel_id]
}
Wrap this in a shared module and require it in every project template. New projects then inherit the alert with no extra effort.
Gate it in CI/CD with policy as code
Add an OPA or Conftest policy to your pipeline that fails any project plan missing a VPC change metric and alert. A simple check looks for the resources by name in the Terraform plan:
# conftest test against a terraform plan in JSON
deny[msg] {
not input.resource.google_logging_metric.vpc_network_changes
msg := "Missing required log-based metric for VPC network changes"
}
Tip: Pair the pipeline gate with a continuous scanner like Lensix so you catch drift in projects that were created outside your IaC, or where someone deleted the alert by hand. CI/CD catches what you provision, continuous scanning catches everything else.
Best practices
- Route alerts to a real on-call destination. An email nobody reads is worse than no alert, because it creates a false sense of coverage. Send these to PagerDuty, Opsgenie, or a monitored Slack channel.
- Cover the whole network family, not just networks. Apply the same pattern to firewall rule changes (CIS 2.7), route changes (CIS 2.8), and subnetwork changes. An attacker only needs one unwatched surface.
- Enrich the alert with context. Include the principal (
protoPayload.authenticationInfo.principalEmail) and source IP in the notification so responders can triage without digging through logs. - Tune for noise, do not silence. If a CI service account makes legitimate network changes constantly, filter that specific principal out rather than raising the threshold and missing real events.
- Test the pipeline. Make a harmless network change in a non-production project and confirm the alert actually fires. An untested alert is a hope, not a control.
Danger: Do not test by deleting a production network or removing a live peering connection. Both can sever connectivity for every workload on that network and trigger a real outage. Always validate alerts in an isolated test project.
VPC network monitoring is cheap to set up and expensive to skip. Log-based metrics carry no extra cost for the matching, and the alerting policy is included with Cloud Monitoring. The only real cost is the few minutes it takes to wire it up, against the hours or days you lose chasing an unexplained network change after the fact.

