GCP Cloud Account
Connect a Google Cloud project so DriftWise can scan live infrastructure and detect drift against your Terraform state.
Prerequisites
- gcloud CLI installed and authenticated
- A GCP project with permission to manage IAM
- A DriftWise API key or OIDC login
1. Enable the Cloud Asset API
DriftWise discovers GCP resources via Cloud Asset Inventory. Enable the API in the target project:
export GCP_PROJECT=your-project-id
gcloud services enable cloudasset.googleapis.com --project=$GCP_PROJECT
See Cloud Discovery for the full discovery model, including how resources are categorized.
2. Grant IAM Permissions
DriftWise needs two roles on the scanner service account:
- Cloud Asset Viewer (
roles/cloudasset.viewer) — grantscloudasset.assets.searchAllResources, the only permission the scanner itself uses. Cloud Asset Inventory projects data from across services, so DriftWise does not need per-service reader roles (compute.viewer,storage.objectViewer, etc.) for discovery. - Browser (
roles/browser) — grantsresourcemanager.projects.getandcompute.projects.get, used by DriftWise's one-time credential validation check when you register the account. Without it,POST /accountsfails with "permission denied for project" even though the scanner itself would have worked.
gcloud projects add-iam-policy-binding $GCP_PROJECT \
--member="serviceAccount:<SA_EMAIL>" \
--role="roles/cloudasset.viewer"
gcloud projects add-iam-policy-binding $GCP_PROJECT \
--member="serviceAccount:<SA_EMAIL>" \
--role="roles/browser"
The validation probe calls Compute's projects.get API, which
cloudasset.viewer does not grant. We intend to switch the probe onto Cloud
Asset Inventory so only cloudasset.viewer is required — until then, the
roles/browser grant closes the gap.
You can grant cloudasset.viewer at project, folder, or organization level. A broader scope lets one DriftWise account discover resources across multiple projects; a narrower scope keeps visibility scoped to a single project. roles/browser should be granted at project scope.
3. Choose a Credential Type
Option A: Service Account Key (simple, for dev/test)
Create a service account and download its JSON key:
gcloud iam service-accounts create driftwise-scanner \
--display-name="DriftWise Scanner"
gcloud projects add-iam-policy-binding $GCP_PROJECT \
--member="serviceAccount:driftwise-scanner@${GCP_PROJECT}.iam.gserviceaccount.com" \
--role="roles/cloudasset.viewer"
gcloud iam service-accounts keys create driftwise-sa-key.json \
--iam-account="driftwise-scanner@${GCP_PROJECT}.iam.gserviceaccount.com"
Service account keys are long-lived credentials. Use Workload Identity for production.
Option B: Workload Identity (recommended for production)
The commands below use the production app.driftwise.ai backend service account. For self-hosted or non-production deployments, fetch the equivalent from GET /api/v2/federation-info — see OIDC Federation.
Avoid managing service-account keys by letting DriftWise's backend service account impersonate a service account in your project. Unlike AWS and Azure, GCP federation does not use iss/sub/aud claims — you grant impersonation directly.
# Create the target service account in your project
gcloud iam service-accounts create driftwise-scanner \
--project=$GCP_PROJECT \
--display-name="DriftWise Scanner"
# Grant Cloud Asset Viewer on the target project
gcloud projects add-iam-policy-binding $GCP_PROJECT \
--member="serviceAccount:driftwise-scanner@${GCP_PROJECT}.iam.gserviceaccount.com" \
--role="roles/cloudasset.viewer"
# Allow DriftWise's backend service account to impersonate your scanner SA
gcloud iam service-accounts add-iam-policy-binding \
driftwise-scanner@${GCP_PROJECT}.iam.gserviceaccount.com \
--project=$GCP_PROJECT \
--role="roles/iam.serviceAccountTokenCreator" \
--member="serviceAccount:[email protected]"
DriftWise runs in its own Google Cloud project, not in yours. The binding above grants DriftWise's backend SA permission to mint short-lived access tokens for your scanner SA via iamcredentials.googleapis.com. Your SA still holds the scan permissions — DriftWise simply borrows its identity for the duration of a scan.
4. Add the account in DriftWise
Via the UI
- Open Cloud Scan in the left sidebar.
- Click + Add Account and select Google Cloud Platform.
- Enter your GCP Project ID (e.g.,
my-project-123). - Select your credential type and fill in the fields.
- Click Save.
Via the API
Call POST /orgs/:id/accounts with provider: "gcp" and one of two
credential types:
credential_type: "gcp_sa_json"—credential_refis a JSON-encoded object withproject_idandservice_account_json. Theservice_account_jsonvalue is the raw text of the downloaded key file — not base64. Becausecredential_refis itself a JSON string, every"in the key file must be escaped to\"and every newline in the private key must stay as the literal two-character sequence\n.credential_type: "gcp_workload_id"—credential_refis a JSON-encoded object withproject_id,target_service_account(the SA email; no key material), and_credential_type: "gcp_workload_id"(see note below).
_credential_type field inside credential_refToday the server parses credential_ref independently of the top-level
credential_type field, so OIDC / Workload Identity credentials must also
include _credential_type inside the inner JSON to trigger the federated
code path. Without it, the request is treated as a service account key with
an empty key and falls through to Application Default Credentials — the
target_service_account is silently ignored. This duplication will be
removed once the backend merges the fields at request time.
A shell one-liner to build the Service Account Key body correctly:
SA_JSON=$(jq -c '.' driftwise-sa-key.json)
CREDENTIAL_REF=$(jq -cn --arg sa "$SA_JSON" \
'{project_id:"your-project-id", service_account_json:$sa}')
Credentials are validated against GCP before save. The provider-reported
project ID must match external_account_id. See the
accounts tag of the API
reference for the full
request and response shapes.
Supported Resources
DriftWise discovers every resource Cloud Asset Inventory returns for the project (or folder / organization, depending on where you granted cloudasset.viewer). There is no per-type allowlist. Resources are normalized into eight broad categories (compute, storage, database, network, iam, serverless, messaging, other) regardless of provider. See Cloud Discovery for the full list with examples.
The underlying GCP asset type (for example compute.googleapis.com/Instance) is preserved alongside the category for exact-match drift detection.
Because Cloud Asset Inventory returns full resource configuration in one call, GCP scans have no separate enrichment phase — every row is stored with enrichment_status = n/a.
Reading Terraform state from GCS
When you link a GCP cloud account to a GCS state source, DriftWise uses the linked account's principal to read the state object. The principal needs read access on the state bucket.
- For service-account key credentials (
gcp_sa_json), grant the role on the service account named in the key. - For workload identity federation (
gcp_workload_id), grant the role on thetarget_service_account— not the federating principal.
Required role:
roles/storage.objectVieweron the state bucket (or at object scope)
Grant at bucket scope rather than project-wide. DriftWise reads one object per refresh and does not enumerate buckets. The grant is read-only; DriftWise never writes or locks state.
If you prefer strict separation, register a second cloud account
whose target SA has only the bucket-read role, then link that
account to the state source instead of the scanner account. The
cloud_account_id field on the state source chooses which
credentials are used for the fetch — no new feature required.
CMEK-encrypted buckets additionally need the target SA to have
roles/cloudkms.cryptoKeyDecrypter on the CMEK key. GCS's
server-side managed keys (the default) require no extra grants.
Troubleshooting
Scan fails with cloudasset.googleapis.com has not been used
The Cloud Asset API is not enabled on the project. Enable it:
gcloud services enable cloudasset.googleapis.com --project=$GCP_PROJECT
Allow a minute for the API enablement to propagate, then re-run the scan.
Scan fails with Permission 'cloudasset.assets.searchAllResources' denied
The service account is missing the cloudasset.viewer role (or a role that grants the permission). Grant it:
gcloud projects add-iam-policy-binding $GCP_PROJECT \
--member="serviceAccount:<SA_EMAIL>" \
--role="roles/cloudasset.viewer"
After granting IAM roles, allow up to 60 seconds for propagation before re-running the scan.
Scan completes with 0 resources and 0 errors
The credentials are valid and Cloud Asset Inventory responded, but no resources matched. Common causes:
- Empty project — verify manually:
gcloud asset search-all-resources --scope=projects/$GCP_PROJECT - Propagation delay — Cloud Asset Inventory takes up to several minutes to reflect newly-created resources. Re-run the scan shortly after.
- Region filter too narrow — if you passed
filter_regionsin the scan request body, verify those regions actually contain resources. Matching is prefix-based: a zone likeus-central1-amatches afilter_regionsentry ofus-central1.
Scan stuck in "pending"
The scan worker hasn't claimed it. Check the worker logs:
kubectl logs -f deploy/scan-worker
If the worker is running but scans stay pending, check for stuck scans:
SELECT id, status, retry_count, started_at FROM scan_runs
WHERE status = 'running' AND started_at < NOW() - INTERVAL '10 minutes';