Skip to content

Google Cloud Platform

This guide explains how RunWhen Local discovers GCP resources and GKE clusters using the native Google Cloud SDK, and how you can control discovery scope, level-of-detail (LoD), and authentication. Each configuration option is followed by a brief rationale so you can decide whether it is relevant to your environment.

RunWhen Local uses the native gcpapi backend by default — first-party google-cloud-* SDK collectors plus an optional Cloud Asset Inventory (CAI) accelerator. There is no CloudQuery binary or gcloud subprocess requirement. The legacy CloudQuery backend remains available as a fallback (gcpIndexerBackend: cloudquery).


Choosing a discovery backend

Two backends are available, selected by the top-level gcpIndexerBackend key (or the WB_GCP_INDEXER_BACKEND environment variable):

gcpIndexerBackend: gcpapi # default; or: cloudquery (legacy/fallback)
BackendDescription
gcpapi (default)Native google-cloud-* SDK collectors as the functional baseline, plus an optional Cloud Asset Inventory (CAI) accelerator. No CloudQuery binary or gcloud subprocesses.
cloudquery (legacy)Runs the CloudQuery GCP plugin against the configured project(s). Set gcpIndexerBackend: cloudquery to opt back in.

Both backends expose the same generation-rule contract (the CloudQuery table name is the resource_type), so rules don’t change when you flip the backend.

You can optionally select where discovered resources are persisted with the top-level resourceStoreBackend. sqlite is the default (it snapshots the discovered resource graph into a local SQLite database); set memory to keep only the in-memory registry:

gcpIndexerBackend: gcpapi
resourceStoreBackend: sqlite # default; use 'memory' to disable the SQLite snapshot

GKE Cluster Discovery

When using GCP credentials to discover Kubernetes resources in GKE clusters, configure clusters under gkeClusters in the cloudConfig.gcp block. At discovery time RunWhen Local builds a kubeconfig for each cluster directly from your GCP service account and merges them into ~/.kube/gke-kubeconfig, so the Kubernetes indexer can scan them. You do not need to mount an explicit cloudConfig.kubernetes.kubeconfigFile.

This path uses the native Google SDKs — there is no gcloud dependency. RunWhen Local calls the GKE Container API (google-cloud-container’s ClusterManagerClient) to fetch each cluster’s API-server endpoint and CA certificate, then mints a short-lived OAuth bearer token from the service account (google-auth) and writes it straight into the kubeconfig. The gke-gcloud-auth-plugin exec flow is deliberately avoided.

Explicit configuration

List each GKE cluster explicitly when autoDiscover is false or omitted. Each cluster needs a name (used verbatim as the kubeconfig context name) and a location (a zone such as us-central1-a for zonal clusters, or a region such as us-central1 for regional clusters). Per-cluster Level of Detail can be set:

cloudConfig:
# No cloudConfig.kubernetes block needed -- the kubeconfig is generated.
gcp:
applicationCredentialsFile: /shared/gcp.secret # or saSecretName / serviceAccountKey / ADC
projects:
- my-gcp-project
gkeClusters:
autoDiscover: false
clusters:
- name: sandbox-cluster-1
location: us-central1-a # zone (zonal) or region (regional)
projectId: my-gcp-project # optional; falls back to the workspace project
defaultNamespaceLOD: basic # optional per-cluster default LOD for its namespaces
namespaceLODs: # optional per-namespace overrides
kube-system: none
- name: prod-cluster
location: us-east1 # regional cluster
defaultNamespaceLOD: detailed

Auto-discovery

Enable autoDiscover: true to enumerate all GKE clusters visible to the authenticated credentials through the Container API (ClusterManagerClient.list_clusters over projects/<project>/locations/-, which spans every zone and region) — again, no gcloud:

cloudConfig:
gcp:
applicationCredentialsFile: /shared/gcp.secret
projects:
- my-gcp-project
gkeClusters:
autoDiscover: true
discoveryConfig:
projectId: my-gcp-project # optional; defaults to the gcp project

Explicitly listed clusters are always included and merged with anything discovered (explicitly named clusters are de-duplicated against the discovered set). Auto-discovery requires the service account to have container.clusters.list/get permissions (e.g. the roles/container.viewer role).

Per-cluster Level of Detail

defaultNamespaceLOD and namespaceLODs are honored for GKE exactly as they are for AKS and EKS: a cluster’s defaultNamespaceLOD sets the default LOD for every namespace in that cluster, and namespaceLODs overrides individual namespaces within it. These per-cluster values take precedence over the global defaultLOD for the cluster’s namespaces. Valid values are detailed, basic, and none.


GCP Resource Discovery

GCP discovery is scoped per project. List the projects to discover under projects, and set how much is collected per project with projectLevelOfDetails (detailed, basic, or none). A project whose effective LOD is none is skipped entirely.

The native gcpapi backend is also generation-rule-driven: within an in-scope project it only collects the resource types your loaded generation rules reference (plus the mandatory gcp_projects anchor).

Scoping options

KeyScopeWhen it is useful
projectsList of project IDsLimit discovery to specific projects.
projectLevelOfDetailsPer projectApply LoD on a project basis (e.g. detailed for production, basic for staging).
includeTagsAny GCP resourceDiscover only resources that carry specific labels.
excludeTagsAny GCP resourceOmit resources that carry specific labels.
cloudConfig:
gcp:
projects:
- my-gcp-project
- my-other-project
projectLevelOfDetails:
my-gcp-project: detailed
my-other-project: none

Cloud Asset Inventory (optional accelerator)

When using the native gcpapi backend, the supported functional baseline is the per-service typed google-cloud-* SDK collectors (plus the synthesized gcp_projects anchor): the high-value types are discovered using only the relevant per-service viewer roles (compute / storage / container / pubsub / iam), with or without Cloud Asset Inventory.

Cloud Asset Inventory (CAI) is an optional accelerator that broadens coverage to resource types lacking a typed collector. If the service account lacks roles/cloudasset.viewer (or the cloudasset.googleapis.com API is disabled), discovery runs normally on the typed baseline: the indexer logs an informational note (not an error) and simply skips the CAI-only types. No action is needed.

To enable the full CAI-accelerated set:

Terminal window
gcloud services enable cloudasset.googleapis.com --project=$PROJECT_ID
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SA_NAME@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/cloudasset.owner"

Authentication

Credentials are resolved in the following priority order (see src/indexers/gcp_common.py):

PriorityMethodConfig keyUse-case
1Kubernetes secretsaSecretNameService-account key stored as a K8s secret (recommended for production).
2Inline / file service-account keyserviceAccountKey / applicationCredentialsFileSA JSON key mounted into the container or pasted inline.
3Application Default Credentials (ADC)(none — fallback)Workload Identity on GKE, Compute Engine metadata, gcloud auth login, or GOOGLE_APPLICATION_CREDENTIALS.

Method 1: Service-account key file (applicationCredentialsFile)

Mount the service-account key into the container and point at it:

cloudConfig:
gcp:
applicationCredentialsFile: /shared/gcp.secret
projects:
- my-gcp-project

Creating a service account

Terminal window
export PROJECT_ID=[project-id]
export KEY_FILE=GCPServiceAccountKeyWorkspaceBuilder.json
export SA_NAME=runwhen-local-sa
gcloud iam service-accounts create $SA_NAME \
--description="Service Account for RunWhen Discovery" \
--display-name="RunWhen Discovery Service Account"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SA_NAME@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/viewer"
gcloud iam service-accounts keys create $KEY_FILE \
--iam-account=$SA_NAME@$PROJECT_ID.iam.gserviceaccount.com

The output is a service-account JSON key file which needs to be copied to the shared directory accessible to the RunWhen Local container image.

Method 2: Inline service-account key

cloudConfig:
gcp:
serviceAccountKey: |
{ "type": "service_account", "project_id": "my-gcp-project", ... }
projects:
- my-gcp-project

Method 3: Kubernetes secret

cloudConfig:
gcp:
saSecretName: gcp-credentials

Create the secret with a service-account key:

Terminal window
kubectl create secret generic gcp-credentials \
--from-file=serviceAccountKey=./gcp-sa-key.json \
-n runwhen-local

Method 4: Application Default Credentials (ADC) / Workload Identity

If no explicit credentials are configured, ADC is used. On GKE with Workload Identity configured, this means the pod’s bound Google Service Account (GSA) is used automatically — no long-lived key required. This is the GCP equivalent of AWS EKS IRSA / Pod Identity and Azure Managed Identity.

There is no useWorkloadIdentity flag — unlike AWS, GCP Workload Identity is just ADC, so leaving the credential fields empty is the entire configuration.

cloudConfig:
gcp:
projects:
- my-gcp-project

For a complete implementation guide to GKE Workload Identity (GSA/KSA setup, IAM bindings, Helm values), see GKE Workload Identity.

Do not set GOOGLE_APPLICATION_CREDENTIALS in the pod environment when using Workload Identity. That env var forces ADC to read a key file and short-circuits the metadata-server path that Workload Identity relies on.


Cross-cloud Workload Identity parity

CloudIdentity mechanismHow it’s enabledDetection
AWSEKS IRSA / Pod IdentityuseWorkloadIdentity: true or env varsAWS_WEB_IDENTITY_TOKEN_FILE / AWS_CONTAINER_CREDENTIALS_FULL_URI
AzureManaged IdentityFallback (no SP creds set)Absence of clientId / spSecretName
GCPGKE Workload Identity (via ADC)Fallback (no SA key set)Absence of saSecretName / serviceAccountKey / applicationCredentialsFile

All three follow the same shape: explicit credentials first, ambient workload-identity last.


LoD Resolution Order (Quick Reference)

  1. Namespace annotation config.runwhen.com/lod (Kubernetes namespaces within GKE clusters)
  2. Per-cluster defaultNamespaceLOD / namespaceLODs (GKE clusters)
  3. Per-project projectLevelOfDetails (GCP cloud resources)
  4. Workspace-level defaultLOD

Configuration Reference

FieldScopeDescription
gcpIndexerBackendtop-levelgcpapi (default) or cloudquery (legacy/fallback). Env: WB_GCP_INDEXER_BACKEND
resourceStoreBackendtop-levelWhere discovered resources are persisted: sqlite (default) or memory. Env: WB_RESOURCE_STORE_BACKEND
applicationCredentialsFilecloudConfig.gcpPath to a mounted service-account JSON key
serviceAccountKeycloudConfig.gcpInline service-account key (raw JSON or base64)
saSecretNamecloudConfig.gcpName of a Kubernetes secret holding serviceAccountKey / projectId
projectscloudConfig.gcpList of project IDs to discover
projectIdcloudConfig.gcpSingle project ID (alternative to projects)
projectLevelOfDetailscloudConfig.gcpPer-project LOD map: detailed / basic / none
includeTags / excludeTagscloudConfig.gcpOptional label-based include/exclude filters
gkeClusterscloudConfig.gcpGKE discovery-time kubeconfig generation via the native Google SDKs (no gcloud)
gkeClusters.autoDiscovercloudConfig.gcp.gkeClustersAuto-enumerate GKE clusters via the Container API (default: false)
gkeClusters.clusters[].namecloudConfig.gcp.gkeClustersCluster name; used verbatim as the kubeconfig context name
gkeClusters.clusters[].locationcloudConfig.gcp.gkeClustersCluster zone or region (also accepts zone / region)
gkeClusters.clusters[].projectIdcloudConfig.gcp.gkeClustersOptional per-cluster project ID; falls back to the workspace project
gkeClusters.clusters[].defaultNamespaceLODcloudConfig.gcp.gkeClustersPer-cluster default LOD for that cluster’s namespaces
gkeClusters.clusters[].namespaceLODscloudConfig.gcp.gkeClustersPer-namespace LOD overrides within that cluster
gkeClusters.discoveryConfig.projectIdcloudConfig.gcp.gkeClustersProject to enumerate when autoDiscover: true

End-to-End Example

workspaceName: gcp-discovery
workspaceOwnerEmail: platform@example.com
defaultLocation: location-01
defaultLOD: detailed
gcpIndexerBackend: gcpapi # default; native SDK, no CloudQuery
cloudConfig:
gcp:
# Authentication via secret
saSecretName: gcp-credentials
# Project scoping
projects:
- my-production-project
- my-staging-project
projectLevelOfDetails:
my-production-project: detailed
my-staging-project: basic
# GKE cluster discovery (native SDKs, no gcloud)
gkeClusters:
autoDiscover: true
discoveryConfig:
projectId: my-production-project
clusters:
- name: prod-cluster
location: us-east1
defaultNamespaceLOD: detailed
namespaceLODs:
kube-system: none
codeCollections:
- repoURL: "https://github.com/runwhen-contrib/google-cloud-codecollection"
branch: "main"

Execution flow:

  1. The gcp-credentials secret authenticates via a service-account key.
  2. The native gcpapi indexer discovers resources in my-production-project at detailed LoD and my-staging-project at basic LoD.
  3. The GKE Container API enumerates clusters in my-production-project; prod-cluster is also explicitly listed and merged.
  4. For each GKE cluster, a kubeconfig is generated with a short-lived OAuth token and merged into ~/.kube/gke-kubeconfig.
  5. The Kubernetes indexer scans the GKE clusters, applying per-cluster and per-namespace LoD.

Multi-Project Example (Selective Namespace Discovery)

This example demonstrates a common pattern: discovering GKE clusters across two projects with different namespace scoping per cluster, while also discovering cloud resources in both projects.

  • Cluster A in project beta — full discovery (all namespaces at detailed)
  • Cluster B in project staging — discover only specific namespaces (payments and api-gateway); skip everything else
  • Cloud resources (compute, storage, etc.) discovered in both projects
workspaceName: gcp-multi-project
workspaceOwnerEmail: platform@example.com
defaultLocation: location-01
defaultLOD: detailed
gcpIndexerBackend: gcpapi # default; native SDK, no CloudQuery
cloudConfig:
gcp:
saSecretName: gcp-credentials # SA must have access to both projects
# --- Cloud resource discovery (both projects) ---
projects:
- beta
- staging
projectLevelOfDetails:
beta: detailed
staging: basic
# --- GKE cluster discovery ---
gkeClusters:
autoDiscover: false
clusters:
# Cluster A: project beta — discover ALL namespaces
- name: cluster-a
location: us-central1 # zone or region
projectId: beta # overrides the workspace project
defaultNamespaceLOD: detailed
# Cluster B: project staging — discover ONLY specific namespaces
# Set defaultNamespaceLOD to 'none' so every namespace is skipped
# by default, then selectively enable specific ones.
- name: cluster-b
location: us-east1
projectId: staging
defaultNamespaceLOD: none # skip all namespaces by default
namespaceLODs:
payments: detailed # only these namespaces get indexed
api-gateway: basic
codeCollections:
- repoURL: "https://github.com/runwhen-contrib/google-cloud-codecollection"
branch: "main"

Execution flow:

  1. The gcp-credentials secret authenticates (SA must have access to both beta and staging).
  2. The native gcpapi indexer discovers cloud resources in beta at detailed LoD and staging at basic LoD.
  3. For cluster-a (project beta), a kubeconfig is generated and the Kubernetes indexer scans all namespaces at detailed LoD.
  4. For cluster-b (project staging), a kubeconfig is generated but the Kubernetes indexer scans only the payments and api-gateway namespaces — all other namespaces are skipped because defaultNamespaceLOD is none.

How none works for namespaces: When defaultNamespaceLOD: none is set, every namespace in that cluster defaults to “skip entirely.” Individual namespaces are then re-enabled via namespaceLODs with basic or detailed. This is the recommended pattern for restricting Kubernetes discovery to specific namespaces within a single cluster.


Legacy CloudQuery Backend

If you need the CloudQuery backend (e.g. for resource types not yet covered by the native SDK collectors), set:

gcpIndexerBackend: cloudquery

Both backends produce the same registry shape, so generation rules and SLX templates do not change when switching.