|
| 1 | +# Azure Workload Identity Federation |
| 2 | + |
| 3 | +Git Credential Manager supports [Workload Identity Federation][wif] for |
| 4 | +authentication with Azure Repos. This document provides an overview of Workload |
| 5 | +Identity Federation and how to use it with GCM. |
| 6 | + |
| 7 | +## Overview |
| 8 | + |
| 9 | +Workload Identity Federation allows a workload (such as a CI/CD pipeline, VM, or |
| 10 | +container) to exchange a token from an external identity provider for a Microsoft |
| 11 | +Entra ID access token — without needing to manage secrets like client secrets or |
| 12 | +certificates. |
| 13 | + |
| 14 | +This is especially useful in scenarios where: |
| 15 | + |
| 16 | +- You want to avoid storing long-lived secrets. |
| 17 | +- Your workload already has an identity token from another provider (e.g., GitHub |
| 18 | + Actions OIDC, a Managed Identity, or a custom identity provider). |
| 19 | +- You want to follow the principle of least privilege with short-lived, |
| 20 | + automatically rotated credentials. |
| 21 | + |
| 22 | +You can read more about Workload Identity Federation in the |
| 23 | +[Microsoft Entra documentation][wif]. |
| 24 | + |
| 25 | +## How it works |
| 26 | + |
| 27 | +When configured, GCM obtains a client assertion (a token from the external |
| 28 | +identity provider) and exchanges it with Microsoft Entra ID for an access token |
| 29 | +scoped to Azure DevOps. The exact mechanism for obtaining the client assertion |
| 30 | +depends on the federation scenario you choose. |
| 31 | + |
| 32 | +## Scenarios |
| 33 | + |
| 34 | +GCM supports three federation scenarios: |
| 35 | + |
| 36 | +### Generic |
| 37 | + |
| 38 | +Use this scenario when you have a pre-obtained client assertion token from any |
| 39 | +external identity provider. You provide the assertion directly and GCM exchanges |
| 40 | +it for an access token. |
| 41 | + |
| 42 | +**Required settings:** |
| 43 | + |
| 44 | +Setting|Git Configuration|Environment Variable |
| 45 | +-|-|- |
| 46 | +Scenario|[`credential.azreposWorkloadFederation`][gcm-wif-config]|[`GCM_AZREPOS_WIF`][gcm-wif-env] |
| 47 | +Client ID|[`credential.azreposWorkloadFederationClientId`][gcm-wif-clientid-config]|[`GCM_AZREPOS_WIF_CLIENTID`][gcm-wif-clientid-env] |
| 48 | +Tenant ID|[`credential.azreposWorkloadFederationTenantId`][gcm-wif-tenantid-config]|[`GCM_AZREPOS_WIF_TENANTID`][gcm-wif-tenantid-env] |
| 49 | +Assertion|[`credential.azreposWorkloadFederationAssertion`][gcm-wif-assertion-config]|[`GCM_AZREPOS_WIF_ASSERTION`][gcm-wif-assertion-env] |
| 50 | + |
| 51 | +**Optional settings:** |
| 52 | + |
| 53 | +Setting|Git Configuration|Environment Variable |
| 54 | +-|-|- |
| 55 | +Audience|[`credential.azreposWorkloadFederationAudience`][gcm-wif-audience-config]|[`GCM_AZREPOS_WIF_AUDIENCE`][gcm-wif-audience-env] |
| 56 | + |
| 57 | +#### Example |
| 58 | + |
| 59 | +```shell |
| 60 | +git config --global credential.azreposWorkloadFederation generic |
| 61 | +git config --global credential.azreposWorkloadFederationClientId "11111111-1111-1111-1111-111111111111" |
| 62 | +git config --global credential.azreposWorkloadFederationTenantId "22222222-2222-2222-2222-222222222222" |
| 63 | +git config --global credential.azreposWorkloadFederationAssertion "eyJhbGci..." |
| 64 | +``` |
| 65 | + |
| 66 | +### Managed Identity |
| 67 | + |
| 68 | +Use this scenario when your workload runs on an Azure resource that has a |
| 69 | +[Managed Identity][az-mi] assigned. GCM will first request a token from the |
| 70 | +Managed Identity for the configured audience, then exchange that token for an |
| 71 | +Azure DevOps access token. |
| 72 | + |
| 73 | +This is useful for Azure VMs, App Services, or other Azure resources that have a |
| 74 | +Managed Identity but need to authenticate as a specific app registration with |
| 75 | +a federated credential trust. |
| 76 | + |
| 77 | +**Required settings:** |
| 78 | + |
| 79 | +Setting|Git Configuration|Environment Variable |
| 80 | +-|-|- |
| 81 | +Scenario|[`credential.azreposWorkloadFederation`][gcm-wif-config]|[`GCM_AZREPOS_WIF`][gcm-wif-env] |
| 82 | +Client ID|[`credential.azreposWorkloadFederationClientId`][gcm-wif-clientid-config]|[`GCM_AZREPOS_WIF_CLIENTID`][gcm-wif-clientid-env] |
| 83 | +Tenant ID|[`credential.azreposWorkloadFederationTenantId`][gcm-wif-tenantid-config]|[`GCM_AZREPOS_WIF_TENANTID`][gcm-wif-tenantid-env] |
| 84 | +Managed Identity|[`credential.azreposWorkloadFederationManagedIdentity`][gcm-wif-mi-config]|[`GCM_AZREPOS_WIF_MANAGEDIDENTITY`][gcm-wif-mi-env] |
| 85 | + |
| 86 | +**Optional settings:** |
| 87 | + |
| 88 | +Setting|Git Configuration|Environment Variable |
| 89 | +-|-|- |
| 90 | +Audience|[`credential.azreposWorkloadFederationAudience`][gcm-wif-audience-config]|[`GCM_AZREPOS_WIF_AUDIENCE`][gcm-wif-audience-env] |
| 91 | + |
| 92 | +The Managed Identity value accepts the same formats as |
| 93 | +[`credential.azreposManagedIdentity`][gcm-mi-config]: |
| 94 | + |
| 95 | +Value|Description |
| 96 | +-|- |
| 97 | +`system`|System-Assigned Managed Identity |
| 98 | +`[guid]`|User-Assigned Managed Identity with the specified client ID |
| 99 | +`id://[guid]`|User-Assigned Managed Identity with the specified client ID |
| 100 | +`resource://[guid]`|User-Assigned Managed Identity for the associated resource |
| 101 | + |
| 102 | +#### Example |
| 103 | + |
| 104 | +```shell |
| 105 | +git config --global credential.azreposWorkloadFederation managedidentity |
| 106 | +git config --global credential.azreposWorkloadFederationClientId "11111111-1111-1111-1111-111111111111" |
| 107 | +git config --global credential.azreposWorkloadFederationTenantId "22222222-2222-2222-2222-222222222222" |
| 108 | +git config --global credential.azreposWorkloadFederationManagedIdentity system |
| 109 | +``` |
| 110 | + |
| 111 | +### GitHub Actions |
| 112 | + |
| 113 | +Use this scenario when your workload runs in a GitHub Actions workflow. GCM will |
| 114 | +automatically obtain an OIDC token from the GitHub Actions runtime and exchange |
| 115 | +it for an Azure DevOps access token. |
| 116 | + |
| 117 | +This scenario uses the `ACTIONS_ID_TOKEN_REQUEST_URL` and |
| 118 | +`ACTIONS_ID_TOKEN_REQUEST_TOKEN` environment variables that GitHub Actions |
| 119 | +automatically provides when a workflow has the `id-token: write` permission. |
| 120 | + |
| 121 | +**Required settings:** |
| 122 | + |
| 123 | +Setting|Git Configuration|Environment Variable |
| 124 | +-|-|- |
| 125 | +Scenario|[`credential.azreposWorkloadFederation`][gcm-wif-config]|[`GCM_AZREPOS_WIF`][gcm-wif-env] |
| 126 | +Client ID|[`credential.azreposWorkloadFederationClientId`][gcm-wif-clientid-config]|[`GCM_AZREPOS_WIF_CLIENTID`][gcm-wif-clientid-env] |
| 127 | +Tenant ID|[`credential.azreposWorkloadFederationTenantId`][gcm-wif-tenantid-config]|[`GCM_AZREPOS_WIF_TENANTID`][gcm-wif-tenantid-env] |
| 128 | + |
| 129 | +**Optional settings:** |
| 130 | + |
| 131 | +Setting|Git Configuration|Environment Variable |
| 132 | +-|-|- |
| 133 | +Audience|[`credential.azreposWorkloadFederationAudience`][gcm-wif-audience-config]|[`GCM_AZREPOS_WIF_AUDIENCE`][gcm-wif-audience-env] |
| 134 | + |
| 135 | +No additional GCM settings are required — the GitHub Actions OIDC environment |
| 136 | +variables are read automatically. |
| 137 | + |
| 138 | +#### Prerequisites |
| 139 | + |
| 140 | +1. An app registration in Microsoft Entra ID with a federated credential |
| 141 | + configured to trust your GitHub repository. |
| 142 | +2. The app registration must have the necessary permissions to access Azure |
| 143 | + DevOps. |
| 144 | +3. Your GitHub Actions workflow must have the `id-token: write` permission. |
| 145 | + |
| 146 | +#### Example workflow |
| 147 | + |
| 148 | +```yaml |
| 149 | +permissions: |
| 150 | + id-token: write |
| 151 | + contents: read |
| 152 | + |
| 153 | +steps: |
| 154 | + - uses: actions/checkout@v4 |
| 155 | + env: |
| 156 | + GCM_AZREPOS_WIF: githubactions |
| 157 | + GCM_AZREPOS_WIF_CLIENTID: "11111111-1111-1111-1111-111111111111" |
| 158 | + GCM_AZREPOS_WIF_TENANTID: "22222222-2222-2222-2222-222222222222" |
| 159 | +``` |
| 160 | +
|
| 161 | +## Audience |
| 162 | +
|
| 163 | +All scenarios accept an optional audience setting that controls the audience |
| 164 | +claim in the federated token request. The default value is |
| 165 | +`api://AzureADTokenExchange`, which is the standard audience for Microsoft Entra |
| 166 | +ID workload identity federation. |
| 167 | + |
| 168 | +You only need to change this if your federated credential trust is configured |
| 169 | +with a custom audience. |
| 170 | + |
| 171 | +[az-mi]: https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview |
| 172 | +[wif]: https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation |
| 173 | +[gcm-mi-config]: https://gh.io/gcm/config#credentialazreposmanagedidentity |
| 174 | +[gcm-wif-config]: https://gh.io/gcm/config#credentialazreposworkloadfederation |
| 175 | +[gcm-wif-clientid-config]: https://gh.io/gcm/config#credentialazreposworkloadfederationclientid |
| 176 | +[gcm-wif-tenantid-config]: https://gh.io/gcm/config#credentialazreposworkloadfederationtenantid |
| 177 | +[gcm-wif-audience-config]: https://gh.io/gcm/config#credentialazreposworkloadfederationaudience |
| 178 | +[gcm-wif-assertion-config]: https://gh.io/gcm/config#credentialazreposworkloadfederationassertion |
| 179 | +[gcm-wif-mi-config]: https://gh.io/gcm/config#credentialazreposworkloadfederationmanagedidentity |
| 180 | +[gcm-wif-env]: https://gh.io/gcm/env#GCM_AZREPOS_WIF |
| 181 | +[gcm-wif-clientid-env]: https://gh.io/gcm/env#GCM_AZREPOS_WIF_CLIENTID |
| 182 | +[gcm-wif-tenantid-env]: https://gh.io/gcm/env#GCM_AZREPOS_WIF_TENANTID |
| 183 | +[gcm-wif-audience-env]: https://gh.io/gcm/env#GCM_AZREPOS_WIF_AUDIENCE |
| 184 | +[gcm-wif-assertion-env]: https://gh.io/gcm/env#GCM_AZREPOS_WIF_ASSERTION |
| 185 | +[gcm-wif-mi-env]: https://gh.io/gcm/env#GCM_AZREPOS_WIF_MANAGEDIDENTITY |
0 commit comments