SharePoint - How to create a set of Modern Pages from CSV list using PowerShell

Steel Contributor

In some case, it could be useful to use the Modern Pages into the Help site pages.

The previous generic case proposed was the Wiki Site with Wiki Pages.


Advantage to use the Modern Pages

In SharePoint Online the implementation of the modern pages propose many features useful and simplifying the end user navigation:

  • Modern Page display
  • Modern WebPart component
  • Integration of the Modern Pages into the Modern News WebPart
  • Aggregation of the Modern Pages into the Communication Hub Site
  • Aggregation of the Modern Pages into the SharePoint Application (Web and Mobile)
  • Adaptation of the display for any Devices without any development
  • ...

It's really a basic to match with the Microsoft Strategy and the Wiki Site/Page is not anymore part of it.


Business Case presentation

This solution is matching with a dedicated requirement I had:

  • Associated with a Business CheckList, create one Help Page per check task automatically
  • Group the Help page into the Site left menu navigation per topic
  • Implement into each page then basic template using one canvas and text basic
  • Automatize the page creation and menu item creation

The CSV file need to have only the following entries (columns):

  • CHECKID: with a Text format like "1.01", used for the filename "1.01.aspx"
  • CHECKNAME: with Text format with the long task name like "Collect data from the business line"
  • CHECKTOPIC: with Text format like "01. BASICS TASKS"

A generic picture per topic need to be placed into the current SiteAsset library with the name "$(CHECKTOPIC).JPG".


When the Script is executed the responsible of the Checklist have to edit each page to complete the content into the different parts like:


Task Title: Collect the Data from the Business




Get all the data from the business line to be sure the mission is ready to start


Estimated Time:

  • Some minutes

Person in charge:

  • Task Manager

Starting time:

  • When the mission is started



PowerShell script:

You can use that script and adapt it to your specific case as you want, it's only a base to use.

$HelpAndQAUrl = "https://[YourTenant]"
$CSVFilePath = "C:\Business-CheckList.csv"

##Connecting to site - get and save your O365 credentials
[string]$username = ""
[string]$PwdTXTPath = "C:\SECUREDPWD\ExportedPWD-$($username).txt"
$secureStringPwd = ConvertTo-SecureString -string (Get-Content $PwdTXTPath)
$cred = New-Object System.Management.Automation.PSCredential -ArgumentList $username, $secureStringPwd

# ---------------------------------------------------------------------------------------------------------------

[string]$TextTemplateToPut = ""
[string]$pageFilename = ""
[string]$pageFileTitle = ""
[string]$PageTopic = ""
[string]$PageTitleFull = ""
[string]$pageFilename = ""
[string]$PageTitleToUpdate = ""
[int]$ParentNodeID = 0
[int]$PageNodeID = 0

# ---------------------------------------------------------------------------------------------------------------
$AllPagesToCreate = Import-Csv -Path $CSVFilePath

# --------------------------------------------------------------------------------------------
#Loop for each line
foreach ($PageToCreate in $AllPagesToCreate)
	# ---------------------------------------------------------------------------------------------------------------
	Write-host " ==> Page ID", $PageToCreate.CHECKID, "- Name:", $PageToCreate.CHECKNAME , "- Topic:", $PageToCreate.CHECKTOPIC -ForegroundColor Yellow
	# ---------------------------------------------------------------------------------------------------------------

	$pageFileTitle = $PageToCreate.CHECKID
	$PageTopic = $PageToCreate.CHECKTOPIC
	$PageTitleFull = $PageToCreate.CHECKNAME
	if($PageTitleFull.IndexOf("(") -gt 0)
		$PageTitleShort = $PageTitleFull.Substring(0, $PageTitleFull.IndexOf("("))
		$PageTitleShort = $PageTitleFull
	$pageFilename =  -join($pageFileTitle, ".aspx")

	$TextTemplateToPut = "<h2>Task Title: $($PageTitleFull)</h2>"
	$TextTemplateToPut += "<h3>Topic:</h3><ul><li>$($PageTopic)</li></ul>"
	$TextTemplateToPut += "<h3>Description:</h3><p>&nbsp;</p><p>&nbsp;</p>"
	$TextTemplateToPut += "<h3>Estimated Time:</h3><ul><li>&nbsp;</li></ul>"
	$TextTemplateToPut += "<h3>Person in charge:</h3><ul><li>&nbsp;</li></ul>"
	$TextTemplateToPut += "<h3>Starting time:</h3><ul><li>&nbsp;</li></ul><p>&nbsp;</p>"

	$PageTitleToUpdate = -join($pageFileTitle, " - ", $PageTitleShort)
	$ParentNodeID = 0
	$PageNodeID = 0

	Write-host " "
	Write-host " -------------------------------------------------------------------------------------------- " -ForegroundColor green
	Write-host " ---- START THE PAGE CREATION:", $pageFileTitle, "-", $pageFilename -ForegroundColor green
	Write-host " ---- Page Title Full:", $PageTitleFull -ForegroundColor green
	Write-host " ---- Page Title Short:",  $PageTitleShort -ForegroundColor green
	Write-host " -------------------------------------------------------------------------------------------- " -ForegroundColor green

	# ---------------------------------------------------------------------------------------------------------------
	#connect to the web site using the stored credentials
	Write-host " "
	Write-host " -------------------------------------------------------------------------------------------- " -ForegroundColor green
	Write-host " ---- CONNECT THE SITE --- " -ForegroundColor green
	Write-host "   CONNECTED SITE:", $HelpAndQAUrl  -ForegroundColor Yellow
	Connect-PnPOnline -Url $HelpAndQAUrl -Credential $cred
	Write-host " -------------------------------------------------------------------------------------------- " -ForegroundColor green
	$checkpage = Get-PnPClientSidePage -Identity $pageFilename -ErrorAction SilentlyContinue

	if($checkpage -eq $null)
		Write-Host "  >>>  Page does not exist or is not modern"
		$page = Add-PnPClientSidePage -Name $pageFilename -LayoutType "Article"
		Write-Host "  >>> We have a modern page present"
		$page = $checkpage
	#Add text webpart to page
	Add-PnPClientSideText -Page $page -Text $TextTemplateToPut
	Set-PnPClientSidePage -Identity $page -LayoutType "Article" -Title $PageTitleToUpdate

	$page = Get-PnPClientSidePage -Identity $pageFilename -ErrorAction SilentlyContinue

	Write-host "   ==>> PAGE HEADERS ImageServerRelativeUrl:", $page.PageHeader.ImageServerRelativeUrl  -ForegroundColor Green
	$ctx = Get-PnPContext
	Write-host "   ==>> WEB Relative URL:", $ctx.Web.ServerRelativeUrl  -ForegroundColor Yellow
	$mySiteRelativeURL = $ctx.Web.ServerRelativeUrl
	$myPageRelativeURL = -join($mySiteRelativeURL, "/", $page.PagesLibrary, "/", $pageFilename)
	Write-host "   ==>> PAGE Relative URL:", $myPageRelativeURL  -ForegroundColor Yellow

	$page.PageHeader.ImageServerRelativeUrl = $mySiteRelativeURL +"/SiteAssets/$($PageTopic).JPG"

	$AllQuicklaunchNodes = Get-PnPNavigationNode

	foreach($MyNode in $AllQuicklaunchNodes)
		if($MyNode.Title -eq $PageTopic)
			Write-host "   ->>>>  PARENT - MenuNode Title:", $MyNode.Title, "- ID:", $MyNode.ID  -ForegroundColor Yellow
			$ParentNodeID = $MyNode.ID
			Write-host "   - MenuNode Title:", $MyNode.Title, "- ID:", $MyNode.ID  -ForegroundColor Green
	if($ParentNodeID -eq 0)
		Write-host "               ===>>>>  TOPIC LINK NOT EXIST, Need to create it"  -ForegroundColor Red
		$AddMyNode = Add-PnPNavigationNode -Title $PageTopic -Url $mySiteRelativeURL -Location "QuickLaunch"
		$ParentNodeID = $AddMyNode.Id
	$Topicnodes = Get-PnPNavigationNode -Id $ParentNodeID	
	foreach($MyPageNode in $Topicnodes.Children)
		if($MyPageNode.Title -eq $PageTitleToUpdate)
			Write-host "            ->>>>  PAGE NODE EXIST- MenuNode Title:", $MyPageNode.Title, "- ID:", $MyPageNode.ID  -ForegroundColor Red
			$PageNodeID = $MyPageNode.ID
			Write-host "            ->>>>  PAGE NODE - MenuNode Title:", $MyPageNode.Title, "- ID:", $MyPageNode.ID  -ForegroundColor green
	if($PageNodeID -eq 0)
		$AddMyNode = Add-PnPNavigationNode -Title $PageTitleToUpdate -Url $myPageRelativeURL -Location "QuickLaunch" -Parent $ParentNodeID


Visual Result:

The home page of the Help Site can be configured with the Modern News WebPart to display the last published help pages:Site Home Page with the last publishedSite Home Page with the last published

And for one of the page, you can retrieve the content with that basic display:Help page with the content loadedHelp page with the content loaded

Post execution step: 

When the site is created and after the task owner enrich the content into the pages, you can use that other script to update the Page Metadata setting the author field and publication date adapted with real information of the Page Owner and not your admin account:



This script was used for many internal sites to create some site with hundreds of pages.


Romelard Fabrice


French version:

2 Replies
Hi Fabrice

I found your article interessant. As workaround againt the limitations of the news web part and the highlighted content web part, we were thinking about getting all news from every other site collections and then create a news link (repost page) for all of them in the main intranet portal page library. This will allow us using the news webpart with page properties filtering options and the "AND" mechanismus to filter the news (as we know that the "AND" is badly implemented in the highlighted content web part).
Of course, we will have to run regularly a script checking for newly created news, then create a news link into the main portal site collection, including the copy of the page properties. Till to this point, everything fine. I haven't tested but i don't see why it wouldn't work.
Now to the point: I was wondering about the news source "recommended for current user" in the news webpart configuration. Do you think that we will loose the "Following site" information if the transform the news of the source site collections into a repost page in the main portal site collection? I'm even not sure if "recommended for current user" is meaning getting the site i'm Following. Do you have more informations about it ? It's sometimes very difficult to get real specifications from MS about the new functions on SPO.
Merci d'avance

(PS: tu peux répondre en français si ça t'arrange. J'ai juste posté en anglais pour la communauté)

I'm not sure to understand the point

The best way is to send me a direct email and I will try to respond to (in French better)