Search code examples
azureazure-functionsmicrosoft-graph-apiazure-managed-identity

Trouble obtaining access token for managed identity with application role


I'm trying to assign an app role to my function app's system-assigned managed identity.

I believe I've followed all the required steps but my access token (obtained through DefaultAzureCredential()) still doesn't contain the required role (Mail.Send).

Here's what I've done:

  1. I created an app registration and added the Microsoft Graph Mail.Send permission with Type: Application and obtained admin consent.

  2. I created an app role to use Mail.Send with.. Allowed member types: Applications, Value: Mail.Send, State: Enabled.

  3. I assigned the role to my function app's managed identity. I did this using Azure CLI following these instructions. I then can confirm that the permission appears under my managed identity's Permissions in the Portal with... Claim value: Mail.Send, Type: Application, Granted through: Admin Consent, Granted by: An administrator.


Solution

  • I have created a function App and enabled System assigned managed identity.

    Assign Send.Mail role to System Managed Identity of function App using below PowerShell Script.

    Connect-AzureAD -TenantId <Tenant_ID>
    
    $TenantID="<Tenant_ID>"
    $GraphAppId = "00000003-0000-0000-c000-000000000000" 
    $DisplayNameOfMSI="Display Name of Managed Identity"
    $PermissionName = "Mail.Send"
    
    $ObjectId= "Object ID of managed identity"
    Start-Sleep -Seconds 10
    $GraphServicePrincipal = Get-AzureADServicePrincipal -Filter "appId eq '$GraphAppId'"
    
    New-AzureAdServiceAppRoleAssignment -ObjectId <Object_ID> -PrincipalId <Principal_ID> -ResourceId $GraphServicePrincipal.ObjectId -Id <AppRole_Id>
    

    enter image description here

    Function code to obtain access token for managed identity with application role.

    public class Function1
    {
        private readonly ILogger<Function1> _logger;
    
        public Function1(ILogger<Function1> logger)
        {
            _logger = logger;
        }
    
        [Function("Function1")]
        public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequest req)
        {
            _logger.LogInformation("C# HTTP trigger function processed a request.");
    
            string jwtToken = await GetJwtTokenUsingSystemManagedIdentity();
            return new OkObjectResult($"JWT Token: {jwtToken}");
        }
    
        private static async Task<string> GetJwtTokenUsingSystemManagedIdentity()
        {
            string resource = "https://graph.microsoft.com/.default"; 
            var credential = new DefaultAzureCredential();  
    
            var tokenRequestContext = new Azure.Core.TokenRequestContext(new[] { resource });
            var token = await credential.GetTokenAsync(tokenRequestContext);
            
            return token.Token;  
        }
    }
    
    Connected! You are now viewing logs of Function runs in the current Code + Test panel. To see all the logs for this Function, please go to 'Logs' from the Function menu.
    2025-01-27T08:40:20Z   [Information]   Executing 'Functions.Function1' (Reason='This function was programmatically called via the host APIs.', Id=394cf91b-4b2c-4335-9d5a-33fab65735ad)
    2025-01-27T08:40:21Z   [Information]   C# HTTP trigger function processed a request.
    2025-01-27T08:40:21Z   [Information]   Executing OkObjectResult, writing value of type 'System.String'.
    2025-01-27T08:40:22Z   [Information]   Executed 'Functions.Function1' (Succeeded, Id=394cf91b-4b2c-4335-9d5a-33fab65735ad, Duration=2002ms)
    

    Able to generate the Access token in functionapp:

    enter image description here

    Decoded Access token and able to see the assigned role Mail.Send:

    enter image description here