In SharePoint Online projects, provisioning templates are a powerful way to capture site structures (lists, fields, content types, navigation, etc.) and reapply them to new sites.


Exporting SharePoint Templates with PnP Framework and MSAL (C# Console App)

In SharePoint Online projects, provisioning templates are a powerful way to capture site structures (lists, fields, content types, navigation, etc.) and reapply them to new sites.
In this guide, we’ll build a simple C# console application that exports a site template using the PnP Framework and authenticates with MSAL (Microsoft Authentication Library).


🔹 What the app does

  • Authenticates to SharePoint Online using Azure AD app registration (MSAL interactive login).
  • Connects to a given source site.
  • Exports its structure as a PnP XML template.
  • Saves the template in the same directory where the app is running.

✅ Full Source Code

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.SharePoint.Client;
using Microsoft.Identity.Client;
using PnP.Framework.Provisioning.Model;
using PnP.Framework.Provisioning.ObjectHandlers;
using PnP.Framework.Provisioning.Connectors;
using PnP.Framework.Provisioning.Providers.Xml;

namespace PnPConsoleExport
{
    internal class Program
    {
        // 🔐 Replace with your registered Azure AD App
        private static readonly string tenantId = "<TENANT_ID>";
        private static readonly string clientId = "<CLIENT_ID>";

        // Scope for SharePoint
        private static readonly string[] scopes = new[] { "https://<yourtenant>.sharepoint.com/AllSites.FullControl" };

        static async Task Main()
        {
            Console.WriteLine("=== PnP Template Export Tool ===");
            Console.Write("Enter the source SharePoint site URL: ");
            string sourceUrl = Console.ReadLine();

            // Current directory where the app is running
            string workDir = AppDomain.CurrentDomain.BaseDirectory;

            try
            {
                // 🔐 Authenticate with MSAL (Interactive)
                string token = await GetAccessTokenAsync();

                // Export Template
                ExportTemplate(sourceUrl, token, workDir);

                Console.WriteLine("✅ Export finished.");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"❌ ERROR: {ex.Message}");
                if (ex.InnerException != null) Console.WriteLine($"🔎 Inner: {ex.InnerException.Message}");
            }
        }

        // --- Authentication (MSAL Interactive) ---
        private static async Task<string> GetAccessTokenAsync()
        {
            var app = PublicClientApplicationBuilder.Create(clientId)
                .WithAuthority($"https://login.microsoftonline.com/{tenantId}")
                .WithRedirectUri("http://localhost")
                .Build();

            var result = await app.AcquireTokenInteractive(scopes).ExecuteAsync();
            Console.WriteLine("🔑 Token acquired.");
            return result.AccessToken;
        }

        // --- Export template as XML ---
        private static void ExportTemplate(string sourceUrl, string token, string workDir)
        {
            using (var ctx = GetContext(sourceUrl, token))
            {
                Console.WriteLine($"🌐 Exporting template from {sourceUrl}...");

                var creationInfo = new ProvisioningTemplateCreationInformation(ctx.Web)
                {
                    FileConnector = new FileSystemConnector(workDir, ""),
                    HandlersToProcess = Handlers.Lists | Handlers.Fields | Handlers.ContentTypes,
                    MessagesDelegate = (msg, type) => Console.WriteLine($"[PnP {type}] {msg}")
                };

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

                // Save as XML in the app directory
                string filePath = Path.Combine(workDir, "ExportedTemplate.xml");

                // Create XML provider pointing to the current folder
                var provider = new XMLFileSystemTemplateProvider(workDir, "");

                // Save the template as XML
                provider.SaveAs(template, "ExportedTemplate.xml");

                Console.WriteLine($"📦 Template exported to {filePath}");
            }
        }

        // --- Helper: Get Context with Token ---
        private static ClientContext GetContext(string url, string token)
        {
            var ctx = new ClientContext(url);
            ctx.ExecutingWebRequest += (s, e) =>
            {
                e.WebRequestExecutor.WebRequest.Headers["Authorization"] = "Bearer " + token;
            };
            return ctx;
        }
    }
}


🔹 How to run

  1. Register an app in Azure AD (Entra ID):
    • Allow public client flows.
    • Grant delegated permission AllSites.FullControl.
  2. Replace values in the code: private static readonly string tenantId = "<TENANT_ID>"; private static readonly string clientId = "<CLIENT_ID>"; private static readonly string[] scopes = new[] { "https://<yourtenant>.sharepoint.com/AllSites.FullControl" };
  3. Build and run the console app: dotnet build dotnet run
  4. Enter your source site URL when prompted.
  5. The tool will open a MSAL login window.
  6. The template will be saved as ExportedTemplate.xml in the same directory as the .exe.

🔹 Output Example

=== PnP Template Export Tool ===
Enter the source SharePoint site URL: https://tenant.sharepoint.com/sites/Engineering
🔑 Token acquired.
🌐 Exporting template from https://tenant.sharepoint.com/sites/Engineering...
[PnP Information]: Extracting Lists...
[PnP Information]: Extracting Fields...
[PnP Information]: Extracting Content Types...
📦 Template exported to C:\Users\you\source\repos\PnPConsoleExport\bin\Debug\net6.0\ExportedTemplate.xml
✅ Export finished.


🔹 Next Steps

  • Create a second console app (or extend this one) to apply the template to a target site.
  • Extend handlers if you want to capture navigation, features, or branding.
  • Automate token acquisition using client credentials if you want a headless job.

🔹 References


Edvaldo Guimrães Filho Avatar

Published by