1
votes

We need run apps accessing Azure Service Bus (ASB) from Azure App Services and Azure Functions. We need to auth using user assigned identities. We write following code that works with system-assigned identities, but not user-assigned identities:

var tokenProvider = TokenProvider.CreateManagedIdentityTokenProvider();
var managementClient = new ManagementClient(serviceBusEndpoint, tokenProvider);

if(await managementClient.QueueExistsAsync(queueName))
{
    return new OkObjectResult($"Queue with name {queueName} exists.");
}
else
{
    return new OkObjectResult($"Queue with name {queueName} doesn't exist.");
}

This error are thrown:

Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProviderException: Parameters: Connection String: [No connection string specified], Resource: https://servicebus.azure.net/, 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://servicebus.azure.net/, Authority: . Exception Message: Tried to get token using Managed Service Identity. Access token could not be acquired. MSI ResponseCode: BadRequest, Response: {"StatusCode":400,"Message":"No MSI found for specified ClientId/ResourceId.","CorrelationId":"dd2bcf6c-6f1d-489e-b178-ca6007502841"}
Parameters: Connection String: [No connection string specified], Resource: https://servicebus.azure.net/, Authority: . Exception Message: Tried to get token using Visual Studio. Access token could not be acquired. Visual Studio Token provider file not found at "D:\local\LocalAppData\.IdentityService\AzureServiceAuth\tokenprovider.json"
Parameters: Connection String: [No connection string specified], Resource: https://servicebus.azure.net/, Authority: . Exception Message: Tried to get token using Azure CLI. Access token could not be acquired. 'az' is not recognized as an internal or external command,
operable program or batch file.


   at Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider.GetAccessTokenAsyncImpl(String authority, String resource, String scope)
   at Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider.GetAccessTokenAsync(String resource, String tenantId)
   at Microsoft.Azure.ServiceBus.Primitives.ManagedIdentityTokenProvider.GetTokenAsync(String appliesTo, TimeSpan timeout)
   at Microsoft.Azure.ServiceBus.Management.ManagementClient.GetToken(String requestUri)
   at Microsoft.Azure.ServiceBus.Management.ManagementClient.SendHttpRequest(HttpRequestMessage request, CancellationToken cancellationToken)
   at Microsoft.Azure.ServiceBus.Management.ManagementClient.GetEntity(String path, String query, Boolean enrich, CancellationToken cancellationToken)
   at Microsoft.Azure.ServiceBus.Management.ManagementClient.GetQueueAsync(String queuePath, CancellationToken cancellationToken)
   at Microsoft.Azure.ServiceBus.Management.ManagementClient.QueueExistsAsync(String queuePath, CancellationToken cancellationToken)

So core error is No MSI found for specified ClientId/ResourceId.. Looks like we need specify client id. Then we found https://github.com/Azure/azure-sdk-for-net/tree/master/sdk/identity/Azure.Identity with ManagedIdentityCredential where we can specify client id, but haven't figured yet out how to use it for ASB.

Other option is using HTTP GET request to MSI_ENDPOINT as described here https://docs.microsoft.com/en-us/azure/app-service/overview-managed-identity?context=azure%2Factive-directory%2Fmanaged-identities-azure-resources%2Fcontext%2Fmsi-context&tabs=dotnet#obtaining-tokens-for-azure-resources, but may be real working .net library exists that we missed.

3

3 Answers

2
votes

TokenProvider.CreateManagedIdentityTokenProvider takes a dependency on Microsoft.Azure.Services.AppAuthentication for implementing Managed Identity.

User-assigned Managed Identity is supported from version 1.2.1 of Microsoft.Azure.Services.AppAuthentication. Documentation can be found here.

So, you have to do two things to make this work with the code you already have:

1.Update the version of Microsoft.Azure.Services.AppAuthentication to the latest

2.Set AzureServicesAuthConnectionString in the App settings of the AppService to RunAs=App;AppId={ClientId of user-assigned identity}. e.g. RunAs=App;AppId=587f16c8-81ed-41c7-b19a-9ded0dbe2ca2

Once you do these two steps, your code should be using user-assigned managed identity. I tried this out with an App Service it worked fine for me.

3
votes

I'm on the Azure SDK team. We are in the process of unifying all Azure SDKs here: https://aka.ms/azsdkpackages, but do not have a new Service Bus SDK yet.

I haven't attempted user-assigned Identities with the TokenProvider API, but I do know that it works with the new DefaultAzureCredential object, which will search for creds in your environment, and automatically picks up the Managed Identity endpoints. You can read more about it here: https://docs.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet

You can't however use DefaultAzureCredential directly because ServiceBus accepts ITokenProvider.

In the meantime, you can use this adapter I created - which is just a stopgap until we have the Service Bus SDK.

  1. Add the Azure.Identity and Azure.Core nuget packages to your project.

  2. Copy this file to your project: https://github.com/jongio/azidext/blob/master/net/JonGallant.Azure.Identity.Extensions/DefaultAzureServiceBusCredential.cs

  3. Here's a sample usage https://github.com/jongio/azidext/blob/master/net/JonGallant.Azure.Identity.Extensions.Tests/ServiceBus/ServiceBusTests.cs

You can set the client id, secret, and tenant id with the following Environment variables:

AZURE_CLIENT_ID

AZURE_CLIENT_SECRET

AZURE_TENANT_ID

If you have to use TokenProvider and don't want to use the stopgap, then please let me know and I can research it further.

1
votes

As of today we can get user assigned working with following code.

var managedCredential = new ManagedIdentityCredential(userAssignedIdentityAppId);
var accessToken = await _managedCredential.GetTokenAsync(
                        new TokenRequestContext(
                            new[] { "https://servicebus.azure.net" })).ConfigureAwait(false);

It works with "https://servicebus.azure.net/.default" as well.