Forum Discussion

johnjohn-Peter's avatar
johnjohn-Peter
Iron Contributor
Apr 22, 2025

SharePoint Online REST API using Azure Function Managed Identity

I have created an Azure Function which uses .NET Core 8.0. and i enabled its managed identity:-

also i accessed the azure function from "Enterprise Application", and i copied its AppID:-

Then i run those commands as per this official documentation for Microsft @ https://learn.microsoft.com/en-us/sharepoint/dev/apis/webhooks/sharepoint-webhooks-using-azd-template#grant-the-function-app-access-to-sharepoint-online :-

# This script requires the modules Microsoft.Graph.Authentication, Microsoft.Graph.Applications, Microsoft.Graph.Identity.SignIns, which can be installed with the cmdlet Install-Module below:
# Install-Module Microsoft.Graph.Authentication, Microsoft.Graph.Applications, Microsoft.Graph.Identity.SignIns -Scope CurrentUser -Repository PSGallery -Force
Connect-MgGraph -Scope "Application.Read.All", "AppRoleAssignment.ReadWrite.All"
$managedIdentityObjectId = "******" # 'Object (principal) ID' of the managed identity
$scopeName = "Sites.Selected"
$resourceAppPrincipalObj = Get-MgServicePrincipal -Filter "displayName eq 'Office 365 SharePoint Online'" # SPO
$targetAppPrincipalAppRole = $resourceAppPrincipalObj.AppRoles | ? Value -eq $scopeName

$appRoleAssignment = @{
    "principalId" = $managedIdentityObjectId
    "resourceId"  = $resourceAppPrincipalObj.Id
    "appRoleId"   = $targetAppPrincipalAppRole.Id
}
New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $managedIdentityObjectId -BodyParameter $appRoleAssignment | Format-List

and this command, on the Analytics site:-

Connect-PnPOnline -Url "https://YOUR_SHAREPOINT_TENANT_PREFIX.sharepoint.com/sites/analytics" -Interactive -ClientId "YOUR_PNP_APP_CLIENT_ID"
Grant-PnPAzureADAppSitePermission -AppId "****" -DisplayName "YOUR_FUNC_APP_NAME" -Permissions Manage

Everything went well, then I verify the above for the Analytics site, as follow:-

here is my code inside Azure Function, to get the items inside a list named "Call Transfer Log Data":-

accessToken = await GetJwtTokenUsingSystemManagedIdentity();
try
{

    string siteUrl = "https://***.sharepoint.com/sites/analytics";
    string listName = "Call Transfer Log Data";
    string tenant = "****";
    string site = "analytics";
    string listTitle = "Call Transfer Log Data";
    siteUrl = $"https://{tenant}.sharepoint.com/sites/{site}";
    string apiBaseUrl = $"{siteUrl}/_api/web/lists/GetByTitle('{listTitle}')/items";
    var httpClient = new HttpClient();
    httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
    httpClient.DefaultRequestHeaders.Accept.ParseAdd("application/json;odata=verbose");
    string filterDate = DateTime.UtcNow.AddDays(-120).ToString("yyyy-MM-ddTHH:mm:ssZ");
    string requestUrl = $"{apiBaseUrl}?$filter=Modified ge datetime'{filterDate}'&$top=100&$orderby=Modified desc";

    bool hasMore = true;
    int page = 1;
    List<CallTransferLogData> responseContent = new List<CallTransferLogData>();
    while (hasMore)
    {
        Console.WriteLine($"Fetching page {page}...");
        var request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
        var response = await httpClient.SendAsync(request);
        Console.WriteLine("Raw response ");
        Console.WriteLine(response);
        string content = await response.Content.ReadAsStringAsync();

        using JsonDocument doc = JsonDocument.Parse(content);
        Console.WriteLine($"Parse doc {page}...");
        Console.WriteLine("Raw response content:");
        Console.WriteLine(content);
        var root = doc.RootElement.GetProperty("d");
        Console.WriteLine($"Building Root {page}...");
        // Process results
        foreach (var item in root.GetProperty("results").EnumerateArray())
        {//code goes here;;
   }//end of try

    private static async Task<string> GetJwtTokenUsingSystemManagedIdentity()
    {
        string resource = "https://****.sharepoint.com/.default";
        var credential = new DefaultAzureCredential();
        var tokenRequestContext = new TokenRequestContext(new[] { resource });
        var token = await credential.GetTokenAsync(tokenRequestContext);
        Console.WriteLine("Toekn is " + token.Token)
;            return token.Token;
        }

but the content will be {"error_description":"ID3035: The request was not valid or is malformed."}

the full response will be:-

2025-04-22T16:31:38Z   [Information]   StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:
2025-04-22T16:31:38Z   [Information]   {
2025-04-22T16:31:38Z   [Information]     Cache-Control: private
2025-04-22T16:31:38Z   [Information]     Server: Microsoft-IIS/10.0
2025-04-22T16:31:38Z   [Information]     X-NetworkStatistics: 0,********0
2025-04-22T16:31:38Z   [Information]     x-ms-diagnostics: 3001000;reason="There has been an error authenticating the request.";category="invalid_client"
2025-04-22T16:31:38Z   [Information]     IsOCDI: 0
2025-04-22T16:31:38Z   [Information]     X-DataBoundary: NONE
2025-04-22T16:31:38Z   [Information]     X-1DSCollectorUrl: https://mobile.events.data.microsoft.com/OneCollector/1.0/
2025-04-22T16:31:38Z   [Information]     X-AriaCollectorURL: https://browser.pipe.aria.microsoft.com/Collector/3.0/
2025-04-22T16:31:38Z   [Information]     SPRequestGuid: 4***2
2025-04-22T16:31:38Z   [Information]     request-id: 4***2
2025-04-22T16:31:38Z   [Information]     MS-CV: o****/Q8g.0
2025-04-22T16:31:38Z   [Information]     SPRequestDuration: 56
2025-04-22T16:31:38Z   [Information]     SPIisLatency: 2
2025-04-22T16:31:38Z   [Information]     X-Powered-By: ASP.NET
2025-04-22T16:31:38Z   [Information]     MicrosoftSharePointTeamServices: 16.0.0.26002
2025-04-22T16:31:38Z   [Information]     X-Content-Type-Options: nosniff
2025-04-22T16:31:38Z   [Information]     X-MS-InvokeApp: 1; RequireReadOnly
2025-04-22T16:31:38Z   [Information]     P3P: CP="ALL ****"
2025-04-22T16:31:38Z   [Information]     WWW-Authenticate: Bearer realm="e****20",client_id="00000003-******00-000000000000",trusted_issuers="00000001-0000-0000-c000-000000000000@*,D****@*,https://sts.windows.net/*/,https://login.microsoftonline.com/*/v2.0,00000003-0000-0ff1-ce00-000000000000@***b",authorization_uri="https://login.microsoftonline.com/common/oauth2/authorize"
2025-04-22T16:31:38Z   [Information]     Date: Tue, 22 Apr 2025 16:31:36 GMT
2025-04-22T16:31:38Z   [Information]     Content-Length: 74
2025-04-22T16:31:38Z   [Information]   }

Also when i decode the token, i got those valid claims for all the properties.. So why the code is not working? Thanks

No RepliesBe the first to reply

Resources