SharePoint Modern pages allow users to customise the look and feel by adding a banner image at the top of the page. This is an often task that usually users do manually, but what happens if we're creating a lot of pages in different environments, such as Development QA, UAT and Live? In my case, I usually need to create SharePoint environments with default data from scratch.
Do we need to manually set the banner image on each page for each environment? Thanks to PowerShell, no we don't. We can run a PowerShell scripts that reads the information of each page, i.e: what image goes in what page, and then sets that image automatically. Obviously we should have created the pages and uploaded the images in a previous step. We can do this with another PowerShell script which we will talk about soon in another post.
So, with all of this in mind, the first step as said, is to create the Modern pages in SharePoint, and upload the images to some assets library. Then, we need to identify somehow, what banner image goes in what page. For this, I have the following dictionary:
$banners = @{"Welcome.aspx" = "home.png";
"Employee Images.aspx" = "employee-images.png";
"Publications.aspx" = "publications.png";
"Event Materials.aspx" = "event-materials.png";
"Advertisements.aspx" = "advertisements.png";
"Visual Guidelines.aspx" = "visual-guidelines.png";
"Stock Images.aspx" = "stock-images.png";
"Templates.aspx" = "templates.png";
"Contact.aspx" = "contact.png";
"Imprint.aspx" = "imprint.png";
"Cart.aspx" = "basket.png";
"Account.aspx" = "account.png";
};
As you can see, basically I'm matching each Modern page with an image. In my case I have a separate script file (banners.ps1) with the banners information in a dictionary-way, so I can have as many environments as I need, and I just need to reference the proper banners.ps1 script from the main script as we'll see later. You can have that dictionary embedded in the script if you prefer it. I also have a "secrets" file (secrets.ps1) that contains the tenant and login details:
$properties = @{};
$properties.O365Tenant = "mytenant";
$properties.SPSite = "/sites/mysitename";
$properties.AppsCatalog = "/sites/myappcatalog";
$properties.UserName = "xxxxx@mytenant.onmicrosoft.com";
$properties.Password = 'xxxxxxxxxx';
$secrets = New-Object -TypeName PSObject -Property $properties;
And finally, here you have the PowerShell script that will set the banner image on each Modern page, as per the information in the banners.ps1 and the tenant and login details from the secrets.ps1.
param ([string]$env = "dev", [string]$images = "Pictures", [string]$pages = "SitePages");
# Run secrets
$secretsScript = (Split-Path $MyInvocation.MyCommand.Definition) + ("\\\\\\\\\\\\\\\\", $env, "\\\\\\\\\\\\\\\\" -join "") + "secrets.ps1";
. $secretsScript;
#Run banners
$bannersScript = (Split-Path $MyInvocation.MyCommand.Definition) + ("\\\\\\\\\\\\\\\\", $env, "\\\\\\\\\\\\\\\\" -join "") + "banners.ps1";
. $bannersScript;
# Getting context
$credentials = new-object -typename System.Management.Automation.PSCredential -argumentlist $secrets.UserName, ($secrets.Password | ConvertTo-SecureString -AsPlainText -Force)
$webUrl = "https://{0}.sharepoint.com{1}/" -f $secrets.O365Tenant, $secrets.SPSite;
Write-Host "[1] Connecting to " -NoNewline
Write-Host $("{0} " -f $webUrl) -NoNewline -ForegroundColor Yellow;
Connect-PnPOnline -url $webUrl -Credential $credentials;
$guidSite = (Get-PnPSite -Includes ID).ID.ToString();
$guidWeb = (Get-PnPWeb).ID.ToString();
Write-Host "OK!" -ForegroundColor Green;
#Getting pages
Write-Host "[2] Getting Client Side (Modern) Pages from " -NoNewline
Write-Host $("{0} " -f $pages) -NoNewline -ForegroundColor Yellow;
Write-Host "library..." -NoNewline;
$clientSidePages = (Get-PnPListItem -List $pages -Fields ID,Title,UniqueId,FileLeafRef) | %{new-object PSObject -Property @{Id=$_["ID"];UniqueId=$_["UniqueId"];FileLeafRef=$_["FileLeafRef"]; Title=[uri]::EscapeUriString($_["Title"]);}};
Write-Host "OK!" -ForegroundColor Green;
#Getting banners
Write-Host "[3] Getting banner images from " -NoNewline
Write-Host $("{0} " -f $images) -NoNewline -ForegroundColor Yellow;
Write-Host "library..." -NoNewline;
$bannerImages = (Get-PnPListItem -List $images -Fields ID,UniqueId,FileLeafRef,FileRef) | %{new-object PSObject -Property @{Id=$_["ID"];UniqueId=$_["UniqueId"];FileLeafRef=$_["FileLeafRef"];FileRef=$_["FileRef"]}};
$guidList = (Get-PnPList -Identity $images).ID.ToString();
Write-Host "OK!" -ForegroundColor Green;
Write-Host "[4] Setting banners:"
$counter = 1;
$banners.Keys | ForEach-Object {
$pageKey = $_;
$bannerValue = $banners[$pageKey];
Write-Host $(" [{0}/{1}] {2}|{3}" -f $counter++, $banners.Keys.Count, $pageKey, $bannerValue) -NoNewline -ForegroundColor Yellow;
$clientSidePage = $clientSidePages | ?{$_.FileLeafRef -eq $pageKey;}
if ($clientSidePage -ne $null) {
$bannerImage = $bannerImages | ?{$_.FileLeafRef -eq $bannerValue;}
if ($bannerImage -ne $null) {
$BannerImageUrlValue = $("https://{0}.sharepoint.com/_layouts/15/getpreview.ashx?guidSite={1}&guidWeb={2}&guidFile={3}, https://{0}.sharepoint.com/_layouts/15/getpreview.ashx?guidSite={1}&guidWeb={2}&guidFile={3}" -f $secrets.O365Tenant, $guidSite, $guidWeb, $bannerImage.UniqueId);
$LayoutWebpartsContentValue = $("<div><div data-sp-controldata=""%7B%22id%22:%22{0}%22,%22instanceId%22:%22{0}%22,%22title%22:%22Title%20Region%22,
%22description%22:%22Title%20Region%20Description%22,%22serverProcessedContent%22:
%7B%22htmlStrings%22:%7B%7D,%22searchablePlainTexts%22:%7B%7D,%22imageSources%22:
%7B%22imageSource%22:%22{1}%22%7D,%22links%22:%7B%7D%7D,%22dataVersion%22:%221.2%22,
%22properties%22:%7B%22title%22:%22{2}%22,%22imageSourceType%22:2,%22siteId%22:%22{3}%22,
%22webId%22:%22{4}%22,%22listId%22:%22%7B{5}%7D%22,%22uniqueId%22:%22{6}%22,
%22translateX%22:50,%22translateY%22:50%7D%7D"" data-sp-canvascontrol="""">
</div></div>" -f "cbe7b0a9-3504-44dd-a3a3-0e5cacd07788", $bannerImage.FileRef, $clientSidePage.Title, $guidSite, $guidWeb, $guidList, $bannerImage.UniqueId);
$oItem = Set-PnPListItem -List $pages -Id $clientSidePage.ID -Values @{"BannerImageUrl" =$BannerImageUrlValue; "LayoutWebpartsContent"= $LayoutWebpartsContentValue} -EA SilentlyContinue -EV ProcessError;
if ($ProcessError) {Write-Host " ERROR!" -ForegroundColor Red; $ProcessError}
else {Write-Host " OK!" -ForegroundColor Green;}
}
else {Write-Host " ERROR: " -ForegroundColor Red -NoNewline; Write-Host $("""{0}"" picture not found in ""{1}"" library" -f $bannerValue, $images)}
}
else {Write-Host " ERROR: " -ForegroundColor Red -NoNewline; Write-Host $("""{0}"" page not found in ""{1}"" library" -f $pageKey, $pages)}
};
Write-Host "[5] Provisioning completed" -ForegroundColor Green
So, basically we are doing these steps:
- Run the secrets.ps1 script to get the tenant and login details.
- Run the banners.ps1 script to the matching between Modern pages and banner images.
- Getting the context with SharePoint according to the secrets.ps1 data.
- Retrieving the Modern pages according to the banners.ps1 data.
- Retrieving the banner images according to the banners.ps1 data. There is a configuration parameter to specify in which assets library we are storing the banner images.
- Finally, we iterate through each Modern page and set the properties "BannerImageUrl" and "LayoutsWebpartsContent" with the proper value and format.
And it does! I hope you find this useful and help you saving some time when building different SharePoint environments/tenants with default data.