Your pipeline needs to deploy to Azure. The naive way: store Azure credentials as a GitHub secret. The correct way: OIDC federated identity — no credentials stored anywhere. This lesson teaches you why and how.
The Problem with Stored Credentials
How OIDC Works (The Flow)
The federated credential is the trust relationship. It says: "Azure, trust JWTs from GitHub Actions that claim to be repo
user/cicd-mastery on branch main." This is scoped — a different repo or branch can't impersonate your pipeline.
🏋️ Hands-On: Set Up Azure OIDC
Step 1: Create an Azure AD App Registration
# Login to Azure
az login
# Create app registration
az ad app create --display-name "github-actions-cicd-mastery"
# Get the App (Client) ID
APP_ID=$(az ad app list --display-name "github-actions-cicd-mastery" \
--query "[0].appId" -o tsv)
# Create service principal
az ad sp create --id $APP_ID
# Get IDs you'll need
SUBSCRIPTION_ID=$(az account show --query id -o tsv)
TENANT_ID=$(az account show --query tenantId -o tsv)
echo "Client ID: $APP_ID"
echo "Tenant ID: $TENANT_ID"
echo "Subscription ID: $SUBSCRIPTION_ID"
Step 2: Add Federated Credentials
# Trust GitHub Actions from your repo's main branch
az ad app federated-credential create --id $APP_ID --parameters '{
"name": "github-main-branch",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo:YOUR_USERNAME/cicd-mastery:ref:refs/heads/main",
"audiences": ["api://AzureADTokenExchange"]
}'
# Trust your "production" environment
az ad app federated-credential create --id $APP_ID --parameters '{
"name": "github-env-production",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo:YOUR_USERNAME/cicd-mastery:environment:production",
"audiences": ["api://AzureADTokenExchange"]
}'
Step 3: Grant Azure Permissions
# Contributor on a resource group (scope it tight!)
az group create --name rg-cicd-mastery --location eastus
RG_ID=$(az group show --name rg-cicd-mastery --query id -o tsv)
az role assignment create \
--assignee $APP_ID \
--role "Contributor" \
--scope $RG_ID
Step 4: Add to GitHub (NOT secrets — just IDs)
Go to repo → Settings → Secrets and Variables → Actions → Variables:
| Name | Value | Type |
|---|---|---|
AZURE_CLIENT_ID | Your App ID | Secret* |
AZURE_TENANT_ID | Your Tenant ID | Secret* |
AZURE_SUBSCRIPTION_ID | Your Subscription ID | Secret* |
*These aren't truly secret (they're in every Azure portal URL), but storing as secrets is conventional and prevents accidental exposure in logs.
Step 5: Use in Workflow
permissions:
id-token: write # REQUIRED for OIDC!
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Azure Login (OIDC)
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Verify access
run: az account show
Scoping: Why Federated Credentials Are Precise
Common Pitfall: Missing permissions
If you forget
permissions: id-token: write, the OIDC token request will silently fail and azure/login will error with "AADSTS700024" or "Failed to get federated token." This is the #1 debugging issue with OIDC.
🧠 Recall Check
- What is stored in GitHub Secrets when using OIDC? (Hint: it's NOT a password)
- What permission must be set at the workflow level for OIDC to work?
- What does the "subject" field in a federated credential control?
- Why is OIDC more secure than a client secret?
Reveal answers
- Only IDs — Client ID, Tenant ID, Subscription ID. These are identifiers (like a username), not passwords. No credential is stored.
permissions: id-token: write— this allows the workflow to request a JWT from GitHub's OIDC provider.- Who can authenticate. E.g.,
repo:user/repo:ref:refs/heads/mainmeans only workflows running on themainbranch of that specific repo can get a token. - No secret exists to leak, expire, or rotate. Tokens are short-lived (~1 hour) and scoped. Even if intercepted, they expire quickly and only work for the specific resource scope granted.
You now have credential-free Azure authentication. Every future deployment lesson will use OIDC. This is the foundation of secure CI/CD — no long-lived secrets, ever. Next: deploying a real application to Azure App Service.