azureazure-functions

Azure Function App using Key Vault with Managed identity not authorised


I have written a function app and at the start of my code I have:

 var credential = new  ManagedIdentityCredential(); 
 var client = new SecretClient(vaultUri, credential);

Within Azure, I have gone to the 'Identity' section of my function app and set status to 'on' for system assigned. I have also gone to the key vault and with 'Access Control (IAM), I have added the managed identity as a Key Vaults Secrets User.

From within my function app if I go to Access Control there, I have Assigned the managed role as a contributor and managed application contributor role.

I have no idea what else I need to set to give access to the key vault from within my app for the managed user.

I also publish the function all as self-contained.

Start of the error in the invocation section of the function app

Result: Failure Exception: Azure.RequestFailedException: Caller is not authorized to perform action on resource. If role assignments, deny assignments or role definitions were changed recently, please observe propagation time. Caller: appid=61c8be63-9b5d-4c29-80ec-c839a0f0a61c;oid=849fa139-74c5-4adb-9b94-74bc444b68c0;iss=https://sts.windows.net/2f77693b-b7cd-4918-8d8a-d4f28910516f/ Action: 'Microsoft.KeyVault/vaults/secrets/getSecret/action' Resource: '/subscriptions/fb54f07b-964f-4366-88e2-

Following Ikhtesam Shots from his answer below I have:

In my Program.cs file I have:

var host = new HostBuilder()
    .ConfigureFunctionsWebApplication()
    .ConfigureServices((context,services) =>
    {
        var vaultUri = new Uri("https://{Key Vault Name Here}.vault.azure.net/"); 
        var credential = new   ManagedIdentityCredential(); 
        var client = new SecretClient(vaultUri, credential); 
        services.AddSingleton(client); 
        services.AddTransient<ISecretService, SecretService>();

then in my function app I have:

public class GetMongoData
{
    private readonly ILogger _logger;
    private readonly IMySqlSession _session;
    private readonly IMySqlClaimants _claimants;
    private readonly IMySqlAddresses _addresses;    
    private readonly IMySqlClaims _claims;
    private readonly IMySqlDefendants _defendants;
    private readonly IMySqlDuplicateClaims _duplicateClaims;
    private readonly IMySqlEvidence _evidence;
    private readonly IMySqlStatuses _statuses;
    private readonly ISecretService _secretService;
    private readonly string _sshPath;
    private readonly string _mySqlServerIp;

    public GetMongoData(ILoggerFactory loggerFactory, IConfiguration 
configuration, IMySqlSession mySqlSession, IMySqlClaimants mySqlClaimants,
                        IMySqlAddresses mySqlAddresses, IMySqlClaims mySqlClaims, IMySqlDefendants defendants, IMySqlDuplicateClaims mySqlDuplicateClaims,
                        IMySqlEvidence mySqlEvidence, IMySqlStatuses mySqlStatuses, ISecretService secretService)
    {

        _logger = loggerFactory.CreateLogger<GetMongoData>();
        _session = mySqlSession;
        _secretService = secretService;
        _claimants = mySqlClaimants;
        _addresses = mySqlAddresses;
        _claims = mySqlClaims;
        _defendants = defendants;
        _duplicateClaims = mySqlDuplicateClaims;
        _evidence = mySqlEvidence;
        _statuses = mySqlStatuses;
        _mySqlServerIp = _secretService.GetSecret("MySqlServerIp");
        _defendants = defendants;
        
       
    }

[Function("RetrieveMongoData")]
public void Run([TimerTrigger("0 0 * * * *")] TimerInfo myTimer)
{ //function set to run every hour.
    _logger.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
    try
    {
        var server = _mySqlServerIp;
        var sshUserName = "forge";
        var sshPassword = "";

enter image description here


Solution

  • enter image description here

    using System;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Azure.WebJobs;
    using Microsoft.Azure.WebJobs.Extensions.Http;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.Logging;
    using Azure.Identity;
    using Azure.Security.KeyVault.Secrets;
    
    namespace FunctionApp6
    {
        public static class Function1
        {
            [FunctionName("GetSecret")]
            public static async Task<IActionResult> Run(
                [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
                ILogger log)
            {
                log.LogInformation("C# HTTP trigger function processed a request.");
    
                try
                {
                    string keyVaultUrl = "https://{keyvaultName}.vault.azure.net/";
    
                    var credential = new ManagedIdentityCredential();
                    var client = new SecretClient(new Uri(keyVaultUrl), credential);
    
                    string secretName = "testSecret"; 
                    KeyVaultSecret secret = await client.GetSecretAsync(secretName);
                    string secretValue = secret.Value;
    
                    log.LogInformation($"Fetched Secret Value: {secretValue}");
    
                    return new OkObjectResult($"Secret fetched successfully. Check logs for the value.");
                }
                catch (Exception ex)
                {
                    log.LogError($"Error fetching secret: {ex.Message}");
                    return new StatusCodeResult(StatusCodes.Status500InternalServerError);
                }
            }
        }
    }
    

    enter image description here

    If you intent to read the secret content then Key Vaults Secrets User role should do. Please verify if you have assigned the role correctly as I did.

    I have created an isolated timer trigger function and have added below given code in program.cs file which worked successfully for me.

    using Azure.Identity;
    using Azure.Security.KeyVault.Secrets;
    using Microsoft.Azure.Functions.Worker.Builder;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    
    var host = new HostBuilder()
        .ConfigureFunctionsWebApplication()
        .ConfigureServices((context, services) =>
        {
            var vaultUri = new Uri("https://afreeenKv.vault.azure.net/");
            var credential = new ManagedIdentityCredential();
            var client = new SecretClient(vaultUri, credential);
            services.AddSingleton(client);
        })
        .Build();
    
    host.Run();