Skip to content

Drift Detection

asbrodova edited this page Aug 13, 2026 · 3 revisions

Module: Drift Detection


Overview

The Drift Detection module compares the live, effective configuration of two configured GCP environments in one read-only call. Ask for a whole-environment comparison, or narrow the request to one or more components such as Cloud Run, GKE, networking, Pub/Sub, or BigQuery.

The comparison is symmetric. Neither environment is treated as a baseline, target, source of truth, or desired state. Results always use the configured aliases:

  • A resource present in prod but absent from dev is Missing in dev and Only in prod.
  • A resource present in dev but absent from prod is Missing in prod and Only in dev.
  • A matched resource with different effective settings is Changed, with values labeled dev and prod.

This makes the output safe to read in either order. “Compare prod and dev” does not silently assign special meaning to the first or second environment.


Enable the Module

Drift detection requires at least two configured environments. For local use, add them to ~/.aura-tracker.yaml:

environments:
  - project_id: my-company-dev
    alias: dev
    default: true
  - project_id: my-company-prod
    alias: prod

Start only the drift tool:

./aura-tracker-gcp --modules drift

For a container or hosted deployment, use the equivalent JSON setting:

GCP_ENVIRONMENTS_JSON='[{"project_id":"my-company-dev","alias":"dev","default":true},{"project_id":"my-company-prod","alias":"prod"}]' \
  ./aura-tracker-gcp --modules drift

Both selectors are mandatory for a comparison; the default environment is not silently substituted for either side. Aliases are case-insensitive, and configured project IDs are accepted as input, but responses use aliases and do not expose aliased project IDs.

See Configure Your Environment for all validation and privacy rules.


What You Can Ask

Whole environments

“Find drifts between dev and prod.”

“What is different between project 111 and project 222?”

When no component is named, components is omitted and all supported components are compared.

One component

“Find diffs between prod Cloud Run and dev Cloud Run.”

“What is the difference between dev GKE setup and prod GKE setup?”

Several components

“Compare networking and Pub/Sub in dev and prod.”

“Compare Cloud Run, Cloud SQL, and Scheduler between staging and prod. Show detailed field differences.”

A narrower resource scope

“Compare the api service in dev and prod Cloud Run.”

“Compare GKE workloads in the payments namespace between dev and prod.”

The MCP server instructions route drift, diff, difference, parity, and two-environment comparison requests to gcp_compare_environments. The final answer is presented as a summary, alias-named missing-resource sections, changed fields with values from both aliases, and coverage gaps last.


Supported Components

Component value Compared resources and configuration
bigquery Datasets, tables, partition fields, labels, and bounded table schemas
cloudrun Services, jobs, traffic, ingress, scaling, security settings, and the selected revision template
cloudsql Instances, database version, region, tier, and labels; advanced settings are reported as a coverage limitation
datastores Spanner, AlloyDB, Firestore, and Memorystore inventory and exposed configuration
eventarc Triggers, destinations, transport topics, filters, service accounts, and labels
functions Gen 1 and Gen 2 functions, runtime, entry point, memory, timeout, identity, VPC connector, and labels
gke Clusters, node pools, versions, networking, identity, private-cluster and security settings, autoscaling, and add-ons
gke_workloads Workloads, container specs, Services, Ingress/HTTPRoute resources, and NetworkPolicies across discovered clusters
iam Service-account inventory and configuration; project and resource bindings are explicitly reported as not covered
monitoring Alert policies, uptime checks, SLOs, and dashboards
networking Load balancers, URL maps, NEGs, API Gateways, VPC networks, subnets, and PSC endpoints
pubsub Topics, labels, subscriptions, destinations, dead-letter topics, and filters
scheduler Jobs, schedules, time zones, target kinds, target references, and descriptions
secretmanager Secret names, labels, and replication mode; secret payloads are never read
storage Buckets, location/class, labels, versioning, uniform access, public-access prevention, and lifecycle-rule counts
supplychain Artifact Registry repositories, Cloud Build triggers, and Service Directory namespaces
tasks Queue presence; current rate and retry settings are reported as a coverage limitation
vpcaccess Serverless VPC Access connectors and networks
workflows Workflow presence, descriptions, and labels

“Whole environment” means every component in this supported comparison surface. It does not imply that every GCP product or every field in every Google API is collected. Known limitations are returned in coverage and warnings rather than hidden.


Tool

gcp_compare_environments

Compares two distinct configured environments.

Parameter Required Default Description
environment_a yes First configured alias or project ID; positional only, not authoritative
environment_b yes Second configured alias or project ID; must resolve to a different environment
components no all supported One or more component values from the table above
resource_names no all Exact resource names to include, up to 100
locations no all Regions or zones to include, up to 100
namespaces no all Kubernetes namespaces when comparing gke_workloads, up to 100
detail_level no standard summary, standard, or detailed
include_unchanged no false Include matched resources whose normalized configuration is equivalent
max_changes no 250 Maximum resource results returned, from 1 to 1000

The tool is read-only, side-effect-free, and bounded by a 90-second comparison budget. Collection runs with bounded concurrency, and each component is capped at 500 collected resources per environment. If a cap or timeout is reached, coverage becomes partial.

Direct argument examples

Whole environment:

{
  "environment_a": "dev",
  "environment_b": "prod"
}

Selected components and a resource filter:

{
  "environment_a": "dev",
  "environment_b": "prod",
  "components": ["cloudrun", "gke"],
  "resource_names": ["api"],
  "detail_level": "detailed"
}

Reading the Result

The response separates the comparison result from collection coverage.

Result values

result Meaning
differences_found At least one matched resource changed, or a resource was conclusively found in only one environment
parity At least one resource was compared, no differences were found, and coverage was complete
no_differences_observed Comparable resources matched, but partial coverage prevents a full parity claim
no_comparable_resources No resource pair could be compared, commonly because both scopes were empty or one side failed collection

Resource statuses

status Meaning
different Both resources were matched, but normalized configuration differs
missing_in_environment Resource presence was checked completely; missing_in contains the exact alias
equivalent Matched configuration is equivalent; returned only when include_unchanged=true
unknown_due_to_coverage A resource was observed on only one side, but incomplete collection prevents a missing-resource claim

summary.resources_only_in is alias-named rather than side-oriented:

{
  "resources_only_in": [
    {"environment": "dev", "resources": 2},
    {"environment": "prod", "resources": 1}
  ]
}

A changed field carries values from both environments:

{
  "path": "/revision_template/min_instances",
  "change_type": "modified",
  "category": "scaling",
  "importance": "medium",
  "values_by_environment": [
    {"environment": "dev", "value": 0, "present": true},
    {"environment": "prod", "value": 3, "present": true}
  ]
}

The summary detail level omits the full resource list and keeps up to three field differences per highlight. standard keeps up to ten field differences per resource. detailed returns every collected field difference, subject to max_changes and collection limits.


Matching and Normalization

Resources are matched deterministically by resource type and logical name. Location and qualifiers—such as dataset, cluster, namespace, or Kubernetes kind—disambiguate repeated names. A uniquely named resource can still be matched when its location changes, allowing the location difference to be reported as configuration drift.

Before comparison, the engine:

  • replaces environment-local project IDs with a neutral ${PROJECT} marker;
  • removes operational fields such as timestamps, readiness, status, generated endpoints, etags, and generated revision identities;
  • normalizes unordered arrays and aligns named objects before producing field paths;
  • preserves ordered command and argument arrays;
  • compares the current Cloud Run revision through a project-normalized safe fingerprint, detecting literal environment-value changes without returning those values;
  • compares arbitrary GKE annotation changes through sorted, private one-way fingerprints even when the number of withheld annotations is unchanged;
  • sorts resources, fields, coverage, and warnings for stable output.

The v2 normalizer also applies defense-in-depth output sanitization after comparison. Sensitive path variants and containers—credentials, tokens, authorization, cookies, signatures, DSNs, connection strings, private keys, secret-bearing annotations, and environment literal fingerprints—are replaced with [REDACTED]. URL/URI/endpoint fields preserve useful routing structure while removing userinfo and fragments and replacing every query value. Diagnostic warnings and coverage errors are scrubbed for embedded credentials, JWTs, private keys, and credential-bearing URLs.

Normalization prevents expected deployment noise from overwhelming the answer while retaining meaningful scaling, security, networking, runtime, and application-configuration differences.


Coverage and Failure Safety

Coverage is recorded independently for every component and alias as complete, partial, or error.

An API error is never converted into an empty environment. When either side is partial, an unmatched resource is labeled unknown_due_to_coverage; the tool does not claim it is missing. This distinction is particularly important for:

  • GKE clusters that are private, unreachable, or denied by Kubernetes RBAC;
  • regional APIs where only some regions can be listed;
  • truncated BigQuery schemas or component resource caps;
  • IAM, Cloud SQL, and Cloud Tasks fields not exposed by the current collectors;
  • timeouts and permission failures.

The top-level coverage_status is partial when any requested component has a gap. Review coverage and warnings before treating an absence as proof.


Data Safety

  • The tool never mutates either environment.
  • Secret Manager payloads are never requested.
  • Cloud Run literal environment values contribute only to a one-way configuration fingerprint.
  • GKE literal environment-variable values are compared in memory but replaced with [REDACTED] in reported differences.
  • Arbitrary GKE annotation values and their fingerprints never serialize; fingerprint differences are replaced with [REDACTED].
  • Sensitive field names and semantic-array identity values are also removed from public JSON-pointer paths, preventing a secret from leaking through a difference path or summary rather than its value.
  • Secret references are represented by names, never secret payloads.
  • Configured project IDs are replaced by aliases in results, errors, coverage messages, and summaries.
  • The regular anonymization middleware still runs after drift comparison when ANONYMIZE_ENABLED=true.

Required APIs and IAM

Drift detection reuses the existing read-only collectors. Enable the APIs and grant the viewer roles required by every component you intend to compare. The same server identity must have those permissions in both projects.

For example:

Compared scope Relevant module documentation
Cloud Run Cloud Run
GKE clusters and workloads GKE, GKE Workloads
Networking Networking, Serverless VPC Access
Pub/Sub and eventing [[Pub/Sub
BigQuery metadata and schemas [[MCP Resources Reference
Data and storage Cloud SQL, Data Stores, Cloud Storage
IAM and secrets IAM, Secret Manager
Monitoring Cloud Monitoring and Trace
Supply chain Supply Chain

Run the idempotent setup once per real project ID, not per alias:

PROJECT_ID=my-company-dev bash scripts/setup-iam.sh
PROJECT_ID=my-company-prod bash scripts/setup-iam.sh

For GKE workload comparison, the identity also needs Kubernetes read-only RBAC in each cluster. Inaccessible clusters appear as coverage gaps.


Related Modules

Module How it complements drift detection
archgraph Visualize the live topology around resources that differ
security Run a deeper security-posture assessment after security-relevant drift is found
incident Correlate runtime evidence when a configuration difference may explain an outage
tagging Check governance tags that are outside the current drift collection surface
cost Investigate whether changed capacity or traffic also changed spend

Clone this wiki locally