Implementing Federated Identity when running in Azure DevOps should be added to the CLI so we can start supporting deployment of apps without adding certificates to CI/CD.
Implementation
I've discovered that an environment variable called SYSTEM_OIDCREQUESTURI is available in Azure DevOps. Based on that I found this documentation.
Some researching and try-outs resulted in following specs.
We can start supporting m365 login --authType federatedIdentity within Azure DevOps. This can be done as follows.
We can implement two roads at the same time:
- Not using a service connection
- Using a Service Connection
Road 1: Not using a Service Connection
It's possible to not use a service connection. When not using a service connection you need to specify the following yaml in your build pipeline:
- task: PowerShell@2
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
inputs:
targetType: 'inline'
script: |
m365 login --authType federatedIdentity --appId "<some-appid-or-variable>" --tenant "<some-tenantid-or-variable>"
How it works:
We're not running in a service connection, so we need to input the appId and tenantId.
We also need to add the env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) section, to make sure the SYSTEM_ACCESSTOKEN environment variable is available. That token is necessary to call the local OIDC endpoint.
The m365 login sequence will check if SYSTEM_OIDCREQUESTURI and SYSTEM_ACCESSTOKEN are available. It will start signing in. It will request a federation token from the URL <oidcRequestUrl>?api-version=7.1. This will result in a token that can be passed to Entra ID to be swapped for an access token. The appId and tenant values from the command options will be used for retrieving the token.
The specified app will need a federated identity credential with the right Subject and Issuer. These values are normally determined when you create a Service Connection, but in this case you'll need to configure them manually. The subject will look like this: p://contoso/MyProjectName/MyRepoName. The issuer will be https://vstoken.dev.azure.com/<some-devops-organization-id>. The organization id can be determined by exporting the organizations from DevOps:

Road 2: Using a Service Connection
When using a service connection you need to specify the following yaml in your build pipeline:
- task: AzurePowerShell@5
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
inputs:
azureSubscription: 'MyServiceConnection'
ScriptType: 'InlineScript'
azurePowerShellVersion: LatestVersion
Inline: |
m365 login --authType federatedIdentity
How it works:
When using a service connection we need the AzurePowerShell@5 task as a wrapper task so that we can input the service connection. The task will make the following environment variables available:
- AZURESUBSCRIPTION_SERVICE_CONNECTION_ID
- AZURESUBSCRIPTION_CLIENT_ID
- AZURESUBSCRIPTION_TENANT_ID
We also need to add the env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) section, to make sure the SYSTEM_ACCESSTOKEN environment variable is available. That token is necessary to call the local OIDC endpoint.
The m365 login sequence will check if SYSTEM_OIDCREQUESTURI and SYSTEM_ACCESSTOKEN are available. It will start signing in. It will request a federation token from the URL <oidcRequestUrl>?api-version=7.1&serviceConnectionId=<serviceConnectionId. This will result in a token that can be passed to Entra ID to be swapped for an access token. The App AZURESUBSCRIPTION_CLIENT_ID on the tenant AZURESUBSCRIPTION_TENANT_ID will be used for retrieving the token.
The specified app will need a federated identity credential with the right Subject and Issuer. These values are determined when you create the Service Connection. The subject will look like this: sc://contoso/MyProjectName/MyServiceConnectionName.
Earlier specs idea 2: create a devops extension task and add a federatedToken option to the login command
This idea is superseded by the latest idea because we'd rather avoid having to force devs to use an extension task. We'd like to use just the m365 login command if possible. Also it takes another repo to manage.
The point with Azure DevOps is that we need to use service connections. It seems though that service connections are not just available within any task. You need to build a task extension to be able to leverage the azure-pipelines-task-lib to get at the service connection.
Studying the AzureCLIV2 task and how it interacts with the actual az cli, I believe I know how we can tackle this challenge. We can do it in the exact same way as the az cli:
- We'll need to build an Azure DevOps extension (just like we have a GitHub extension) that is able to get at the service connections and can retrieve a federated token. This extension installs the CLI, retrieves the federated token and then calls the login command, giving the federated token as an option.
- We'll add a --federatedToken [federatedToken] option to the login command. The login command calls login.microsoftonline.com and swaps the federated token for an access token.
And that should be it! I believe I have all these steps figured out now. But of course, I'm interested to hear your opinions. I've already created a repository and uploaded some code for the extension.
We'll need to publish the extension to the DevOps marketplace when it's finished.
As a side-thing, the extension could be made in such a way that it can also deal with other types of service connections, like one with a secret, or one with a certificate.
The CLI option should be like:
Option
| Option |
Description |
-t, --federatedToken [federatedToken] |
Federated token that can be used for OIDC token exchange. Can be used together with --authType federatedIdentity when running the CLI in Azure DevOps. |
Earlier specs idea 1: pass the service connection to the CLI
This option is impossible. You need a DevOps extension task to pass a service connection into and load data from the azure devops task library.
Options
We'll allow logging in through the existing option --authType federatedIdentity (see #6610)
But Azure DevOps needs to know what Service Connection will be used, so we'll need an additional option:
| Option |
Description |
-c, --serviceConnection [serviceConnection] |
The Azure DevOps service connection to use. Can only used in Azure DevOps when logging in with --authType federatedIdentity |
Implementation
For inspiration, we'll need to check how the az cli does it: check out the source code here
Just like with GitHub it first retrieves an idToken. That will probably get posted to Entra Id to switch it for an access token. The rest will need to be researched yet.
We may need to install azure-pipelines-task-lib. For example for retrieving variables that we need.
Let's check out if we really need it.
Requirements
- If the service connection is not of the Workload Identity Federation type, logging in should fail with a clear exception message.
Implementing Federated Identity when running in Azure DevOps should be added to the CLI so we can start supporting deployment of apps without adding certificates to CI/CD.
Implementation
I've discovered that an environment variable called
SYSTEM_OIDCREQUESTURIis available in Azure DevOps. Based on that I found this documentation.Some researching and try-outs resulted in following specs.
We can start supporting
m365 login --authType federatedIdentitywithin Azure DevOps. This can be done as follows.We can implement two roads at the same time:
Road 1: Not using a Service Connection
It's possible to not use a service connection. When not using a service connection you need to specify the following yaml in your build pipeline:
How it works:
We're not running in a service connection, so we need to input the appId and tenantId.
We also need to add the
env: SYSTEM_ACCESSTOKEN: $(System.AccessToken)section, to make sure theSYSTEM_ACCESSTOKENenvironment variable is available. That token is necessary to call the local OIDC endpoint.The m365 login sequence will check if
SYSTEM_OIDCREQUESTURIandSYSTEM_ACCESSTOKENare available. It will start signing in. It will request a federation token from the URL<oidcRequestUrl>?api-version=7.1. This will result in a token that can be passed to Entra ID to be swapped for an access token. The appId and tenant values from the command options will be used for retrieving the token.The specified app will need a federated identity credential with the right Subject and Issuer. These values are normally determined when you create a Service Connection, but in this case you'll need to configure them manually. The subject will look like this:
p://contoso/MyProjectName/MyRepoName. The issuer will behttps://vstoken.dev.azure.com/<some-devops-organization-id>. The organization id can be determined by exporting the organizations from DevOps:Road 2: Using a Service Connection
When using a service connection you need to specify the following yaml in your build pipeline:
How it works:
When using a service connection we need the
AzurePowerShell@5task as a wrapper task so that we can input the service connection. The task will make the following environment variables available:We also need to add the
env: SYSTEM_ACCESSTOKEN: $(System.AccessToken)section, to make sure theSYSTEM_ACCESSTOKENenvironment variable is available. That token is necessary to call the local OIDC endpoint.The m365 login sequence will check if
SYSTEM_OIDCREQUESTURIandSYSTEM_ACCESSTOKENare available. It will start signing in. It will request a federation token from the URL<oidcRequestUrl>?api-version=7.1&serviceConnectionId=<serviceConnectionId. This will result in a token that can be passed to Entra ID to be swapped for an access token. The App AZURESUBSCRIPTION_CLIENT_ID on the tenant AZURESUBSCRIPTION_TENANT_ID will be used for retrieving the token.The specified app will need a federated identity credential with the right Subject and Issuer. These values are determined when you create the Service Connection. The subject will look like this:
sc://contoso/MyProjectName/MyServiceConnectionName.Earlier specs idea 2: create a devops extension task and add a federatedToken option to the login command
The point with Azure DevOps is that we need to use service connections. It seems though that service connections are not just available within any task. You need to build a task extension to be able to leverage the
azure-pipelines-task-libto get at the service connection.Studying the AzureCLIV2 task and how it interacts with the actual
az cli, I believe I know how we can tackle this challenge. We can do it in the exact same way as the az cli:And that should be it! I believe I have all these steps figured out now. But of course, I'm interested to hear your opinions. I've already created a repository and uploaded some code for the extension.
We'll need to publish the extension to the DevOps marketplace when it's finished.
As a side-thing, the extension could be made in such a way that it can also deal with other types of service connections, like one with a secret, or one with a certificate.
The CLI option should be like:
Option
-t, --federatedToken [federatedToken]--authType federatedIdentitywhen running the CLI in Azure DevOps.Earlier specs idea 1: pass the service connection to the CLI
Options
We'll allow logging in through the existing option
--authType federatedIdentity(see #6610)But Azure DevOps needs to know what Service Connection will be used, so we'll need an additional option:
-c, --serviceConnection [serviceConnection]--authType federatedIdentityImplementation
For inspiration, we'll need to check how the az cli does it: check out the source code here
Just like with GitHub it first retrieves an idToken. That will probably get posted to Entra Id to switch it for an access token. The rest will need to be researched yet.
We may need to install azure-pipelines-task-lib. For example for retrieving variables that we need.
Let's check out if we really need it.
Requirements