Skip to content

gcp-oidc

helps configure and implement GitHub OIDC authentication with GCP for secure, keyless access from GitHub Actions to GCP resources

IDE:
claude
codex
vscode
Version:
0.0.0

GitHub OIDC Authentication for GCP

Overview

OpenID Connect (OIDC) allows your GitHub Actions workflows to access resources in GCP (Google Cloud Platform) without needing to store GCP credentials as long-lived GitHub secrets. This eliminates the security risks associated with static credentials and provides temporary, scoped access tokens through Workload Identity Federation (WIF).

Core Concepts

What is OIDC?

  • Federated Identity: GCP trusts GitHub's OIDC provider to authenticate workflows
  • Temporary Tokens: Short-lived tokens replace static credentials
  • Scoped Access: Fine-grained permissions based on repository, branch, and environment
  • Security: No secrets stored in GitHub; tokens are generated on-demand

What is Workload Identity Federation?

GCP's Workload Identity Federation (WIF) allows external identities (like GitHub Actions) to impersonate GCP service accounts without using service account keys.

Benefits of WIF:

  • Improved Security: Eliminates the need to store and manage long-lived service account keys
  • Automatic Key Rotation: Short-lived tokens automatically expire
  • Simplified Management: Focus permission management on the service account
  • Fine-grained Access Control: Grant specific IAM permissions to service accounts

Key Components

  1. Workload Identity Pool: Container for external identities (e.g., GitHub Actions runners)
  2. Workload Identity Provider: Maps external identities to GCP identities
  3. Service Account: Special account used by applications to access GCP resources
  4. Token Exchange: External token is exchanged with GCP Security Token Service (STS)
  5. Resource Access: Temporary GCP access token is used to access authorized resources

Authentication Flow

  1. GitHub Actions runner requests OIDC token from https://token.actions.githubusercontent.com
  2. External identity provider issues token
  3. Token is exchanged with GCP Security Token Service (STS)
  4. STS verifies token and issues temporary GCP access token
  5. Workload uses temporary token to access GCP resources via service account impersonation

Setup Methods

Method 1: Manual Configuration via gcloud CLI

Prerequisites

  • gcloud CLI installed and configured
  • GCP project with appropriate permissions
  • GitHub repository for Actions workflows

Step 1: Authenticate with GCP

# Login to gcloud
gcloud auth login

# Set your project
gcloud config set project PROJECT_ID

# Set application default credentials
gcloud auth application-default login

Step 2: Enable Required APIs

# Enable IAM Service Account Credentials API
gcloud services enable iamcredentials.googleapis.com

# Enable Security Token Service API
gcloud services enable sts.googleapis.com

# Enable IAM API
gcloud services enable iam.googleapis.com

# Enable Cloud Resource Manager API
gcloud services enable cloudresourcemanager.googleapis.com

Step 3: Create Service Account

# Create a service account for GitHub Actions
gcloud iam service-accounts create github-actions-sa \
    --display-name="GitHub Actions Service Account" \
    --description="Service account for GitHub Actions workflows"

# Get the service account email
export SA_EMAIL="github-actions-sa@PROJECT_ID.iam.gserviceaccount.com"

Step 4: Grant IAM Roles to Service Account

# Grant necessary roles (adjust based on your needs)
gcloud projects add-iam-policy-binding PROJECT_ID \
    --member="serviceAccount:${SA_EMAIL}" \
    --role="roles/storage.admin"

gcloud projects add-iam-policy-binding PROJECT_ID \
    --member="serviceAccount:${SA_EMAIL}" \
    --role="roles/compute.admin"

# Grant service account token creator role for impersonation
gcloud iam service-accounts add-iam-policy-binding ${SA_EMAIL} \
    --member="serviceAccount:${SA_EMAIL}" \
    --role="roles/iam.serviceAccountTokenCreator"

Step 5: Create Workload Identity Pool

# Create workload identity pool
gcloud iam workload-identity-pools create github-pool \
    --location="global" \
    --display-name="GitHub Actions Pool" \
    --description="Workload Identity Pool for GitHub Actions"

# Get the pool ID
export POOL_ID="projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool"

Step 6: Create Workload Identity Provider

# Create workload identity provider for GitHub
gcloud iam workload-identity-pools providers create-oidc github-provider \
    --location="global" \
    --workload-identity-pool="github-pool" \
    --issuer-uri="https://token.actions.githubusercontent.com" \
    --attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner" \
    --attribute-condition="assertion.repository_owner == 'YOUR_GITHUB_ORG'"

# Get the provider ID
export PROVIDER_ID="projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool/providers/github-provider"

Step 7: Allow Service Account Impersonation

# Allow the workload identity pool to impersonate the service account
# This grants access to specific repositories
gcloud iam service-accounts add-iam-policy-binding ${SA_EMAIL} \
    --role="roles/iam.workloadIdentityUser" \
    --member="principalSet://iam.googleapis.com/${POOL_ID}/attribute.repository/YOUR_GITHUB_ORG/YOUR_REPO"

# For wildcard repository access (less secure):
# gcloud iam service-accounts add-iam-policy-binding ${SA_EMAIL} \
#     --role="roles/iam.workloadIdentityUser" \
#     --member="principalSet://iam.googleapis.com/${POOL_ID}/attribute.repository_owner/YOUR_GITHUB_ORG"

Step 8: Get Workload Identity Provider Resource Name

# Get the full workload identity provider resource name
gcloud iam workload-identity-pools providers describe github-provider \
    --workload-identity-pool="github-pool" \
    --location="global" \
    --format="value(name)"

Note the following values for GitHub Actions configuration:

  • Workload Identity Provider: Full resource name from Step 8
  • Service Account Email: github-actions-sa@PROJECT_ID.iam.gserviceaccount.com

Method 2: Terraform Infrastructure as Code

Terraform provides a repeatable, version-controlled way to configure Workload Identity Federation for multiple projects and environments.

Prerequisites

  • Terraform installed (version >= 1.9.8)
  • gcloud CLI authenticated
  • GCP project created

Repository Structure

terraform/
├── provider.tf          # Provider configuration
├── iam.tf              # Service account and IAM bindings
├── gh-oidc.tf          # Workload Identity Pool and Provider
├── api.tf              # GCP API enablement
├── backend.tf          # Terraform state backend
├── constants.tf        # Constants and locals
├── variables.tf        # Input variables
├── outputs.tf          # Output values
└── tfvars/
    ├── dev.tfvars      # Development environment
    ├── staging.tfvars  # Staging environment
    └── prod.tfvars     # Production environment

Key Terraform Resources

1. Provider Configuration (provider.tf)

terraform {
  required_version = ">= 1.9.8"
  
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = ">= 5.15.0"
    }
    google-beta = {
      source  = "hashicorp/google-beta"
      version = ">= 5.15.0"
    }
  }
}

provider "google" {
  project = var.project_id
  region  = var.region
}

provider "google-beta" {
  project = var.project_id
  region  = var.region
}

2. Service Account and IAM (iam.tf)

# Create service account for GitHub Actions
resource "google_service_account" "github_actions_sa" {
  account_id   = "${var.service_account_name}-${var.environment}"
  display_name = "GitHub Actions Service Account"
  description  = "Service account for GitHub Actions workflows in ${var.environment}"
  project      = var.project_id
}

# Grant roles to service account
resource "google_project_iam_member" "github_actions_roles" {
  for_each = toset(var.service_account_roles)
  
  project = var.project_id
  role    = each.value
  member  = "serviceAccount:${google_service_account.github_actions_sa.email}"
}

# Additional storage admin role example
resource "google_project_iam_member" "storage_admin" {
  project = var.project_id
  role    = "roles/storage.admin"
  member  = "serviceAccount:${google_service_account.github_actions_sa.email}"
}

# Service account token creator for impersonation
resource "google_service_account_iam_member" "token_creator" {
  service_account_id = google_service_account.github_actions_sa.name
  role               = "roles/iam.serviceAccountTokenCreator"
  member             = "serviceAccount:${google_service_account.github_actions_sa.email}"
}

3. Workload Identity Federation (gh-oidc.tf)

# Use the Google GitHub Actions module for OIDC setup
module "gh_oidc" {
  source  = "terraform-google-modules/github-actions-runners/google//modules/gh-oidc"
  version = "~> 5.0"

  project_id = var.project_id
  
  # Workload Identity Pool configuration
  pool_id          = var.wif_pool_id
  pool_display_name = "GitHub Actions Pool - ${var.environment}"
  pool_description = "Workload Identity Pool for GitHub Actions in ${var.environment}"
  
  # Workload Identity Provider configuration
  provider_id          = var.wif_provider_id
  provider_display_name = "GitHub Provider - ${var.environment}"
  provider_description = "GitHub Actions OIDC Provider for ${var.environment}"
  
  # Attribute mapping from GitHub OIDC token to GCP attributes
  attribute_mapping = {
    "google.subject"             = "assertion.sub"
    "attribute.actor"            = "assertion.actor"
    "attribute.aud"              = "assertion.aud"
    "attribute.repository"       = "assertion.repository"
    "attribute.repository_owner" = "assertion.repository_owner"
  }
  
  # Condition to restrict access to specific organization or repository pattern
  # Option 1: Specific repository
  attribute_condition = "assertion.repository == '${var.github_org}/${var.github_repo_pattern}'"
  
  # Option 2: Repository pattern with startsWith
  # attribute_condition = "attribute.repository.startsWith('${var.github_org}/${var.github_repo_prefix}')"
  
  # Option 3: Organization-wide (less secure)
  # attribute_condition = "assertion.repository_owner == '${var.github_org}'"
  
  # Map service account to specific repositories
  # The attribute field defines which GitHub identities can impersonate this SA
  sa_mapping = {
    "${google_service_account.github_actions_sa.account_id}@${var.project_id}" = {
      sa_name   = google_service_account.github_actions_sa.id
      attribute = "attribute.repository/${var.github_org}/${var.github_repo_pattern}"
    }
  }
  
  # For organization-wide access (use with caution):
  # sa_mapping = {
  #   "${google_service_account.github_actions_sa.account_id}@${var.project_id}" = {
  #     sa_name   = google_service_account.github_actions_sa.id
  #     attribute = "attribute.repository_owner/${var.github_org}"
  #   }
  # }
  
  depends_on = [google_service_account.github_actions_sa, google_project_service.required_apis]
}

4. API Enablement (api.tf)

# Define required GCP APIs
locals {
  required_apis = [
    "iam.googleapis.com",
    "iamcredentials.googleapis.com",
    "sts.googleapis.com",
    "cloudresourcemanager.googleapis.com",
    "storage.googleapis.com",
    "compute.googleapis.com",
  ]
}

# Enable required APIs
resource "google_project_service" "required_apis" {
  for_each = toset(local.required_apis)
  
  project = var.project_id
  service = each.value
  
  disable_on_destroy = false
}

5. Backend Configuration (backend.tf)

terraform {
  backend "gcs" {
    bucket = "your-terraform-state-bucket"
    prefix = "github-oidc/terraform.tfstate"
  }
}

6. Constants (constants.tf)

locals {
  # Common roles for GitHub Actions
  default_sa_roles = [
    "roles/storage.admin",
    "roles/compute.instanceAdmin.v1",
    "roles/iam.serviceAccountUser",
  ]
  
  # Tags for resources
  common_labels = {
    managed_by  = "terraform"
    environment = var.environment
    team        = var.team
    purpose     = "github-actions-oidc"
  }
}

7. Variables (variables.tf)

variable "project_id" {
  type        = string
  description = "GCP project ID"
}

variable "region" {
  type        = string
  description = "GCP region"
  default     = "us-central1"
}

variable "environment" {
  type        = string
  description = "Environment (dev, staging, prod)"
  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Environment must be dev, staging, or prod."
  }
}

variable "team" {
  type        = string
  description = "Team name"
}

variable "service_account_name" {
  type        = string
  description = "Base name for the service account"
  default     = "github-actions-sa"
}

variable "service_account_roles" {
  type        = list(string)
  description = "List of IAM roles to grant to the service account"
  default     = []
}

variable "wif_pool_id" {
  type        = string
  description = "Workload Identity Pool ID"
  default     = "github-pool"
}

variable "wif_provider_id" {
  type        = string
  description = "Workload Identity Provider ID"
  default     = "github-provider"
}

variable "github_org" {
  type        = string
  description = "GitHub organization name"
}

variable "github_repo_pattern" {
  type        = string
  description = "GitHub repository pattern (e.g., 'my-repo' or 'my-repo-*' for wildcard)"
}

8. Outputs (outputs.tf)

output "service_account_email" {
  description = "Email of the created service account"
  value       = google_service_account.github_actions_sa.email
}

output "workload_identity_provider" {
  description = "Full resource name of the Workload Identity Provider"
  value       = module.gh_oidc.provider_name
}

output "workload_identity_pool" {
  description = "Full resource name of the Workload Identity Pool"
  value       = "projects/${var.project_id}/locations/global/workloadIdentityPools/${var.wif_pool_id}"
}

output "project_number" {
  description = "GCP project number"
  value       = data.google_project.project.number
}

data "google_project" "project" {
  project_id = var.project_id
}

Example Configuration (tfvars/dev.tfvars)

project_id             = "my-gcp-project-dev"
region                 = "us-central1"
environment            = "dev"
team                   = "platform-engineering"
service_account_name   = "github-actions-sa"
wif_pool_id           = "github-pool"
wif_provider_id       = "github-provider"
github_org            = "OptumInsight-Platform"
github_repo_pattern   = "my-project-*"

service_account_roles = [
  "roles/storage.admin",
  "roles/compute.instanceAdmin.v1",
  "roles/cloudfunctions.developer",
  "roles/iam.serviceAccountUser",
]

Deployment Commands

# Navigate to terraform directory
cd terraform

# Initialize Terraform
terraform init

# Validate configuration
terraform validate

# Preview changes
terraform plan -var-file=tfvars/dev.tfvars -out=tfplan

# Apply configuration
terraform apply tfplan

# View outputs
terraform output

# Save outputs for GitHub Actions
terraform output workload_identity_provider > wif_provider.txt
terraform output service_account_email > sa_email.txt

Terraform State Backend Setup

Before running the main configuration, create a GCS bucket for Terraform state:

# Create state bucket
gcloud storage buckets create gs://your-terraform-state-bucket \
    --project=PROJECT_ID \
    --location=US \
    --uniform-bucket-level-access

# Enable versioning
gcloud storage buckets update gs://your-terraform-state-bucket \
    --versioning

GitHub Actions Integration

Required Permissions

permissions:
  id-token: write   # Required for OIDC token generation
  contents: read    # Required for repository access

Basic Workflow Example

name: Deploy to GCP
on:
  push:
    branches:
      - main

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: uhg-runner
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        
      - name: 'Google auth'
        id: 'auth'
        uses: 'google-github-actions/auth@v2'
        with:
          project_id: PROJECT_ID
          workload_identity_provider: 'projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/gh-workflow-identity-pool/providers/gh-workflow-identity-provider'
          service_account: 'gcp-oi-data-catalog-dev-auto@PROJECT_ID.iam.gserviceaccount.com'
          create_credentials_file: true
          token_format: "access_token"
 

      - name: Set up Cloud SDK
        uses: google-github-actions/setup-gcloud@v2
        
      - name: Verify authentication
        run: |
          gcloud auth list
          gcloud config list
          gsutil ls

Advanced Workflow with Multiple Environments

name: Multi-Environment Deployment
on:
  push:
    branches:
      - main
      - develop

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: uhg-runner
    strategy:
      matrix:
        environment: [dev, staging, prod]
        include:
          - environment: dev
            project_id: my-project-dev
            wif_provider: projects/123456789/locations/global/workloadIdentityPools/github-pool-dev/providers/github-provider
            service_account: [email protected]
            condition: github.ref == 'refs/heads/develop'
          - environment: staging
            project_id: my-project-staging
            wif_provider: projects/234567890/locations/global/workloadIdentityPools/github-pool-staging/providers/github-provider
            service_account: github-actions-sa-staging@my-project-staging.iam.gserviceaccount.com
            condition: github.ref == 'refs/heads/main'
          - environment: prod
            project_id: my-project-prod
            wif_provider: projects/345678901/locations/global/workloadIdentityPools/github-pool-prod/providers/github-provider
            service_account: [email protected]
            condition: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch'
    
    if: ${{ matrix.condition }}
    environment: ${{ matrix.environment }}
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Authenticate to Google Cloud
        id: auth
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: ${{ matrix.wif_provider }}
          service_account: ${{ matrix.service_account }}
          
      - name: Set up Cloud SDK
        uses: google-github-actions/setup-gcloud@v2
        with:
          project_id: ${{ matrix.project_id }}
          
      - name: Deploy to ${{ matrix.environment }}
        run: |
          echo "Deploying to ${{ matrix.environment }}"
          gcloud auth list
          gcloud config get-value project

Integration with Cloud Storage

name: Upload to Cloud Storage
on:
  push:
    branches: [main]

permissions:
  id-token: write
  contents: read

jobs:
  upload:
    runs-on: uhg-runner
    steps:
      - uses: actions/checkout@v4
      
      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
          service_account: ${{ secrets.SA_EMAIL }}
          
      - name: Upload files to GCS
        run: |
          gsutil -m rsync -r ./dist gs://${{ secrets.GCS_BUCKET }}/
          gsutil -m setmeta -h "Cache-Control:public, max-age=3600" gs://${{ secrets.GCS_BUCKET }}/**

Integration with Cloud Run

name: Deploy to Cloud Run
on:
  push:
    branches: [main]

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: uhg-runner
    steps:
      - uses: actions/checkout@v4
      
      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
          service_account: ${{ secrets.SA_EMAIL }}
          
      - name: Set up Cloud SDK
        uses: google-github-actions/setup-gcloud@v2
        
      - name: Build and push container
        run: |
          gcloud builds submit --tag gcr.io/${{ secrets.PROJECT_ID }}/my-app:${{ github.sha }}
          
      - name: Deploy to Cloud Run
        run: |
          gcloud run deploy my-app \
            --image gcr.io/${{ secrets.PROJECT_ID }}/my-app:${{ github.sha }} \
            --platform managed \
            --region us-central1 \
            --allow-unauthenticated

Integration with Optum Artifactory

name: Build, Scan, and Deploy to GCP
on:
  push:
    branches: [main]

permissions:
  actions: read
  contents: write
  pull-requests: write
  security-events: write
  checks: write
  id-token: write

jobs:
  build-and-deploy:
    runs-on: [uhg-runner]
    steps:
      - uses: actions/checkout@v4
      
      # Configure Artifactory
      - name: Configure Artifactory Connection
        id: artifactory-setup
        uses: uhg-pipelines/epl-jf/configure-saas-connection@latest
        with:
          jfrog-project-key: your-project-key
          npm-setup: true
          
      # Build and publish to Artifactory
      - name: Build and Scan
        uses: optum-eeps/epl-actions/node-build-scan@v1
        with:
          jfrog-project-key: your-project-key
          jfrog-build-name: ${{ steps.artifactory-setup.outputs.jfrog-build-name }}
          jfrog-build-number: ${{ steps.artifactory-setup.outputs.jfrog-build-number }}
          npm-publish: true
          
      # Authenticate to GCP
      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
          service_account: ${{ secrets.SA_EMAIL }}
          
      # Deploy to GCP
      - name: Deploy to GCP
        run: |
          gsutil -m rsync -r ./dist gs://${{ secrets.GCS_BUCKET }}/
          gcloud compute url-maps invalidate-cdn-cache ${{ secrets.CDN_NAME }} --path "/*"

Security Best Practices

1. Least Privilege Access

Grant only the minimum permissions required:

# Specific bucket access
resource "google_storage_bucket_iam_member" "github_actions" {
  bucket = google_storage_bucket.app_bucket.name
  role   = "roles/storage.objectAdmin"
  member = "serviceAccount:${google_service_account.github_actions_sa.email}"
}

2. Restrict Repository Access

Use attribute conditions to limit access:

# Specific repository
attribute_condition = "assertion.repository == '${var.github_org}/${var.github_repo}'"

# Specific branch
attribute_condition = "assertion.repository == '${var.github_org}/${var.github_repo}' && assertion.ref == 'refs/heads/main'"

# Organization-wide (use with caution)
attribute_condition = "assertion.repository_owner == '${var.github_org}'"

3. Environment-Specific Service Accounts

Create separate service accounts for each environment:

# Development
gcloud iam service-accounts create github-actions-dev-sa

# Staging
gcloud iam service-accounts create github-actions-staging-sa

# Production
gcloud iam service-accounts create github-actions-prod-sa

4. Attribute Mapping Best Practices

Map all relevant GitHub token attributes:

attribute_mapping = {
  "google.subject"             = "assertion.sub"
  "attribute.actor"            = "assertion.actor"
  "attribute.repository"       = "assertion.repository"
  "attribute.repository_owner" = "assertion.repository_owner"
  "attribute.ref"              = "assertion.ref"
  "attribute.environment"      = "assertion.environment"
}

5. Token Lifetime Management

Configure appropriate token lifetimes:

- name: Authenticate to Google Cloud
  uses: google-github-actions/auth@v2
  with:
    workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
    service_account: ${{ secrets.SA_EMAIL }}
    token_format: 'access_token'
    access_token_lifetime: '3600s'  # 1 hour

Troubleshooting

Common Issues

1. "Workload Identity Pool does not exist"

# Verify pool exists
gcloud iam workload-identity-pools describe github-pool \
    --location=global \
    --project=PROJECT_ID

2. "Permission denied: Caller is not authorized to impersonate"

# Check service account IAM bindings
gcloud iam service-accounts get-iam-policy \
    github-actions-sa@PROJECT_ID.iam.gserviceaccount.com

# Add workload identity user binding
gcloud iam service-accounts add-iam-policy-binding \
    github-actions-sa@PROJECT_ID.iam.gserviceaccount.com \
    --role="roles/iam.workloadIdentityUser" \
    --member="principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool/attribute.repository/ORG/REPO"

3. "Invalid JWT signature"

  • Verify the issuer URI is correct: https://token.actions.githubusercontent.com
  • Check attribute mapping configuration
  • Ensure workflow has id-token: write permission

4. "Attribute condition not satisfied"

# Debug: Check what attributes are being sent
# View the OIDC token claims in GitHub Actions logs

# Verify attribute condition matches your repository
gcloud iam workload-identity-pools providers describe github-provider \
    --workload-identity-pool=github-pool \
    --location=global \
    --project=PROJECT_ID

Debugging Steps

Debug OIDC Token Claims

- name: Debug OIDC Token
  run: |
    # Get token from GitHub
    OIDC_TOKEN=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
      "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool/providers/github-provider" | jq -r .value)
    
    # Decode JWT payload (base64 decode the middle part)
    echo $OIDC_TOKEN | cut -d'.' -f2 | base64 -d | jq .

Test Service Account Permissions

# Test with gcloud
gcloud auth print-access-token \
    --impersonate-service-account=github-actions-sa@PROJECT_ID.iam.gserviceaccount.com

# List accessible buckets
gcloud storage ls \
    --impersonate-service-account=github-actions-sa@PROJECT_ID.iam.gserviceaccount.com

Verify API Enablement

# Check required APIs are enabled
gcloud services list --enabled --project=PROJECT_ID | grep -E "(iam|sts|iamcredentials)"

Advanced Patterns

1. Cross-Project Access

# Grant service account access to another project
resource "google_project_iam_member" "cross_project" {
  project = "another-project-id"
  role    = "roles/storage.objectViewer"
  member  = "serviceAccount:${google_service_account.github_actions_sa.email}"
}

2. Conditional Access Based on PR Labels

- name: Deploy to production
  if: contains(github.event.pull_request.labels.*.name, 'deploy-prod')
  uses: google-github-actions/auth@v2
  with:
    workload_identity_provider: ${{ secrets.WIF_PROVIDER_PROD }}
    service_account: ${{ secrets.SA_EMAIL_PROD }}

3. Multi-Region Deployment

strategy:
  matrix:
    region: [us-central1, us-east1, europe-west1]
steps:
  - name: Authenticate to Google Cloud
    uses: google-github-actions/auth@v2
    with:
      workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
      service_account: ${{ secrets.SA_EMAIL }}
      
  - name: Deploy to ${{ matrix.region }}
    run: |
      gcloud run deploy my-app \
        --image gcr.io/${{ secrets.PROJECT_ID }}/my-app:${{ github.sha }} \
        --platform managed \
        --region ${{ matrix.region }}

4. Service Account Chaining

- name: Authenticate with intermediate SA
  uses: google-github-actions/auth@v2
  with:
    workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
    service_account: intermediate-sa@PROJECT_ID.iam.gserviceaccount.com
    
- name: Impersonate production SA
  run: |
    gcloud config set auth/impersonate_service_account prod-sa@PROJECT_ID.iam.gserviceaccount.com
    # Now all gcloud commands use prod-sa

Getting Latest Versions

GitHub CLI Commands

# Get latest google-github-actions/auth version
gh api repos/google-github-actions/auth/releases/latest --jq '.tag_name'

# Get latest google-github-actions/setup-gcloud version
gh api repos/google-github-actions/setup-gcloud/releases/latest --jq '.tag_name'

# Check for Terraform module updates
terraform init -upgrade

GCP CLI Commands

# List all workload identity pools
gcloud iam workload-identity-pools list --location=global

# List all providers in a pool
gcloud iam workload-identity-pools providers list \
    --workload-identity-pool=github-pool \
    --location=global

# Get service account details
gcloud iam service-accounts describe github-actions-sa@PROJECT_ID.iam.gserviceaccount.com

Implementation Checklist

Pre-Setup

  • GCP project with appropriate permissions
  • gcloud CLI installed and authenticated
  • Terraform installed (if using IaC approach)
  • GitHub repository in organization

WIF Configuration

  • Required GCP APIs enabled
  • Workload Identity Pool created
  • Workload Identity Provider configured
  • Service account created
  • IAM roles granted to service account
  • Service account impersonation configured
  • Attribute mapping configured
  • Attribute conditions restrict access appropriately

GitHub Actions

  • Workflow includes id-token: write permission
  • WIF provider resource name configured as secret
  • Service account email configured as secret
  • Test workflow validates authentication
  • Error handling implemented

Security Validation

  • Least privilege principle applied
  • Repository access properly restricted
  • Token lifetime appropriate
  • Monitoring and alerting configured
  • Separate service accounts per environment

Documentation

  • Team training on OIDC and WIF concepts
  • Runbooks for troubleshooting
  • Security policies documented
  • Regular review schedule established

Support Resources

Configuration Examples Repository

For complete working examples, refer to:

  • Reference Implementation: OptumInsight-Platform/oi-data-catalog-bootstrap
    • provider.tf - Provider configuration
    • iam.tf - Service account setup
    • gh-oidc.tf - Workload Identity Federation configuration
    • api.tf - API enablement
    • variables.tf - Variable definitions

Remember: Workload Identity Federation eliminates the need for long-lived service account keys, significantly improving security posture while maintaining automation capabilities. Always use the principle of least privilege and restrict access to specific repositories and branches.