GitHub Actions: Deploy a Azure con Workload Identity Federation
Resumen
Olv
ídate de guardar service principal secrets en GitHub. Con Workload Identity Federation, GitHub Actions se autentica a Azure sin credenciales almacenadas. Más seguro y cero rotación de secrets.
¿Qué es Workload Identity Federation?
Permite que GitHub Actions obtenga un access token de Azure usando OIDC (OpenID Connect). Azure confía en los tokens de GitHub basándose en: - Repositorio específico - Branch específico - Environment específico
Setup completo
1. Crear service principal
# Variables
APP_NAME="github-actions-federation"
RG="my-rg"
SUBSCRIPTION_ID=$(az account show --query id -o tsv)
# Crear app registration
APP_ID=$(az ad app create \
--display-name $APP_NAME \
--query appId -o tsv)
# Crear service principal
az ad sp create --id $APP_ID
SP_OBJECT_ID=$(az ad sp show --id $APP_ID --query id -o tsv)
# Asignar rol Contributor
az role assignment create \
--role Contributor \
--assignee $APP_ID \
--scope /subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG
2. Configurar Federated Credential
# Para branch main
az ad app federated-credential create \
--id $APP_ID \
--parameters '{
"name": "github-main-branch",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo:myorg/myrepo:ref:refs/heads/main",
"description": "GitHub Actions - main branch",
"audiences": ["api://AzureADTokenExchange"]
}'
# Para environment production
az ad app federated-credential create \
--id $APP_ID \
--parameters '{
"name": "github-prod-environment",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo:myorg/myrepo:environment:production",
"description": "GitHub Actions - production environment",
"audiences": ["api://AzureADTokenExchange"]
}'
# Para pull requests
az ad app federated-credential create \
--id $APP_ID \
--parameters '{
"name": "github-pull-requests",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo:myorg/myrepo:pull_request",
"description": "GitHub Actions - pull requests",
"audiences": ["api://AzureADTokenExchange"]
}'
3. Configurar GitHub Secrets
En tu repositorio → Settings → Secrets:
4. GitHub Actions Workflow
.github/workflows/deploy.yml:
name: Deploy to Azure
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
id-token: write # Required for OIDC
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
environment: production # Si usas federated credential por environment
steps:
- uses: actions/checkout@v4
- name: Azure Login with OIDC
uses: azure/login@v1
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Deploy to Azure Web App
uses: azure/webapps-deploy@v2
with:
app-name: my-web-app
package: ./dist
- name: Azure CLI commands
run: |
az group list
az webapp list --resource-group ${{ env.RG }}
Ventajas vs Service Principal Secrets
| Aspecto | Service Principal Secret | Workload Identity |
|---|---|---|
| Secrets en GitHub | Sí (password) | No (solo IDs) |
| Rotación | Manual cada 90 días | Automática |
| Scope | Amplio | Granular (repo/branch/env) |
| Audit trail | Limitado | Completo (OIDC claims) |
| Expiración | Hasta 2 años | Token expira en minutos |
Debugging
- name: Debug OIDC Token
run: |
curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=api://AzureADTokenExchange" \
| jq -R 'split(".") | .[1] | @base64d | fromjson'
Claims del token:
{
"iss": "https://token.actions.githubusercontent.com",
"sub": "repo:myorg/myrepo:ref:refs/heads/main",
"aud": "api://AzureADTokenExchange",
"ref": "refs/heads/main",
"sha": "abc123...",
"repository": "myorg/myrepo",
"repository_owner": "myorg",
"run_id": "123456789",
"workflow": "Deploy to Azure"
}
Buenas prácticas
- Usa environments: Requiere aprobaciones manuales para producción
- Scope mínimo: Asigna roles solo al resource group necesario
- Multiple credentials: Diferentes para dev/staging/prod
- Branch protection: Solo permite deploy desde branches protegidos
- Audit logs: Revisa sign-in logs en Azure AD regularmente