In Part 1 we modularized PnP templates into multiple XML files.
In Part 2 we wrapped the process in an Azure Function for automation.

Now we’ll make the system cloud-native by replacing local storage (c:\pnp) with Azure Blob Storage.


Part 3: Storing SharePoint PnP Templates in Azure Blob Storage

In Part 1 we modularized PnP templates into multiple XML files.
In Part 2 we wrapped the process in an Azure Function for automation.

Now we’ll make the system cloud-native by replacing local storage (c:\pnp) with Azure Blob Storage.


1. Why Azure Blob Storage?

  • Scalable → store thousands of XML templates without size limits
  • Centralized → share templates across environments and teams
  • Secure → use SAS tokens, Managed Identity or Key Vault for access control
  • Automation → Functions can read/write directly to Blob Storage

2. Setting up Blob Storage

  1. Create a Storage Account in Azure
  2. Create a Blob Container (e.g., templates)
  3. Generate a connection string or use Managed Identity

3. Updating Code to Use Blob Storage

PnP already supports AzureBlobFileSystemConnector instead of FileSystemConnector.

Example: Export to Blob Storage

using Microsoft.SharePoint.Client;
using PnP.Framework;
using PnP.Framework.Provisioning.Connectors;
using PnP.Framework.Provisioning.ObjectHandlers;
using PnP.Framework.Provisioning.Providers.Xml;
using System;

namespace PnPExportBlob
{
    class Program
    {
        static void Main(string[] args)
        {
            string siteUrl = "https://yourtenant.sharepoint.com/sites/YourSite";
            string user = "user@yourtenant.onmicrosoft.com";
            string password = "yourpassword";

            // Azure Blob Storage config
            string connectionString = "DefaultEndpointsProtocol=https;AccountName=YOURACCOUNT;AccountKey=YOURKEY;EndpointSuffix=core.windows.net";
            string containerName = "templates";

            using (var ctx = new AuthenticationManager().GetSharePointOnlineAuthenticatedContextTenant(siteUrl, user, password))
            {
                var connector = new AzureStorageConnector(connectionString, containerName, "xml");

                var creationInfo = new ProvisioningTemplateCreationInformation(ctx.Web)
                {
                    FileConnector = connector,
                    HandlersToProcess = Handlers.Lists
                };

                var template = ctx.Web.GetProvisioningTemplate(creationInfo);

                var provider = new XMLAzureStorageTemplateProvider(connectionString, containerName, "xml");
                provider.SaveAs(template, "List_Projects.xml");

                Console.WriteLine("✅ Template exported to Azure Blob Storage.");
            }
        }
    }
}


Example: Import from Blob Storage

using Microsoft.SharePoint.Client;
using PnP.Framework;
using PnP.Framework.Provisioning.Providers.Xml;
using PnP.Framework.Provisioning.Connectors;

namespace PnPImportBlob
{
    class Program
    {
        static void Main(string[] args)
        {
            string targetUrl = "https://yourtenant.sharepoint.com/sites/TargetSite";
            string user = "user@yourtenant.onmicrosoft.com";
            string password = "yourpassword";

            // Azure Blob Storage config
            string connectionString = "DefaultEndpointsProtocol=https;AccountName=YOURACCOUNT;AccountKey=YOURKEY;EndpointSuffix=core.windows.net";
            string containerName = "templates";

            using (var ctx = new AuthenticationManager().GetSharePointOnlineAuthenticatedContextTenant(targetUrl, user, password))
            {
                var provider = new XMLAzureStorageTemplateProvider(connectionString, containerName, "xml");

                var template = provider.GetTemplate("List_Projects.xml");
                ctx.Web.ApplyProvisioningTemplate(template);

                Console.WriteLine("✅ Template imported from Azure Blob Storage.");
            }
        }
    }
}


4. Integrating with Azure Functions

In Part 2 we used:

var provider = new XMLFileSystemTemplateProvider(@"c:\pnp", "");

Now we swap it for:

var provider = new XMLAzureStorageTemplateProvider(connectionString, "templates", "xml");

This means both export and import will save/load templates directly from Blob Storage, so Power Automate (or any system) can also fetch the XMLs later.


5. Best Practices

  • Do not hardcode keys → use Azure Key Vault or Managed Identity
  • Organize containers → use folder-like prefixes:
    • /exports/{siteName}/Fields.xml
    • /exports/{siteName}/Lists/List_Projects.xml
  • Versioning → enable Blob versioning to keep history of templates
  • Lifecycle policies → auto-clean old templates after X days

✅ Key Takeaways

StepLocal DiskCloud Native
ConnectorFileSystemConnector(@"c:\pnp")AzureStorageConnector(connectionString, container, "xml")
ProviderXMLFileSystemTemplateProviderXMLAzureStorageTemplateProvider
Locationc:\pnp\*.xmlhttps://<account>.blob.core.windows.net/templates/*.xml
BenefitLocal onlyGlobally accessible, secure, automated

👉 With this change, the provisioning workflow is fully cloud-based:

  • Azure Functions orchestrate
  • Templates live in Blob Storage
  • Power Automate can trigger and even retrieve XMLs
Edvaldo Guimrães Filho Avatar

Published by