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
- Open your Function App in the Azure Portal
- Go to Identity → System Assigned → On
- Note down the Object ID (Principal ID)
- Assign RBAC roles:
- Blob Storage → Storage Blob Data Contributor
- SharePoint → grant via Entra ID app roles (
Sites.FullControl.AllorAllSites.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 testing →
DefaultAzureCredentialfalls back to Azure CLI / Visual Studio credentials when running locally - Logging → combine with Application Insights for audit and troubleshooting
✅ Key Takeaways
| Area | Old way | With Managed Identity |
|---|---|---|
| Blob Storage | Connection string with secret | DefaultAzureCredential() |
| SharePoint | Username + password / client secret | Token injected automatically |
| Security | Secrets in code/config | Zero secrets, RBAC-based |
| Scalability | Local/VM only | Cloud-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
