GCP Identity and Connection Patterns

Why this matters for data engineering

Data platforms almost always combine multiple trust boundaries:

  • A local engineer uses the CLI and client libraries.
  • A CI/CD system deploys infrastructure and code.
  • A Cloud Run job or VM executes data movement.
  • A secret store protects database passwords and vendor tokens.
  • A private database may require IAP or private VPC routing even when Google API calls do not.

If those layers are not separated clearly, teams end up debugging the wrong thing. A GitHub Actions workflow can authenticate correctly through WIF and still fail because it cannot impersonate the deployment service account. A Cloud Run job can have correct BigQuery IAM and still fail to reach a private SQL Server because the network path is wrong. This note keeps those failure domains separate.

Conceptual Model

flowchart TD
    A["Runtime context<br/>workstation, CI, Cloud Run, VM"] --> B["Identity type<br/>human or service account"]
    B --> C["Authentication path<br/>ADC, metadata server, impersonation, WIF"]
    C --> D["Credential material<br/>refresh token, access token, ID token"]
    D --> E["Authorization<br/>IAM allow, conditions, deny"]
    E --> F["Network path<br/>public API, private IP, IAP tunnel"]
    F --> G["Target resource<br/>BigQuery, GCS, Secret Manager, SQL Server"]

Read the diagram from left to right. A request only succeeds if every stage lines up:

  • the runtime uses the intended identity
  • the identity gets the correct short-lived token
  • IAM grants the needed permission
  • the network path can actually reach the target

Local Human Auth and ADC

The most important local distinction is that gcloud CLI auth and ADC are different stores. In this workstation both exist and both resolve to the same human identity, but that is an implementation detail, not a guarantee.

PowerShell / Linux | gcloud auth and config | inspect the local operator context

This subsection proves what the workstation is authenticated as right now and whether application code can get ADC without a key file.

List the active CLI account

Before any operator action that changes IAM, secrets, or infrastructure. It is typically triggered by you need to confirm which human identity the CLI will use. Read-only inspection of the local gcloud credential store. Avoid applying changes under the wrong user session.

gcloud auth list \
  --format='table(account,status)'
ACCOUNT                     ACTIVE
alexper.recovery@gmail.com  *

This is the gcloud auth login side of the world: the CLI is acting as the human user.

Read the active project and account from the local SDK config

At the start of any shell session or incident-response console. It is typically triggered by you want to see whether the local SDK context matches the project you intended to touch. Read-only local config lookup. Confirm project, account, and default region in one place.

gcloud config list \
  --format=json
{
  "accessibility": {
    "screen_reader": "False"
  },
  "core": {
    "account": "alexper.recovery@gmail.com",
    "disable_usage_reporting": "False",
    "project": "bq-wh-nb"
  },
  "run": {
    "region": "europe-west1"
  }
}

This is local workstation state, not an IAM policy. Changing it affects what the CLI targets, but it does not grant new permissions by itself.

Prove that ADC is available for client libraries

Before running Python, C#, Go, or Terraform code that relies on Google client libraries. It is typically triggered by you need to know whether local application code can obtain a token without a service-account key. Read-only token mint from the ADC credential store. Distinguish “the CLI works” from “application code can authenticate.”.

gcloud auth application-default print-access-token
ya29.a0Aa7MYi...[redacted]...0209

The important result is not the token value itself but the fact that a token was minted successfully. That means local ADC is configured. The practical distinction is:

  • gcloud auth login feeds CLI commands.
  • gcloud auth application-default login feeds application code through ADC.
FlagSyntaxDescription
--format--format='table(account,status)'Restricts the output shape to the fields you actually need during validation.

Service Accounts, WIF, and Project Machine Identity

The project’s machine identity layer is the next thing to inspect. In bq-wh-nb there are dedicated service accounts for GitHub Actions, pipeline state writes, and the main warehouse runtime account bq-wh-sa.

PowerShell / Linux | gcloud iam workload-identity-pools | inspect service accounts and the GitHub WIF trust chain

This subsection proves which machine identities exist and how the GitHub Actions trust path is restricted.

List the current project service accounts

At the start of least-privilege review or service-account cleanup. It is typically triggered by you need an inventory of machine identities already present in the project. Read-only IAM lookup on the project. Identify which principals should be considered runtime identities versus one-off lab principals.

gcloud iam service-accounts list \
  --project=bq-wh-nb \
  --format='table(displayName,email,disabled)'
DISPLAY NAME                            EMAIL                                                   DISABLED
Pipeline State Writer                   pipeline-state-writer@bq-wh-nb.iam.gserviceaccount.com  False
GitHub Actions (git-lab)                github-actions-sa@bq-wh-nb.iam.gserviceaccount.com      False
Codex Security Lab                      codex-sec-lab-260413@bq-wh-nb.iam.gserviceaccount.com   False
BQ WH SA                                bq-wh-sa@bq-wh-nb.iam.gserviceaccount.com               False
Compute Engine default service account  348557092514-compute@developer.gserviceaccount.com      False

This confirms the project already uses dedicated service accounts rather than only the default Compute Engine service account.

List the current WIF pools

Before reviewing CI/CD access or external workload access. It is typically triggered by you want to confirm whether the project already has federation configured. Read-only IAM lookup on the project’s workload-identity-pool collection. Identify external trust boundaries that can authenticate into the project without JSON keys.

gcloud iam workload-identity-pools list \
  --project=bq-wh-nb \
  --location=global \
  --format='table(name,state,displayName)'
NAME                                                                         STATE   DISPLAY_NAME
projects/348557092514/locations/global/workloadIdentityPools/github-actions  ACTIVE  GitHub Actions

There is exactly one pool, and it is dedicated to GitHub Actions.

Describe the GitHub provider inside the WIF pool

When validating which external OIDC issuer and claims are trusted. It is typically triggered by A repository deployment workflow needs to be audited or debugged. Read-only provider lookup. Show the exact issuer, attribute mapping, and provider-side condition used for GitHub federation.

gcloud iam workload-identity-pools providers describe github \
  --project=bq-wh-nb \
  --location=global \
  --workload-identity-pool=github-actions \
  --format=json
{
  "attributeCondition": "assertion.repository_owner == 'alp78'",
  "attributeMapping": {
    "attribute.actor": "assertion.actor",
    "attribute.repository": "assertion.repository",
    "attribute.repository_owner": "assertion.repository_owner",
    "google.subject": "assertion.sub"
  },
  "displayName": "GitHub",
  "name": "projects/348557092514/locations/global/workloadIdentityPools/github-actions/providers/github",
  "oidc": {
    "issuerUri": "https://token.actions.githubusercontent.com"
  },
  "state": "ACTIVE"
}

This shows two independent guardrails:

  • the provider only trusts GitHub’s OIDC issuer
  • the provider condition only allows repositories owned by alp78

Inspect which repository can impersonate the GitHub Actions service account

After reviewing the provider and before approving repository access. It is typically triggered by you need to know which repository can actually exchange WIF into the target service account. Read-only IAM policy lookup on the service account. Confirm the repo-level trust binding.

gcloud iam service-accounts get-iam-policy \
  github-actions-sa@bq-wh-nb.iam.gserviceaccount.com \
  --project=bq-wh-nb \
  --format=json
{
  "bindings": [
    {
      "members": [
        "principalSet://iam.googleapis.com/projects/348557092514/locations/global/workloadIdentityPools/github-actions/attribute.repository/alp78/git-lab"
      ],
      "role": "roles/iam.workloadIdentityUser"
    }
  ],
  "etag": "BwZPVI5NoOc=",
  "version": 1
}

This is narrower than the provider condition. The provider trusts alp78 as the owner. The service-account binding then narrows actual impersonation to the repository alp78/git-lab.

Current product note: GitHub WIF hardening

Current IAM guidance explicitly recommends attribute conditions for multi-tenant identity providers such as GitHub. The important design rule is to constrain trust twice: first at the provider with issuer and claim conditions, then again at the target service account with the narrowest possible principalSet membership.

FlagSyntaxDescription
--project--project=bq-wh-nbProject that owns the service-account and WIF resources.
--location--location=globalWIF pools and providers are global in this setup.
--workload-identity-pool--workload-identity-pool=github-actionsSelects the pool whose provider you want to inspect.
--format--format=jsonPreserves provider conditions and attribute mappings exactly.

Impersonation and Token Types

Impersonation is the cleanest operator path when you need to act like a workload. It avoids service-account keys and gives you a short-lived token with an audit trail that still points back to the human operator.

PowerShell / Linux | gcloud auth | mint short-lived tokens through service-account impersonation

This subsection proves the difference between an access token for Google APIs and an ID token for audience-bound service calls.

Mint an access token as the lab service account

Before testing Google API access as a workload identity. It is typically triggered by you need to reproduce workload behavior without exporting a key file. Read-only IAM Credentials token mint. Get a short-lived OAuth 2.0 access token for Google APIs as the target service account.

gcloud auth print-access-token \
  --impersonate-service-account=codex-sec-lab-260413@bq-wh-nb.iam.gserviceaccount.com
ya29.c.c0AZ4bNp...[redacted]...x0nvIl6WW314cVjOFg9SckfQuRf8JZft0ZWSJ3mf-kdjW0ROm9-YrhFwwgcZ-3-6-0ph6esxg4adr8QUy0le-lv3hF
WARNING: This command is using service account impersonation. All API calls will be executed as [codex-sec-lab-260413@bq-wh-nb.iam.gserviceaccount.com].

This token is for Google APIs such as BigQuery, Cloud Storage, Secret Manager, and Cloud Resource Manager.

Mint an audience-bound ID token as the same service account

Before calling an HTTPS endpoint that validates identity tokens instead of generic Google API access tokens. It is typically triggered by you need a token for Cloud Run invoker-style or IAP-protected HTTP flows. Read-only IAM Credentials token mint. Show the difference between “authenticate to Google APIs” and “authenticate to an audience-bound receiver.”.

gcloud auth print-identity-token \
  --impersonate-service-account=codex-sec-lab-260413@bq-wh-nb.iam.gserviceaccount.com \
  --audiences=https://example.com
eyJhbGciOiJSUzI1NiIsImtpZCI6ImIzZDk1Yjk1...[redacted]...v4BL-Q
WARNING: This command is using service account impersonation. All API calls will be executed as [codex-sec-lab-260413@bq-wh-nb.iam.gserviceaccount.com].

The operational split is:

  • access token: use for Google APIs
  • ID token: use for an HTTP receiver that validates an audience claim
FlagSyntaxDescription
--impersonate-service-account--impersonate-service-account=codex-sec-lab-260413@bq-wh-nb.iam.gserviceaccount.comTells gcloud to mint a short-lived token for the target service account.
--audiences--audiences=https://example.comSets the audience claim for the ID token.

Metadata Server and Private Network Paths

The metadata server and IAP are important, but they are runtime-specific. This workstation can prove local auth, WIF, and impersonation directly. It cannot safely demonstrate a GCE metadata token or an IAP tunnel in this note because those require a running Google runtime or a live target behind IAP.

Archived verified workflow

The live examples above verify:

  • human CLI auth
  • local ADC
  • project service-account inventory
  • GitHub WIF trust
  • impersonated access and ID tokens

Important conceptual note not safely executed here

The metadata server only exists inside supported runtimes such as GCE and Cloud Run. IAP tunneling only matters when the target is a private HTTPS or TCP service, such as a private VM or SQL Server host. Those runtime-specific flows are real, but they are not safely reproducible from this workstation-only note without a dedicated live target and network boundary.

Current product note: metadata tokens and GCE scopes

Metadata-issued access tokens are still the default machine-auth path for Compute Engine, but access scopes remain part of the effective boundary on GCE. In practice, the least-surprising design is an attached service account with narrow IAM roles and a VM scope configuration that does not accidentally block the API set the workload needs.

For the detailed CLI mechanics behind user auth, ADC, and WIF credential files, see 03-gcloud-authentication. For IAP and private VM access patterns, see the compute chapter notes such as 02-vm-ssh-and-file-transfer.

Runtime Decision Matrix

Runtime or sourceIdentity typeAuth pathCredential materialNetwork pathDefault recommendation
Local developer running gcloudHuman identitygcloud auth loginCLI refresh tokenPublic Google API endpointFine for operator commands, not for workload runtime
Local developer running Python or C#Human identity or impersonated service accountADC, optionally impersonationADC access tokenPublic Google API endpointPrefer ADC plus impersonation over downloading a JSON key
Cloud Run job calling BigQuery or Secret ManagerService account attached to the serviceMetadata serverShort-lived access tokenPublic Google API endpointBest default for serverless GCP workloads
GCE VM calling Google APIsService account attached to the VMMetadata serverShort-lived access tokenPublic Google API endpointBest default for VM-hosted GCP access
GitHub Actions deploying to GCPExternal GitHub identity service accountWIF plus impersonationExternal OIDC token short-lived Google tokenPublic Google API endpointBest practice for CI/CD, no JSON key
Airflow worker reading Secret Manager then hitting SQL ServerService account for Google APIs plus database login for SQLMetadata server or impersonation for Google APIs, DB auth for SQLAccess token for Google APIs plus DB credentialPublic Google API endpoint for Google services, private VPC or IAP for SQLTreat Google auth and SQL auth as separate layers
Workstation connecting to a private SQL Server VMHuman identity for tunnel plus SQL login at the DBgcloud auth for IAP, DB auth for SQLCLI token plus DB credentialIAP tunnel or private networkFirst debug the tunnel, then debug database auth

Data-Engineering Scenarios

ScenarioCorrect patternWhy it is correctWhat usually goes wrong
Local analyst reads BigQuery safelyHuman ADC or service-account impersonationNo key file, short-lived token, direct API pathThe developer only ran gcloud auth login, so client libraries still fail
GitHub Actions deploys a Cloud Run jobGitHub OIDC WIF provider deployment service accountNo repository secret key, repo-scoped trustThe repo is missing roles/iam.workloadIdentityUser on the service account
Cloud Run job reads Secret Manager and writes BigQueryAttached service account via metadata serverNo local credential material, best runtime patternThe service account has BigQuery roles but not secret-level access
Airflow triggers a data load and writes state to GCSAttached worker identity for Google APIs, separate DB or vendor credential only where requiredKeeps Google auth keyless while isolating non-Google secretsTeams mix Google API auth and downstream database auth into one opaque “credentials” problem
Operator tests a workload in production-like conditions--impersonate-service-accountExact IAM answer without JSON keysThe human user tests with their own broad access and misreads the result

Troubleshooting and Incident Response

SymptomLayer to suspect firstFastest live checkLikely fix
gcloud works but Python failsADC, not CLI authgcloud auth application-default print-access-tokenRun gcloud auth application-default login or use impersonation-aware ADC
GitHub workflow authenticates but deploy still failsService-account impersonation bindinggcloud iam service-accounts get-iam-policy on the target service accountAdd or narrow roles/iam.workloadIdentityUser
Cloud Run can call BigQuery but not private SQLNetwork pathCheck connector, IAP, or private IP routing designFix VPC connector, tunnel, or firewall, not IAM
Local test succeeds but workload fails in productionWrong identityCompare human auth path versus runtime-attached service accountRe-test through impersonation or runtime identity
HTTP call returns unauthorized but Google APIs workToken type mismatchCheck whether the receiver expects an ID tokenUse gcloud auth print-identity-token or the runtime equivalent

Quick Reference

NeedUseAvoid
Operator CLI sessiongcloud auth loginAssuming it automatically configures ADC
Local application codegcloud auth application-default login or impersonation-backed ADCDownloading a service-account key by default
Production workload on GCPAttached service account plus metadata serverBaking credentials into the image
External CI/CDWIF plus service-account impersonationJSON keys in repository secrets
Exact workload test from a laptop--impersonate-service-accountTesting with a broad human owner role
Private VM or SQL accessIAP or private VPC routingTreating it as only an IAM issue

GCP Identity and Connection Patterns References