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)| Backend | Description |
|---|---|
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: gcpapiresourceStoreBackend: sqlite # default; use 'memory' to disable the SQLite snapshotGKE 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: detailedAuto-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 projectExplicitly 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
| Key | Scope | When it is useful |
|---|---|---|
projects | List of project IDs | Limit discovery to specific projects. |
projectLevelOfDetails | Per project | Apply LoD on a project basis (e.g. detailed for production, basic for staging). |
includeTags | Any GCP resource | Discover only resources that carry specific labels. |
excludeTags | Any GCP resource | Omit resources that carry specific labels. |
cloudConfig: gcp: projects: - my-gcp-project - my-other-project projectLevelOfDetails: my-gcp-project: detailed my-other-project: noneCloud 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:
gcloud services enable cloudasset.googleapis.com --project=$PROJECT_IDgcloud 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):
| Priority | Method | Config key | Use-case |
|---|---|---|---|
| 1 | Kubernetes secret | saSecretName | Service-account key stored as a K8s secret (recommended for production). |
| 2 | Inline / file service-account key | serviceAccountKey / applicationCredentialsFile | SA JSON key mounted into the container or pasted inline. |
| 3 | Application 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-projectCreating a service account
export PROJECT_ID=[project-id]export KEY_FILE=GCPServiceAccountKeyWorkspaceBuilder.jsonexport SA_NAME=runwhen-local-sagcloud 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.comThe 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-projectMethod 3: Kubernetes secret
cloudConfig: gcp: saSecretName: gcp-credentialsCreate the secret with a service-account key:
kubectl create secret generic gcp-credentials \ --from-file=serviceAccountKey=./gcp-sa-key.json \ -n runwhen-localMethod 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-projectFor a complete implementation guide to GKE Workload Identity (GSA/KSA setup, IAM bindings, Helm values), see GKE Workload Identity.
Do not set
GOOGLE_APPLICATION_CREDENTIALSin 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
| Cloud | Identity mechanism | How it’s enabled | Detection |
|---|---|---|---|
| AWS | EKS IRSA / Pod Identity | useWorkloadIdentity: true or env vars | AWS_WEB_IDENTITY_TOKEN_FILE / AWS_CONTAINER_CREDENTIALS_FULL_URI |
| Azure | Managed Identity | Fallback (no SP creds set) | Absence of clientId / spSecretName |
| GCP | GKE 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)
- Namespace annotation
config.runwhen.com/lod(Kubernetes namespaces within GKE clusters) - Per-cluster
defaultNamespaceLOD/namespaceLODs(GKE clusters) - Per-project
projectLevelOfDetails(GCP cloud resources) - Workspace-level
defaultLOD
Configuration Reference
| Field | Scope | Description |
|---|---|---|
gcpIndexerBackend | top-level | gcpapi (default) or cloudquery (legacy/fallback). Env: WB_GCP_INDEXER_BACKEND |
resourceStoreBackend | top-level | Where discovered resources are persisted: sqlite (default) or memory. Env: WB_RESOURCE_STORE_BACKEND |
applicationCredentialsFile | cloudConfig.gcp | Path to a mounted service-account JSON key |
serviceAccountKey | cloudConfig.gcp | Inline service-account key (raw JSON or base64) |
saSecretName | cloudConfig.gcp | Name of a Kubernetes secret holding serviceAccountKey / projectId |
projects | cloudConfig.gcp | List of project IDs to discover |
projectId | cloudConfig.gcp | Single project ID (alternative to projects) |
projectLevelOfDetails | cloudConfig.gcp | Per-project LOD map: detailed / basic / none |
includeTags / excludeTags | cloudConfig.gcp | Optional label-based include/exclude filters |
gkeClusters | cloudConfig.gcp | GKE discovery-time kubeconfig generation via the native Google SDKs (no gcloud) |
gkeClusters.autoDiscover | cloudConfig.gcp.gkeClusters | Auto-enumerate GKE clusters via the Container API (default: false) |
gkeClusters.clusters[].name | cloudConfig.gcp.gkeClusters | Cluster name; used verbatim as the kubeconfig context name |
gkeClusters.clusters[].location | cloudConfig.gcp.gkeClusters | Cluster zone or region (also accepts zone / region) |
gkeClusters.clusters[].projectId | cloudConfig.gcp.gkeClusters | Optional per-cluster project ID; falls back to the workspace project |
gkeClusters.clusters[].defaultNamespaceLOD | cloudConfig.gcp.gkeClusters | Per-cluster default LOD for that cluster’s namespaces |
gkeClusters.clusters[].namespaceLODs | cloudConfig.gcp.gkeClusters | Per-namespace LOD overrides within that cluster |
gkeClusters.discoveryConfig.projectId | cloudConfig.gcp.gkeClusters | Project to enumerate when autoDiscover: true |
End-to-End Example
workspaceName: gcp-discoveryworkspaceOwnerEmail: platform@example.comdefaultLocation: location-01defaultLOD: 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:
- The gcp-credentials secret authenticates via a service-account key.
- The native
gcpapiindexer discovers resources in my-production-project at detailed LoD and my-staging-project at basic LoD. - The GKE Container API enumerates clusters in my-production-project; prod-cluster is also explicitly listed and merged.
- For each GKE cluster, a kubeconfig is generated with a short-lived OAuth token and merged into
~/.kube/gke-kubeconfig. - 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 atdetailed) - Cluster B in project
staging— discover only specific namespaces (paymentsandapi-gateway); skip everything else - Cloud resources (compute, storage, etc.) discovered in both projects
workspaceName: gcp-multi-projectworkspaceOwnerEmail: platform@example.comdefaultLocation: location-01defaultLOD: 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:
- The gcp-credentials secret authenticates (SA must have access to both
betaandstaging). - The native
gcpapiindexer discovers cloud resources in beta at detailed LoD and staging at basic LoD. - For cluster-a (project
beta), a kubeconfig is generated and the Kubernetes indexer scans all namespaces at detailed LoD. - For cluster-b (project
staging), a kubeconfig is generated but the Kubernetes indexer scans only thepaymentsandapi-gatewaynamespaces — all other namespaces are skipped becausedefaultNamespaceLODisnone.
How
noneworks for namespaces: WhendefaultNamespaceLOD: noneis set, every namespace in that cluster defaults to “skip entirely.” Individual namespaces are then re-enabled vianamespaceLODswithbasicordetailed. 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- Currently supported source plugin: GCP
- Available resources: See the CloudQuery tables documentation
Both backends produce the same registry shape, so generation rules and SLX templates do not change when switching.