Articles Security & IAM

Zero-Trust CI/CD with Azure DevOps and Workload Identity Federation

🎧 Listen to Article Client-side voice reader powered by Web Speech API.
Ready to play
Speed:

Zero-Trust CI/CD with Azure DevOps and Workload Identity Federation

Long-lived service account keys stored in CI/CD secret stores represent one of the largest security liabilities in cloud engineering. A compromised JSON key provides perpetual access until explicitly revoked.

In the AustinSS platform, we enforce a zero-secret policy across all repositories: no service account keys exist in git, Azure DevOps variable groups, or agent disks.


How Workload Identity Federation (WIF) Works

Workload Identity Federation allows external identity providers (like Azure DevOps) to exchange short-lived OIDC tokens for Google Cloud IAM access tokens:

[Azure DevOps Agent]
       │
       │ 1. Mint JWT ID Token ($idToken)
       ▼
[Google Security Token Service (STS)]
       │
       │ 2. Validate JWT signature against Azure OIDC discovery endpoint
       ▼
[GCP Workload Identity Pool]
       │
       │ 3. Exchange federated token for short-lived IAM access token
       ▼
[GCP Service Account Impersonation (sa-tf-website)]
       │
       │ 4. Execute Terraform / gcloud deploy commands

1. OIDC Token Exchange in Pipeline Bash

In Azure DevOps pipeline tasks, an OIDC token is automatically generated when configuring an Azure CLI service connection:

# 1. Exchange ADO OIDC token with Google STS
token_resp=$(curl -fsS -X POST "https://sts.googleapis.com/v1/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
  -d "audience=//iam.googleapis.com/${WORKLOAD_IDENTITY_PROVIDER}" \
  -d "scope=https://www.googleapis.com/auth/cloud-platform" \
  -d "requested_token_type=urn:ietf:params:oauth:token-type:access_token" \
  -d "subject_token_type=urn:ietf:params:oauth:token-type:jwt" \
  -d "subject_token=${idToken}")

federated_token=$(echo "$token_resp" | jq -r .access_token)

# 2. Mint short-lived 1-hour GCP service account access token
sa_resp=$(curl -fsS -X POST "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${SERVICE_ACCOUNT_EMAIL}:generateAccessToken" \
  -H "Authorization: Bearer $federated_token" \
  -H "Content-Type: application/json" \
  -d '{"scope": ["https://www.googleapis.com/auth/cloud-platform"]}')

export CLOUDSDK_AUTH_ACCESS_TOKEN=$(echo "$sa_resp" | jq -r .accessToken)

2. Hardening Attribute Conditions

To ensure that only specific repositories and branches in your Azure DevOps organization can impersonate the target service account, configure strict WIF attribute conditions:

attribute_condition = "assertion.repository_id == 'austinss-blog-app' && assertion.ref in ['refs/heads/develop', 'refs/heads/prod']"

This prevents any developer or compromised fork from accessing deployment privileges outside sanctioned release branches.