Pipeline Identity: Service Principals, Managed IDs & OIDC
Authenticate pipelines with Microsoft Entra service principals, managed identities, and workload identity federation. Configure GitHub Apps, GITHUB_TOKEN, and Azure DevOps service connections.
Why pipeline identity matters
Think of a hotel key card system.
When you check in, the front desk gives you a key card. That card only opens YOUR room, the pool, and the gym β not every room in the hotel. If you lose it, they deactivate it instantly and give you a new one.
Pipeline identity works the same way. Instead of giving your pipeline a master key (your personal password), you give it its own identity β a key card that only opens the rooms it needs, and that can be revoked without affecting anyone else.
Microsoft Entra service principals
A service principal is an identity created in Microsoft Entra ID (formerly Azure AD) for applications, services, and automation tools. When you register an app in Entra ID, a service principal object is created in the tenant.
How pipelines use service principals
- Client secret β a password string stored as a pipeline secret. Simple but risky: secrets expire (max 2 years), can leak, and must be rotated manually.
- Certificate β a certificate uploaded to the app registration. More secure than secrets, but certificates still need rotation and secure storage.
- Federated credential (OIDC) β no secret at all. The pipeline presents a token from its own identity provider, and Entra ID trusts it via federation. This is the modern approach.
Service principal lifecycle
- Created via Azure Portal, Azure CLI (
az ad sp create-for-rbac), or Terraform - Assigned RBAC roles at the appropriate scope (subscription, resource group, or resource)
- Credentials must be rotated before expiry
- Should follow least-privilege: scope to specific resource groups, not entire subscriptions
Managed identities
Managed identities eliminate credential management entirely for Azure-hosted workloads. Azure handles the token issuance and rotation behind the scenes.
| Feature | System-Assigned | User-Assigned |
|---|---|---|
| Created with | The Azure resource itself | Standalone resource you create separately |
| Lifecycle | Tied to the resource β deleted when resource is deleted | Independent β survives resource deletion |
| Shared across resources | No β one identity per resource | Yes β assign to multiple resources |
| Use case | Single VM or App Service needs Azure access | Multiple VMs sharing the same identity and permissions |
| Management overhead | Lower (auto-created, auto-deleted) | Slightly higher (you manage lifecycle) |
| RBAC assignment | Assign roles to the resource identity | Assign roles to the identity, then attach identity to resources |
When to use managed identities
- Self-hosted agents on Azure VMs β assign a managed identity to the VM, then the pipeline authenticates without secrets
- Azure Container Instances / AKS pods β use workload identity (user-assigned managed identity federated with Kubernetes service accounts)
- Azure Functions / App Service β system-assigned identity for accessing Key Vault, Storage, SQL
Key limitation: Managed identities only work for Azure-hosted compute. If your pipeline runs on GitHub-hosted runners or on-premises agents, you cannot use managed identities directly β you need service principals or workload identity federation.
Workload identity federation (OIDC)
Workload identity federation is the recommended approach for pipeline authentication to Azure. It eliminates stored secrets entirely.
How it works
- The pipelineβs identity provider (GitHub, Azure DevOps) issues a short-lived OIDC token describing the workflow run
- The pipeline presents this token to Microsoft Entra ID
- Entra ID validates the token against a pre-configured trust (federated credential on the app registration)
- If valid, Entra ID issues an Azure access token β no client secret involved
Setting it up
GitHub Actions to Azure:
- Create an app registration with a federated credential
- The federated credential specifies: issuer (GitHub), subject (repo + branch/environment/tag), and audience
- In the workflow, use
azure/login@v2withclient-id,tenant-id, andsubscription-id(no secret)
Azure Pipelines to Azure:
- Create a service connection of type βWorkload identity federation (automatic)β in Azure DevOps
- Azure DevOps handles the Entra ID app registration and federated credential automatically
- The service connection uses OIDC β no secret stored
Scenario: Amira eliminates secrets for Major Collins
ποΈ Dr. Amira Hassan is auditing Major Collinsβ defence project. The Azure Pipelines service connections use client secrets expiring in 30 days, and no one has rotated them in 18 months.
Amiraβs remediation:
- Creates new service connections using βWorkload identity federation (automatic)β β Azure DevOps generates the Entra app and federated credential
- Updates all pipeline YAML to reference the new service connections
- Removes the old service connections and their expired secrets
- Adds a policy requiring all new service connections to use workload identity federation
Result: Zero secrets to rotate, zero risk of credential leakage. The OIDC tokens are short-lived (valid for minutes, not months).
GitHub authentication mechanisms
| Feature | GITHUB_TOKEN | GitHub App | Personal Access Token (PAT) |
|---|---|---|---|
| Scope | Single repository (the one running the workflow) | Configurable per-repo or per-org installation | All repos the user can access |
| Lifetime | Expires when the workflow job ends | Installation token: 1 hour. App lives until removed | Up to 1 year (fine-grained) or no expiry (classic) |
| Permissions | Default read for contents, write for the same repo | Granular per-permission (issues, PRs, contents, etc.) | Scope-based (classic) or granular (fine-grained) |
| Identity | github-actions bot | The GitHub App (its own identity) | The user who created it |
| Triggers other workflows | No (prevents recursive loops) | Yes | Yes |
| Rotation | Automatic (per job) | Automatic (per installation token request) | Manual |
| Best for | Standard in-repo operations | Cross-repo automation, org-wide bots | Local development, one-off scripts |
GITHUB_TOKEN
Automatically available in every GitHub Actions workflow as github.token. Default permissions are configurable at the repo or org level. Since November 2023, the default for new repos is read-only β you must explicitly grant write permissions in the workflow YAML:
permissions:
contents: read
pull-requests: write
issues: write
GitHub Apps
A GitHub App is a first-class integration identity. In CI/CD, Apps are preferred when you need:
- Cross-repository operations (e.g., a workflow in repo A creating issues in repo B)
- A distinct identity for audit (not βgithub-actionsβ and not a personal user)
- Fine-grained permissions without tying to a human account
Personal access tokens (PATs)
Fine-grained PATs (recommended) β scoped to specific repos and permissions, with mandatory expiry. Classic PATs β broad scope-based tokens. Avoid for pipelines; they grant access to all repos the user can see.
Azure DevOps service connections
Service connections are the Azure DevOps equivalent of configuring external service credentials. They are defined at the project level and can be shared across pipelines.
Service connection types for Azure
| Connection Type | Authentication | Secret Management |
|---|---|---|
| Workload identity federation (automatic) | OIDC β Azure DevOps manages the Entra app | No secrets (recommended) |
| Workload identity federation (manual) | OIDC β you manage the Entra app and federated credential | No secrets |
| Service principal (automatic) | Client secret β Azure DevOps creates the Entra app | Secret stored, auto-rotated by Azure DevOps |
| Service principal (manual) | Client secret or certificate β you manage everything | Secret stored in service connection |
| Managed identity | Managed identity on self-hosted agent VM | No secrets (Azure-hosted agents only) |
Service connection security
- Pipeline permissions β restrict which pipelines can use a service connection
- Approvals and checks β require approval before a pipeline can use the connection (critical for production)
- Branch restrictions β limit service connection usage to specific branches (e.g., only
maincan deploy to production)
Azure DevOps PATs
Azure DevOps PATs authenticate REST API calls and Git operations. Key practices:
- Scope to specific organisations and projects
- Set the shortest practical expiry
- Use managed identities or service principals for automation where possible
- PATs inherit the userβs permissions β if the user is a Project Admin, the PAT is too
Exam tip: Identity decision tree
The exam may present a scenario and ask you to choose the right identity mechanism. Use this decision tree:
- Is the pipeline hosted on Azure compute? (self-hosted agent on VM, AKS, etc.)
- Yes, and single resource β System-assigned managed identity
- Yes, and shared across resources β User-assigned managed identity
- Is the pipeline on GitHub Actions or Azure DevOps (hosted runners)?
- Use workload identity federation (OIDC) β secretless, no rotation needed
- Do you need cross-repo operations in GitHub?
- Use a GitHub App (not GITHUB_TOKEN, not PAT)
- Is a human running a one-off script?
- Use a fine-grained PAT with minimal scope and short expiry
- Fallback: Service principal with client secret β only when OIDC and managed identities are not possible
Knowledge check
Kai's startup runs all CI/CD on GitHub-hosted runners. He needs pipelines to deploy to Azure without storing any secrets. What should he configure?
Nadia needs a GitHub Actions workflow in the 'deploy-tools' repo to create issues in five other repositories when a deployment fails. GITHUB_TOKEN is insufficient. What should she use?
Dr. Amira's client has self-hosted Azure DevOps agents running on Azure VMs. Each agent deploys to different Azure subscriptions. What identity approach minimises operational overhead?
π¬ Video coming soon
Pipeline Identity: Service Principals, Managed IDs & OIDC
Next up: Authorization and Access: GitHub Roles and Azure DevOps Security