1

I had enabled system assigned managed identity in azure function(Service bus topic trigger) and added the identity(Object (principal) ID ) in key vault access policy with "Get,List" permissions of secrets, keys. I added the reference of the Key Vault into Azure function Application settings and able to receive at runtime after azure function deployment.

@Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/mysecret/ec96f02080254f109c51a1f14cdb1931)

Code:

        private async Task<string> FetchSecretValueFromKeyvault(string secretName)
        {
            _logger.LogInformation($"FetchSecretValueFromKeyvault: SecretName {secretName}");
            string actualSecret = string.Empty;
            try
            {
                string systemAssignedClientId = GetEnvironmentVariable("AzureADManagedIdentityClientId");
                string azureKeyVaultUrl = GetEnvironmentVariable("AzureKeyVaultUrl");
                var defaultAzureCredentialOptions = new DefaultAzureCredentialOptions();
                defaultAzureCredentialOptions.ExcludeAzureCliCredential = true;
                defaultAzureCredentialOptions.ExcludeEnvironmentCredential = true;
                defaultAzureCredentialOptions.ExcludeAzurePowerShellCredential = true;
                defaultAzureCredentialOptions.ExcludeInteractiveBrowserCredential = true;
                defaultAzureCredentialOptions.ExcludeManagedIdentityCredential = false;
                defaultAzureCredentialOptions.ExcludeSharedTokenCacheCredential = true;
                defaultAzureCredentialOptions.ExcludeVisualStudioCodeCredential = true;
                defaultAzureCredentialOptions.ExcludeVisualStudioCredential = true;
                defaultAzureCredentialOptions.ManagedIdentityClientId = systemAssignedClientId;

                var credential = new DefaultAzureCredential(defaultAzureCredentialOptions);

                var client = new SecretClient(new Uri(AzureKeyVaultUrl)), credential);
                var secret = await client.GetSecretAsync(secretName).ConfigureAwait(false);
                actualSecret = secret.Value.Value;
                _logger.LogInformation($"FetchSecretValueFromKeyvault:  Received secretValue for {secretName}");
            }
            catch (RequestFailedException ex)
            {
                actualSecret = string.Empty;
                _logger.LogError($"Message: {ex.Message}. \nInnerException:{ex.InnerException}. \nStackTrace: {ex.StackTrace}. \nInnerExceptionMessage:{ex.InnerException?.Message}.");
            }
            catch (Exception ex)
            {
                actualSecret = string.Empty;
                _logger.LogError($"Message: {ex.Message}. \nInnerException:{ex.InnerException}. \nStackTrace: {ex.StackTrace}. \nInnerExceptionMessage:{ex.InnerException?.Message}.");
            }
            return actualSecret;
        }

local settings & Azure Function App Settings:

"AzureADManagedIdentityClientId": "xxx-123-abc-xyz-567890"

"AzureKeyVaultUrl": "https://keyvaulturl.azurewebsites.net",

Nuget package and its versions:

Azure.Security.KeyVault.Secrets -- 4.3.0

Azure.Extensions.AspNetCore.Configuration.Secrets -- 1.2.2

Azure.Identity -- 1.6.1

Function Runtime Version: .NET Core V3.1

I am trying to read same secret value through code with help of same managed identity, I am getting error ManagedIdentityCredential authentication unavailable. Multiple attempts failed to obtain a token from the managed identity endpoint.' while debugging in local machine. I deployed azure function and in application insights, I am getting No Managed Identity found for specified ClientId/ResourceId/PrincipalId. Status: 400 (Bad Request)

I double cross checked PrincipalId, Its existed in both local seetings , azure function app settings and value is correct.

what am i doing wrong?

PavanKumar GVVS
  • 859
  • 14
  • 45

1 Answers1

0

Please check :

  1. When deployed to Azure resource that actually supports managed identity, the library automatically uses managed identities for Azure resources

Local machines does not support managed identities for Azure resources.

  1. So in local environment Microsoft.Azure.Services.AppAuthentication library uses the developer credentials.

  2. For local development, AzureServiceTokenProvider tries to fetch tokens first using Visual Studio, and then using Azure command-line interface (CLI), or Azure AD Integrated Authentication.

  3. You can try to use connection string specified in the AzureServicesAuthConnectionString environment variable that can be passed to the AzureServiceTokenProvider .

  4. You can use visual studio or azure cli method to pass to azure service authentication. Using azure cli , you can create service principal rbac for local testing something like

    az ad sp create-for-rbac --name local-sp --skip-assignment.

To use a connection string passed to the AzureServiceTokenProvider in the AzureServicesAuthConnectionString environment variable for Local development which uses AzureCli to get token.

`RunAs=Developer; DeveloperTool=AzureCli`

You may then have to add the service principal localtest-sp in the access control (IAM ) for the required Azure services.

enter image description here

  1. Using the DefaultAzureCredential in Azure.Identity will provide some sort of similar type functionality to AzureServiceTokenProvider in AppAuthentication, where current environment can be changed.

Please check this App Authentication client library for .NET | Microsoft Docs to try for other options using secret or certificate.

Snippets from AppAuthentication

Using AppAuthentication library :

AzureServiceTokenProvider tokenProvider = new AzureServiceTokenProvider();

var client = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(tokenProvider.KeyVaultTokenCallback));

...

Using Azure.Identity library :

var client = new SecretClient(new Uri("https://keyvaultname.vault.azure.net"), new

DefaultAzureCredential());

var secret = client.GetSecret("secretName").Value;
  • For Access token retrival

Using AppAuthentication library:

var tokenProvider = new AzureServiceTokenProvider();
var accessToken = await tokenProvider.GetAccessTokenAsync(ResourceId);

Using Azure.Identity library:

var tokenCredential = new DefaultAzureCredential();
var accessToken = await tokenCredential.GetTokenAsync(
    new TokenRequestContext(scopes: new string[] { ResourceId + "/.default" }) { }
);

Reference : python - ManagedIdentityCredential authentication unavailable, no managed identity endpoint found - Stack Overflow

kavyaS
  • 8,026
  • 1
  • 7
  • 19