12

I managed to get the below code to work (complete code here) to use Azure managed identity to authenticate (via Visual Studio) and have access to Azure storage account without using credentials.

const string storageResource = "https://storage.azure.com/";

var authResult = await azureServiceTokenProvider.GetAuthenticationResultAsync(storageResource, cancellationToken: cancellationToken);

The code managed to find my user logged in to Visual Studio and uses it to get the token and all goes well.

However, this code is executed as part of a library integration tests in an Azure DevOps build pipeline.

I found the service principal created when I created the service connection to Azure in Azure DevOps and gave it the same Storage Blob Data Contributor role hoping that Azure DevOps would use it to run the code but had no success.

So my question is:

How do I get code that runs in Azure DevOps build pipeline to be able to authenticate using the AzureServiceTokenProvider?

BTW, the error message:

Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProviderException : Parameters: Connection String: [No connection string specified], Resource: https://storage.azure.com/, Authority: . Exception Message: Tried the following 3 methods to get an access token, but none of them worked. Parameters: Connection String: [No connection string specified], Resource: https://storage.azure.com/, Authority: . Exception Message: Tried to get token using Managed Service Identity. Access token could not be acquired. Failed after 5 retries. MSI ResponseCode: BadRequest, Response: {"error":"invalid_request","error_description":"Identity not found"} Parameters: Connection String: [No connection string specified], Resource: https://storage.azure.com/, Authority: . Exception Message: Tried to get token using Visual Studio. Access token could not be acquired. Visual Studio Token provider file not found at "C:\Users\VssAdministrator\AppData\Local.IdentityService\AzureServiceAuth\tokenprovider.json" Parameters: Connection String: [No connection string specified], Resource: https://storage.azure.com/, Authority: . Exception Message: Tried to get token using Azure CLI. Access token could not be acquired. ERROR: Please run 'az login' to setup account.

TearDown : System.NullReferenceException : Object reference not set to an instance of an object.

Fabio Milheiro
  • 8,100
  • 17
  • 57
  • 96

3 Answers3

13

When you create a service connection in Azure DevOps you are presented with (as of writing) 4 options.

  • Service principal (aka app registrations in your Azure Active Directory)
  • Manage identity
  • Publish Profile

New Azure service connection

A publish profile is an Azure App Service specific authentication mechanism that lets you publish via Kudu.

Manage identities are somewhat mislabeled. They are used to allow a VM running your Azure pipelines to act as the managed identity of that VM (you then give this managed identity access to the resources you want it to be able to access in the Azure Portal). This is somewhat backwards from how the other authentication methods work but I guess it makes sense when you are hosting your own Azure pipelines on VMs in Azure. This option has this caveat.

The AzureServiceTokenProvider will only work from within an environment that expose the MSI_ENDPOINT (aka IDENTITY_ENDPOINT). Hosted Azure DevOps pipelines are not such environments.

Service principals is what you will be using in all likelihood.

Service principals come in two flavours automatic and manual. Again with the mislabeling. There's actually nothing automatic with the automatic option the only thing that happens is that it provisions the service principal in your Azure AD for you. It will grant the service principal the contributor role at the subscription level (which means full access to everything in the subscription except access control). You should not grant service principals that kind of access. It is excessive. Remember that you service principals are just protected by credentials, that if they leak, they allow anyone to do irreparable harm.

John Leidegren
  • 59,920
  • 20
  • 131
  • 152
  • Your answer seems to point to no good solution from a security perspective. Is it true that a managed identity with SQL Server access cannot be called by an XUnit project within the DevOps Build Pipeline? – J Weezy Jan 04 '22 at 14:59
  • 1
    @JWeezy you can setup your own VM with a managed identity. Your builds running on this machine will then authenticate as the managed identity. You can then grant the manage identity access to SQL Azure. From a security perspective the problem is moved on to the VM, anyone that can access this VM or queue builds to this VM has essentially access to the same resources. Another option is to create a service principal and put the client secret in a key vault. Then how you manage access to this secret via the key vault is how you protect your identity. – John Leidegren Jan 07 '22 at 08:40
  • Thanks for the explanation. But is the service principal then usable for the purpose; that is, can it be provided to code running in the build (as opposed to steps specifically designed to accept a service connection)? – Jan Hudec Aug 12 '22 at 09:39
  • The way this works is that once you have a connection you use the az CLI tooling to do stuff or get access tokens from within that environment. Those access tokens will then be issued to the service principal and have the same access as the service principal. It would be as if you signed in to Azure portal using the service principal as a login. – John Leidegren Aug 13 '22 at 06:36
1

Since, this question hasn't been answered so far, you can try this: Try passing the connection information expicitly to the azureServiceTokenProvider. Now, the followig codeblock assumes that you're using a shared secret credential to sign into Azure AD but can be extended to any methods described here - Service-to-service authentication to Azure Key Vault using .NET

var azureServicesAuthString = $"RunAs=App;AppId={AppId};TenantId={TenantId};AppKey={ClientSecret}";
tokenProvider = new AzureServiceTokenProvider(connectionString: azureServicesAuthString);
var authResult = await azureServiceTokenProvider.GetAuthenticationResultAsync(storageResource, cancellationToken: cancellationToken);
IronMan
  • 309
  • 4
  • 22
-2

You will need to create a service connection of type "Managed identity authentication" to use managed identity in DevOps pipeline.

Varun Sharma
  • 568
  • 4
  • 5
  • It will only work for VM based agent, not Hosted agent – Thomas Jul 04 '19 at 11:20
  • 1
    Not making much sense yet. After creating a service connection of type `Managed identity authentication`, I don't get any choice other than the connection name. Then I tried to find a managed identity in Azure Portal but found nothing. I'm still missing the point about to make a build machine to be able to authenticate using the token provider. @Thomas suggested hosted agents can't authenticate like this yet but I'm even unsure about having our own VMs to build. – Fabio Milheiro Jul 04 '19 at 21:32
  • Just to make it clear, I know how to create my own build server and have Azure DevOps pick them to build. It's just that creating that "Managed identity authentication" did nothing that I could see. Clearly, I'm still lost but any pointers are welcome. Thanks @Varun and Thomas. – Fabio Milheiro Jul 04 '19 at 21:34
  • This is enabling System assigned managed identity on a VM where your pipeline is running, as much as I understand. That is why you were not able to find it in AAD. You would need to grant acces to a VM itself on storage account. – Igor Apr 13 '20 at 09:49