Improve security posture in Azure service connections with AzurePipelinesCredential

Karishma Ghiya

Recently, the Azure Pipelines team introduced the support for Federated Identity Credentials (FIC) through Service Connections. This feature uses an industry-standard technology, Open ID Connect (OIDC), to simplify the authentication between Azure Pipelines and Azure services. Before this new feature, users needed to store and regularly rotate secrets or certificates. With this feature, not only is authenticating to Azure services easier, but it’s also more secure, as no persistent secret is involved. The tasks running in pipeline jobs can’t leak or exfiltrate secrets that have access to the production environments.

To support FIC in Azure Pipelines (part of Azure DevOps), the Azure Identity libraries for .NET, C++, Go, Java, JavaScript, and Python introduced a new credential called AzurePipelinesCredential.

Before the existence of this credential, Azure Identity library consumers needed to build their own custom credential to support the OIDC token request API callback for Azure Pipelines using ClientAssertionCredential. For example:

function pipelinesServiceConnectionAssertion(
  serviceConnectionId: string
): () => Promise<string> {
  return async () => {
    const oidcRequestUrl = `${process.env.SYSTEM_OIDCTOKENURI}?api-version=7.1&serviceConnectionId=${serviceConnectionId}`;
    const systemAccessToken = `${process.env.SYSTEM_ACCESSTOKEN}`;
    const oidcToken = await requestOidcToken(oidcRequestUrl, systemAccessToken);
    return oidcToken;
  };
}

async function requestOidcToken(
  oidcRequestUrl: string,
  systemAccessToken: string
): Promise<string> {
  // code for sending request using this REST API https://learn.microsoft.com/rest/api/azure/devops/distributedtask/oidctoken/create?view=azure-devops-rest-7.1
  // extract and return "oidcToken" from the response from above request
}
const credential = new ClientAssertionCredential(
  "<tenantId>",
  "<clientId>",
  pipelinesServiceConnectionAssertion("<serviceConnectionId>"),
  options
);

Credential design

Users can now use the AzurePipelinesCredential by setting the following values in its constructor, eliminating the need for a two-step process or a custom callback:

  • clientId: Client ID from your user-assigned managed identity OR Application (client) ID from your app registration.
  • tenantId: Tenant ID from your user-assigned managed identity OR Directory (tenant) ID from your app registration.
  • serviceConnectionId: The service connection ID is the GUID representing your service connection and can be obtained by looking at the browser’s address bar when you navigate to a service connection in Azure Pipelines. It’s the resourceId, as found in the URL’s querystring. resourceId as found in the querystring of the Azure Resource Manager service connection created in Azure Pipelines
  • systemAccessToken: See how to configure the predefined system-level variable $System.AccessToken for the Azure Pipelines task. Pass this field into the credential’s constructor.
/**
 * Authenticate with Azure Pipelines federated identity.
 */
function withAzurePipelinesCredential() {
  const clientId = "<YOUR_CLIENT_ID>";
  const tenantId = "<YOUR_TENANT_ID>";
  const serviceConnectionId = "<YOUR_SERVICE_CONNECTION_ID>";
  const systemAccessToken = "<SYSTEM_ACCESSTOKEN>";
  const credential = new AzurePipelinesCredential(
    tenantId,
    clientId,
    serviceConnectionId,
    systemAccessToken
  );

  const client = new SecretClient(
    "https://key-vault-name.vault.azure.net",
    credential
  );
}

Package versions

The following table provides the minimum stable or beta library versions required to use AzurePipelinesCredential.

Language Library version
.NET 1.12.0
C++ 1.9.0-beta.1
Go 1.7.0
Java 1.13.0
JavaScript 4.3.0
Python 1.17.0

Set up FIC in Azure Pipelines

To use FIC in Azure Pipelines, configure the Azure Resource Manager service connection in one of two ways:

Create a service connection or convert an existing one to use FIC

  • Convert your existing Azure service connections based on secrets to the new scheme. You can perform this conversion one connection at a time. Best of all, you don’t have to modify any of the pipelines that use those service connections. They automatically apply the new scheme once you complete the conversion. Convert your existing Azure service connections
  • Create a new Azure service connection using federated identity by selecting workload identity federation (automatic) in the Azure service connection creation experience. Follow the steps:
    1. In Azure DevOps, go to Project Settings and then Service connections.
    2. Select New service connection.
    3. Select Azure Resource Manager and select Next.
    4. Select Workload identity federation (automatic) and select Next.
    5. Enter a unique value for Service connection name and select Next.

    Create a new Azure service connections

    Note


    To enable every pipeline in your project to use the service connection, check the Grant access permission to all pipelines box.

    You can also create the workload identity federation manually in step 3 above by selecting Workload identity federation (manual) with either a user-assigned managed identity as an FIC or an app registration as an FIC.

User-assigned managed identity as an FIC

  1. First, you need a user-assigned managed identity.
  2. Copy the Subscription ID and Client ID values for your managed identity to use later.
  3. Go to Settings > Properties.
  4. Copy the Tenant Id value to use later.
  5. Go to Settings > Federated credentials.
  6. Select Add credentials.
  7. Select the Other issuer scenario.
  8. Enter values for Issuer and Subject identifier. You’ll replace these values later.
    Field Description
    Issuer Enter https://app.vstoken.visualstudio.com/<unique-identifier>.
    Subject identifier Specify sc://<Azure DevOps organization>/<project name>/<service connection name>. The service connection doesn’t need to be already created.
  9. Select Save.
  10. Keep this window open. Later in the process, you return to the window and update your app registration federated credentials.
  11. Grant permissions to managed identity with the Azure portal:
    1. In the Azure portal, go to the Azure resource that you want to grant permissions for (for example, a resource group).
    2. Select Access control (IAM).
    3. Select Add role assignment. Assign the required role to your managed identity (for example, Contributor).
    4. Select Review and assign.

Read the detailed instructions on setting up a user-assigned managed identity as an FIC service connection.

App registration as an FIC

  1. In the Microsoft Entra ID section of the Azure portal, go to App registrations.
  2. Select New registration.
  3. In the Name textbox, enter a name for your app registration. Then select the appropriate radio button in the Who can use this application or access this API section.
  4. Copy the values for Application (client) ID and Directory (tenant) ID from your app registration to use later.
  5. Go to Manage > Certificates & secrets.
  6. Select Federated credentials.
  7. Select Add credentials.
  8. Select the Other issuer scenario. Enter values for Issuer and Subject identifier. You’ll replace these values later.
    Field Description
    Issuer Enter https://app.vstoken.visualstudio.com/.
    Subject identifier Specify sc://<Azure DevOps organization>/<project name>/<service connection name>. The service connection doesn’t need to be already created.
  9. Select Save.
  10. Keep this window open. Later in the process, you return to the window and update your app registration federated credentials.
  11. Grant permissions to the app registration:
    1. In the Azure portal, go to the Azure resource that you want to grant permissions for (for example, a resource group).
    2. Select Access control (IAM).
    3. Select Add role assignment. Assign the required role to the app registration (for example, Contributor).
    4. Select Review and assign.

Read the detailed instructions on setting up an app registration as an FIC service connection.

Create service connection for either MI as FIC or app registration as FIC

  1. In Azure DevOps, open your project and go to > Pipelines > Service connections.
  2. Select New service connection.
  3. Select Azure Resource Manager, and then select Next.
  4. Select Workload Identity federation (manual), and then select Next. Select Workload Identity federation (manual)
  5. For Service connection name, enter the value that you used for Subject identifier when you created your federated credentials (either MI or App Registration).
  6. For Subscription Id and Subscription Name, enter the values for the subscription in your Azure portal account.
    Subscription Details
  7. In the Authentication section:
    1. For Service Principal Id, enter the value of Client Id from either your managed identity or app registration.
    2. For Tenant ID, enter the value of Tenant Id from your managed identity or app registration. managed-identity-example-values
  8. In Azure DevOps, copy the generated values for Issuer and Subject identifier. federated-values-devops
  9. In the Azure portal, return to your managed identity or app registration federated credentials.
  10. Paste the values for Issuer and Subject identifier that you copied from your Azure DevOps project into your federated credentials in the Azure portal. copy-federated-credential
  11. In the Azure portal, select Update to save the updated credentials.
  12. In Azure DevOps, select Verify and save.

Example of using the Azure Pipelines task

To use the federated identity through service connections feature in Azure Pipelines, use one of the recommended tasks. The following YAML script is an example of running the AzureCLI@2 task for using service connections federated identity with @azure/identity.

In the Azure Pipelines task, make sure to configure the predefined system variable System.AccessToken.

trigger:
  - main

pool:
  vmImage: ubuntu-latest

steps:
  - script: |
      npm install @azure/identity
      npm install @azure/keyvault-secrets
    displayName: "Install the latest version of Azure Identity"

  - task: AzureCLI@2
    displayName: "Azure CLI Task"
    env:
      SYSTEM_ACCESSTOKEN: $(System.AccessToken)
    inputs:
      azureSubscription: "<Name_of_AZURE_SERVICE_CONNECTION>"
      scriptType: bash
      scriptLocation: "inlineScript"
      inlineScript: |
        node <path-to-the-javascript-code>

Troubleshooting

Select one of the following links to see language-specific troubleshooting guidance for AzurePipelinesCredential:

0 comments

Leave a comment

Feedback usabilla icon