Secretless Authentication: Trust the Workload, Not a Copied Password
Configure the trust chain for Azure Pipelines service connections and GitHub OIDC, then verify both federation and Azure authorization.
Replace the copied key with a checked identity claim
A hotel can verify a guestβs identity at the desk and issue a short-lived room card rather than email a permanent master key. Workload identity federation uses a trusted identity assertion to obtain short-lived access without storing a long-lived Azure client secret in the pipeline.
βSecretlessβ describes a particular authentication path. It does not mean the whole application has no secrets, every network is trusted, or the workload has unlimited authorization.
Trace the GitHub-to-Azure trust
The Actions job requests an OIDC token. Microsoft Entra checks its issuer, subject, and audience against a configured federated credential. If they match, the workload can obtain an Azure access token for the configured identity. Azure then evaluates that identityβs permissions on the target.
| Layer | What must match |
|---|---|
| GitHub job | Permission to request an OIDC token |
| Federated credential | Trusted issuer, intended repository/ref or environment subject, audience |
| Azure identity | Correct tenant and client ID |
| Azure authorization | Minimal role at the intended resource scope |
| Environment protection | Who may cause a job using that subject to run |
Granting id-token: write allows token request; it does not itself grant Azure resource access.
A read-only login example
This illustrative GitHub job assumes an administrator has already configured the corresponding federated trust and minimal Azure access:
name: Azure identity check
on:
workflow_dispatch:
permissions:
contents: read
id-token: write
jobs:
inspect:
runs-on: ubuntu-latest
environment: learning-sandbox
steps:
- uses: azure/login@v2
with:
client-id: ${{ vars.AZURE_CLIENT_ID }}
tenant-id: ${{ vars.AZURE_TENANT_ID }}
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
- run: az account show --query "{subscription:name,tenant:tenantId}" -o table
The IDs are identifiers, not a client secret. Configure their variable scope deliberately. Pin reviewed action commit SHAs where your policy requires immutable references; the version tag here is a readable teaching reference.
The environment subject must match the federated credentialβs actual subject. A credential for a branch subject is not interchangeable with one for an environment. Inspect the providerβs documented claims and error messages rather than widening trust to every repository.
The account query verifies the selected context. It does not test a write operation, and no deployment occurs in this example.
Azure Pipelines service connections
An Azure Resource Manager service connection packages the target identity and scope for supported pipeline tasks. Use workload identity federation when supported by the selected task and scenario.
Configure the connection for the intended subscription/resource scope, verify the identityβs role, and authorize only necessary pipelines. βGrant access permission to all pipelinesβ is a broad choice, not a harmless convenience.
Connection creation success does not prove a deployment task can reach its target. Check task compatibility with the chosen authentication method, effective Azure permission, network access, and any resource checks/approvals.
An Azure DevOps PAT is a different mechanism, often used for specific Azure DevOps API operations or legacy integration. Scope it narrowly, set a suitable expiry, store it securely, and plan rotation. It is not a substitute for the Azure RBAC grant behind an ARM deployment.
Troubleshoot one boundary at a time
| Symptom | Likely boundary to inspect |
|---|---|
| Cannot obtain GitHub OIDC token | Job permission, event context, platform policy |
| Federation rejected | Issuer, subject, audience, tenant/client ID |
| Azure login succeeds, operation denied | Target scope, role, data-plane rights |
| Service connection cannot be used | Pipeline authorization and resource checks |
| Works locally only | Different identity, cached context, network path |
Never paste the assertion or access token into a public issue or assistant prompt. Use safe diagnostic identifiers and redacted errors.
An AI assistant may draft the configuration, but require it to state the exact trust subject and scope. Reject a proposal that fixes a mismatch by introducing a permanent broad client secret without a justified requirement.
A GitHub job has id-token: write. What additional configuration is needed to access an Azure resource?
Try it: inspect trust without changing auth state
Prerequisites: read access to an existing sandbox federation/service connection, or use a paper design. Creating trust and role assignments requires appropriate administrative authorization.
Write the intended issuer, subject, audience, identity, and target role. Compare them with an actual sandbox configuration if accessible. Trace how an environment or branch change would alter the subject. If separately authorized to run the example, inspect only the resulting account context.
Expected observation: you can explain each link and identify why a successful login can coexist with a denied resource operation.
Cleanup: a read-only inspection has none. Remove only exercise-specific trust, identity grants, and service connections you explicitly created; do not clear a userβs existing CLI login cache.
Decision check: why not one federation for every branch?
Trust should match the workloads allowed to act. Broad subjects or loosely protected environments can let unintended changes obtain privileged tokens.