gcp-oidc
helps configure and implement GitHub OIDC authentication with GCP for secure, keyless access from GitHub Actions to GCP resources
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
- Workload Identity Pool: Container for external identities (e.g., GitHub Actions runners)
- Workload Identity Provider: Maps external identities to GCP identities
- Service Account: Special account used by applications to access GCP resources
- Token Exchange: External token is exchanged with GCP Security Token Service (STS)
- Resource Access: Temporary GCP access token is used to access authorized resources
Authentication Flow
- GitHub Actions runner requests OIDC token from
https://token.actions.githubusercontent.com - External identity provider issues token
- Token is exchanged with GCP Security Token Service (STS)
- STS verifies token and issues temporary GCP access token
- Workload uses temporary token to access GCP resources via service account impersonation
Setup Methods
Method 1: Manual Configuration via gcloud CLI
Prerequisites
gcloudCLI 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 8Service 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)
gcloudCLI 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: writepermission
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
-
gcloudCLI 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: writepermission - 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
- GCP Documentation: Workload Identity Federation
- GitHub Actions: google-github-actions/auth
- Terraform Module: terraform-google-modules/github-actions-runners
- GitHub Documentation: About security hardening with OpenID Connect
- GCP Best Practices: IAM Best Practices
Configuration Examples Repository
For complete working examples, refer to:
- Reference Implementation:
OptumInsight-Platform/oi-data-catalog-bootstrapprovider.tf- Provider configurationiam.tf- Service account setupgh-oidc.tf- Workload Identity Federation configurationapi.tf- API enablementvariables.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.

