Export Multiple Pages

This PnP PowerShell script automates the export of multiple SharePoint pages for backup, migration, or content management purposes. Export page content, metadata, and configurations in bulk operations.

Purpose

This script helps you:

  • Backup multiple SharePoint pages and their content
  • Export pages for migration between sites or tenants
  • Create content archives for compliance requirements
  • Document existing page structures and layouts

Prerequisites

  • PnP PowerShell module installed
  • Site collection administrator permissions
  • Connection to your SharePoint Online site
  • Access to pages library or site pages

PowerShell Script

# SharePoint Page Export Script - XML Output with CSV Logging
param(
    [Parameter(Mandatory=$false)]
    [string]$ConfigFile = "Migrate_MultiplePages.json",
    [Parameter(Mandatory=$false)]
    [string]$ClientId = ""
)

# Get script directory for output files
$ScriptDir = $PSScriptRoot
if ([string]::IsNullOrEmpty($ScriptDir)) {
    $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
}

# Generate log filename with timestamp
$LogFileName = "SharePoint_Export_Log_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
$LogPath = Join-Path $ScriptDir $LogFileName

# Initialize log array
$LogEntries = @()

# Function to add log entry
function Add-LogEntry {
    param(
        [string]$PageIdentity,
        [string]$Status,
        [string]$OutputPath = "",
        [string]$ErrorMessage = "",
        [string]$Duration = ""
    )

    $LogEntries += [PSCustomObject]@{
        Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
        PageIdentity = $PageIdentity
        Status = $Status
        OutputPath = $OutputPath
        ErrorMessage = $ErrorMessage
        Duration = $Duration
        User = $env:USERNAME
        Computer = $env:COMPUTERNAME
    }
}

# Function to write log to CSV
function Write-LogToCsv {
    try {
        $LogEntries | Export-Csv -Path $LogPath -NoTypeInformation -Encoding UTF8
        Write-Host "Log exported to: $LogPath" -ForegroundColor Green
    }
    catch {
        Write-Error "Failed to write log file: $($_.Exception.Message)"
    }
}

# Check if config file exists
if (-not (Test-Path $ConfigFile)) {
    Write-Error "Configuration file '$ConfigFile' not found. Please ensure the JSON file exists."
    Add-LogEntry -PageIdentity "N/A" -Status "ERROR" -ErrorMessage "Configuration file not found: $ConfigFile"
    Write-LogToCsv
    exit 1
}

# Read and parse JSON configuration
try {
    $configContent = Get-Content $ConfigFile -Raw
    $config = $configContent | ConvertFrom-Json
    Write-Host "Loaded configuration for $($config.pages.Count) pages" -ForegroundColor Green
    Add-LogEntry -PageIdentity "CONFIG" -Status "SUCCESS" -ErrorMessage "Loaded $($config.pages.Count) pages from config"
}
catch {
    Write-Error "Failed to parse JSON configuration file: $($_.Exception.Message)"
    Add-LogEntry -PageIdentity "CONFIG" -Status "ERROR" -ErrorMessage "Failed to parse JSON: $($_.Exception.Message)"
    Write-LogToCsv
    exit 1
}

# Extract settings
$SourceSiteUrl = $config.settings.sourceSiteUrl
$BaseOutputPath = $config.settings.baseOutputPath

Write-Host "Source Site: $SourceSiteUrl" -ForegroundColor Cyan
Write-Host "Output Path: $BaseOutputPath" -ForegroundColor Cyan
Write-Host "Log File: $LogPath" -ForegroundColor Cyan

# Connect to SharePoint
try {
    Write-Host "Connecting to SharePoint site: $SourceSiteUrl" -ForegroundColor Yellow
    $connectionStart = Get-Date
    Connect-PnPOnline -Url $SourceSiteUrl -Interactive -ClientId $ClientId
    $connectionEnd = Get-Date
    $connectionDuration = "{0:F2}" -f ($connectionEnd - $connectionStart).TotalSeconds
    Write-Host "Connected successfully! ($connectionDuration seconds)" -ForegroundColor Green
    Add-LogEntry -PageIdentity "CONNECTION" -Status "SUCCESS" -ErrorMessage "Connected to $SourceSiteUrl" -Duration "$connectionDuration seconds"
}
catch {
    Write-Error "Failed to connect to SharePoint: $($_.Exception.Message)"
    Add-LogEntry -PageIdentity "CONNECTION" -Status "ERROR" -ErrorMessage "Failed to connect: $($_.Exception.Message)"
    Write-LogToCsv
    exit 1
}

# Create output directory if it doesn't exist
if (-not (Test-Path $BaseOutputPath)) {
    try {
        New-Item -ItemType Directory -Path $BaseOutputPath -Force | Out-Null
        Write-Host "Created output directory: $BaseOutputPath" -ForegroundColor Gray
    }
    catch {
        Write-Error "Failed to create output directory: $($_.Exception.Message)"
        Add-LogEntry -PageIdentity "DIRECTORY" -Status "ERROR" -ErrorMessage "Failed to create directory: $BaseOutputPath"
        Write-LogToCsv
        exit 1
    }
}

# Process each page
$successCount = 0
$errorCount = 0

Write-Host "`nStarting page exports..." -ForegroundColor Yellow

foreach ($page in $config.pages) {
    $pageStart = Get-Date

    # Extract filename from identity for the XML output
    $pageName = $page.identity
    if ($pageName.Contains("/")) {
        $fileName = Split-Path $pageName -Leaf
        $fileName = [System.IO.Path]::GetFileNameWithoutExtension($fileName)
    } else {
        $fileName = $pageName -replace "\.aspx$", ""
    }

    $outputFileName = "$fileName.xml"
    $outputPath = Join-Path $BaseOutputPath $outputFileName

    try {
        Write-Host "Exporting: $($page.identity) -> $outputFileName" -ForegroundColor Cyan

        # Export the page using PnP
        Export-PnPPage -Identity $page.identity -Out $outputPath -Force

        $pageEnd = Get-Date
        $pageDuration = "{0:F2}" -f ($pageEnd - $pageStart).TotalSeconds

        Write-Host "✓ Success: $($page.identity) ($pageDuration seconds)" -ForegroundColor Green
        Add-LogEntry -PageIdentity $page.identity -Status "SUCCESS" -OutputPath $outputPath -Duration "$pageDuration seconds"
        $successCount++
    }
    catch {
        $pageEnd = Get-Date
        $pageDuration = "{0:F2}" -f ($pageEnd - $pageStart).TotalSeconds

        Write-Host "✗ Failed: $($page.identity) - $($_.Exception.Message)" -ForegroundColor Red
        Add-LogEntry -PageIdentity $page.identity -Status "ERROR" -OutputPath $outputPath -ErrorMessage $_.Exception.Message -Duration "$pageDuration seconds"
        $errorCount++
    }
}

# Summary
Write-Host "`n=== Export Summary ===" -ForegroundColor Yellow
Write-Host "Successfully exported: $successCount pages" -ForegroundColor Green
Write-Host "Failed exports: $errorCount pages" -ForegroundColor Red
Write-Host "Total processed: $($successCount + $errorCount) pages" -ForegroundColor White

# Add summary to log
Add-LogEntry -PageIdentity "SUMMARY" -Status "COMPLETE" -ErrorMessage "Success: $successCount, Failed: $errorCount, Total: $($successCount + $errorCount)"

# Write log to CSV
Write-LogToCsv

# Disconnect from SharePoint
try {
    Disconnect-PnPOnline
    Write-Host "Disconnected from SharePoint" -ForegroundColor Gray
}
catch {
    Write-Warning "Didn't disconnect from SharePoint"
}

JSON Configuration File

{
  "settings": {
    "sourceSiteUrl": "https://tenantName.sharepoint.com/sites/siteName",
    "baseOutputPath": "C:\\Temp\\PageExports"
  },
  "pages": [
    {
      "identity": "Home.aspx"
    },
    {
      "identity": "About-Us.aspx"
    },
    {
      "identity": "Organisation/Corporate-Guidance.aspx"
    },
    {
      "identity": "templates/Standard-Template.aspx"
    },
    {
      "identity": "Supporting Documents/Policy-Document.aspx"
    }
  ]
}

Usage Notes

  • Ensure you have read permissions on all pages to be exported
  • Large page exports may take considerable time to complete
  • Consider filtering by date or content type for selective exports
  • Test export process with a small subset before bulk operations
  • Exported data includes page content, metadata, and web part configurations