Use (User)-managed identity with Azure Identity library to connect to Azure DevOps

Overview

With the recently introduced service principal & managed identity in Azure DevOps by Microsoft it is now possible to replace the less secure Personal Access Token (PAT) to connect to Azure DevOps resources. These resources can be accessed via the Azure DevOps API such as Work Items, Pipelines and Repositories and is used by many automated processes. An example could be to use an Azure Function app. In this article I want to show how you can connect to Azure DevOps with a user managed identity with the use of Azure Identity library. This library is recommended if you intend or if you are working with C# codebase. The user managed identity is an Azure resources which can be easily managed in Azure.

I will use the managed identity to access a Git repository in a project as part of my Azure DevOps organization.

Prerequisites

  • Azure AD-backed Azure DevOps organization
  • Azure account
  • Visual Studio or similar IDE

Create a new managed identity in Azure

Go to the Azure portal by following this link. This is where you can create a user-managed identity (for free).

Add user managed identity to Azure DevOps organization

Navigate to your Azure DevOps organization: https://dev.azure.com/<your organization name here>/_settings/users and add the newly created managed identity as a user in Azure DevOps. The managed identity can be easily recognized because of the Azure icon:

Select the desired project(s) where the identity should have access to.

Access Azure DevOps repository with an Azure AD token

Use the code below in a C# Console application or Function app. The code makes use of the Azure Identity library which supports Azure AD authentication across the Azure SDK.

💡 For local testing on a Windows machine on Visual Studio you can login with your Azure credentials via Tools -> Options. Expand Azure Service Authentication -> Account Selection. On a Mac you can login first via the terminal in Visual Studio with az login.

 1using System.Net.Http.Headers;
 2using System.Text.Json.Nodes;
 3using Azure.Identity;
 4
 5// Azure DevOps App Scope
 6// This is the Azure DevOps resource id which is the same for all organizations
 7var adoAppScope = "499b84ac-1321-427f-aa17-267ca6975798/.default";
 8
 9// Get Azure AD token through DefaultAzureCredential as part of Azure Identity library
10// Note that the managed identity credential is disabled for local testing
11#if DEBUG
12var credential = new DefaultAzureCredential(
13    new DefaultAzureCredentialOptions { ExcludeManagedIdentityCredential = true });
14#else
15// When deployed to an azure host, the default azure credential will authenticate the specified user assigned managed identity.
16string userAssignedClientId = "<your managed identity client Id>";
17var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = userAssignedClientId });
18#endif
19
20var token = credential.GetToken(
21    new Azure.Core.TokenRequestContext(
22        new[] { adoAppScope }));
23
24var accessToken = token.Token;
25
26// Setup HTTP client
27var httpClient = new HttpClient
28{
29    BaseAddress =
30    new Uri("https://dev.azure.com")
31};
32
33// Setup HTTP client authorization header
34httpClient.DefaultRequestHeaders.Authorization =
35    new AuthenticationHeaderValue("Bearer", accessToken);
36
37// Retrieve a repository through Azure DevOps Repository API
38HttpResponseMessage response = await httpClient.GetAsync("/<azure devops organisation name>/<project name>/_apis/git/repositories/mi-sample-repo?api-version=4.1");
39var content = response.Content.ReadAsStringAsync();
40
41// Get value from a JsonNode
42JsonNode obj = JsonNode.Parse(content.Result);
43var id = obj["id"];
44
45Console.WriteLine(id);

Deploying and using the managed identity in Azure cloud

To make the previous code work in the cloud you can deploy to Azure. This can be done by setting up a basic Function app. An important part is setting an environment variable called AZURE_CLIENT_ID. As the name suggests it is the application(client) id used by Azure to identify which service principal or managed identity should be used to authenticate with. Add this to the app settings of the Function app.