So far in this series:

  • Part 1 → Modular export/import with PnP Framework
  • Part 2 → Wrapping the process in an Azure Function
  • Part 3 → Moving template storage to Azure Blob Storage

Now let’s take the final step toward a cloud-native and secure architecture by replacing client secrets and hardcoded credentials with Managed Identity.

Part 4: Using Managed Identity for SharePoint and Blob Storage in Azure Functions

So far in this series:

  • Part 1 → Modular export/import with PnP Framework
  • Part 2 → Wrapping the process in an Azure Function
  • Part 3 → Moving template storage to Azure Blob Storage

Now let’s take the final step toward a cloud-native and secure architecture by replacing client secrets and hardcoded credentials with Managed Identity.


1. What is Managed Identity?

Managed Identity is an Azure feature that allows your Function App (or any Azure service) to authenticate to other Azure resources without storing credentials.

  • System-assigned identity → unique to a resource, lifecycle managed automatically
  • User-assigned identity → reusable across multiple resources
  • Supports RBAC (Role-Based Access Control)
  • Works seamlessly with Azure SDKs via DefaultAzureCredential

2. Enabling Managed Identity in an Azure Function

  1. Open your Function App in the Azure Portal
  2. Go to Identity → System Assigned → On
  3. Note down the Object ID (Principal ID)
  4. Assign RBAC roles:
    • Blob StorageStorage Blob Data Contributor
    • SharePoint → grant via Entra ID app roles (Sites.FullControl.All or AllSites.FullControl)

3. Accessing Blob Storage with Managed Identity

Instead of using connection strings, we can authenticate directly with DefaultAzureCredential.

using Azure.Identity;
using Azure.Storage.Blobs;
using PnP.Framework.Provisioning.Providers.Xml;

// Blob Storage config
string accountUrl = "https://YOURACCOUNT.blob.core.windows.net";
string containerName = "templates";

// Authenticate via Managed Identity
var credential = new DefaultAzureCredential();
var blobServiceClient = new BlobServiceClient(new Uri(accountUrl), credential);
var containerClient = blobServiceClient.GetBlobContainerClient(containerName);

// Pass to PnP provider
var provider = new XMLAzureStorageTemplateProvider(accountUrl, containerName, "xml", credential);

✅ No connection strings, no secrets.


4. Accessing SharePoint with Managed Identity

PnP Framework does not (yet) support direct Managed Identity auth.
But we can use MSAL via DefaultAzureCredential to acquire an access token, then inject it into ClientContext.

using Azure.Identity;
using Microsoft.SharePoint.Client;

string siteUrl = "https://yourtenant.sharepoint.com/sites/TargetSite";

var credential = new DefaultAzureCredential();
var token = credential.GetToken(
    new Azure.Core.TokenRequestContext(
        new[] { siteUrl + "/.default" })
);

using (var ctx = new ClientContext(siteUrl))
{
    ctx.ExecutingWebRequest += (s, e) =>
    {
        e.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + token.Token;
    };

    ctx.Load(ctx.Web, w => w.Title);
    ctx.ExecuteQuery();

    Console.WriteLine($"Connected to site: {ctx.Web.Title}");
}


5. Integrating into an Azure Function

Inside your Function, replace secrets with Managed Identity:

[Function("CloneSPSite")]
public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req)
{
    string siteUrl = "https://yourtenant.sharepoint.com/sites/TargetSite";
    string accountUrl = "https://YOURACCOUNT.blob.core.windows.net";
    string containerName = "templates";

    var credential = new DefaultAzureCredential();

    // Acquire token for SharePoint
    var token = credential.GetToken(new Azure.Core.TokenRequestContext(new[] { siteUrl + "/.default" }));

    using (var ctx = new ClientContext(siteUrl))
    {
        ctx.ExecutingWebRequest += (s, e) =>
        {
            e.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + token.Token;
        };

        // Blob Storage provider
        var provider = new XMLAzureStorageTemplateProvider(accountUrl, containerName, "xml", credential);

        // Example: Apply template
        var template = provider.GetTemplate("Fields.xml");
        ctx.Web.ApplyProvisioningTemplate(template);
    }

    return new OkObjectResult(new { success = true, message = "Executed with Managed Identity" });
}


6. Best Practices

  • Prefer System-Assigned MI unless you need to reuse across multiple apps
  • RBAC → assign least-privilege roles (Storage Blob Data Contributor, Sites.FullControl.All)
  • Key Vault → if secrets are still needed for legacy scenarios, manage them via Key Vault
  • Local testingDefaultAzureCredential falls back to Azure CLI / Visual Studio credentials when running locally
  • Logging → combine with Application Insights for audit and troubleshooting

✅ Key Takeaways

AreaOld wayWith Managed Identity
Blob StorageConnection string with secretDefaultAzureCredential()
SharePointUsername + password / client secretToken injected automatically
SecuritySecrets in code/configZero secrets, RBAC-based
ScalabilityLocal/VM onlyCloud-native and reusable

👉 With Managed Identity, your provisioning process becomes fully secure and cloud-native:

  • Azure Functions authenticate seamlessly
  • Templates are stored in Blob Storage
  • SharePoint is accessed via automatic tokens
  • No secrets or credentials ever exposed
Edvaldo Guimrães Filho Avatar

Published by