Six Ways to Edit SharePoint Pages with PowerShell
Six ways, really? Certainly!
Although I will only dive into the two most prominent ways because they are the most recent and most recommended, but I wanted to show the long history of API implementations that SharePoint has known and still maintains up until today. That’s ‘backward compatibility’ for ya!
With the introduction of the new MS Graph sitePages API (in beta), Microsoft added a great way to manipulate modern SharePoint pages in addition to the already present methods.
The two methods I will actually implement in this post are:
- SharePoint REST API v2 (using Graph endpoint translations)
- Microsoft Graph API the beta version specifically (using direct Graph endpoints)
There also exist the three ‘old school’ CSOM API implementations that I must mention but will not get into because they are not being actively developed. In my opinion they also constitute a pending? ‘Technical Dept’ if they are still used in any organization.
- the ‘.NET Framework redistributable assemblies’ implementation
- example: SharePoint Online Management Shell
- example: PnP PowerShell (Archived)
- the ‘SharePoint REST API v1’ implementation
- example: PnP PowerShell (Archived)
- the ‘JavaScript library (JSOM)’ implementation
- no PowerShell examples
Finally I want to mention one really old, deprecated, but still operational ‘ASP.NET (asmx) web services’ SOAP API implementation. Just enter https://<tenant>.sharepoint.com/_vti_bin/lists.asmx
and you know what I am talking about.
I was wondering if authentication still worked against this API, since Azure AD had a lot of changes over the years, as did the OAuth authorization protocol and it seems it does. Remarkable!
Maybe it is time Microsoft brought this API down, just to make the service a bit safer? I am getting a CAML shiver even writing this. Just a suggestion…
Six Ways to Edit SharePoint Pages with PowerShell
Preparation
I prefer leveraging API’s through PowerShell, as it still is the most flexible tool for the job that can be handled by both IT Pros and Developers alike. In this case both preferred APIs are handily available with the very capable PnP.PowerShell module. So install that module preferably on top of PowerShell 7+.
NOTE: Alternatively for Method 2 below you could also use the Microsoft Graph PowerShell SDK module or my personal favorite: the lean and mean EasyGraph module.
ATTENTION: If you get any errors running the scripts below on more ‘complex’ pages and are running the current release version (1.12.0) or below of the PnP.PowerShell module, you might want to consider installing a nightly version of the module. A fix was committed (thanks Gautam Sheth!).
In any SharePoint Site you would like, just create and publish a single modern page with a single section containing a single ‘Text’ WebPart containing the text ‘I want more.’.
You should have at least contribute permissions for the library and no further authoring settings on the page library should be active, just to rule out any ‘strange-ness’.
A simple SharePoint webpart page
Open a terminal and make a connection with SharePoint (and the Graph) running Connect-PnPOnline
. Put the site URL in a variable called $SiteUrl
.
NOTE: While using The Graph you can connect to the root site of your tenant (if you have permissions) and do all operations from there.
Now we are going to update the text on this page. You could imagine a scenario where someone in your organization would publish pages with some ‘placeholder text’ which can be replaced after running a script like the ones that follow.
Method 1: SharePoint REST API v2
NOTE: The API endpoints used in this example can also be used with standard SharePoint REST connector in Power Automate.
Please pay attention to the ‘ONE TIME
’ remarks (there are two per method) in the scripts below, you will have to fill in something yourself.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
######################
# Fetch Page Details #
######################
## place the GET call for sites
$uri = "https://$SiteUrl/_api/v2.0/sites?`$select=id,webUrl,title"
$result = Invoke-PnPSPRestMethod -Url "$($uri)" -Method "GET" -ContentType "application/json;odata=nometadata" -ErrorAction Stop
### ONE TIME: print the '$result' variable and put the desired site id in the '$siteid' variable below
$siteid = "$SiteUrl,11111111-1111-1111-1111-111111111111,22222222-2222-2222-2222-22222222222"
## place the GET call for pages passing the site id
$uri = "https://$SiteUrl/_api/v2.0/sites('$siteid')/pages?`$select=id,title"
$result = Invoke-PnPSPRestMethod -Url "$($uri)" -Method "GET" -ContentType "application/json;odata=nometadata" -ErrorAction Stop
### ONE TIME: print the '$result' variable and put the desired page id in the '$pageid' variable below
$pageid = "33333333-3333-3333-3333-333333333333"
## place the GET call for page canvas passing the site id and page id
$uri = "https://$SiteUrl/_api/v2.0/sites('$siteid')/pages/$pageid/?`$select=id,title&`$expand=canvasLayout"
$result = Invoke-PnPSPRestMethod -Url "$($uri)" -Method "GET" -ContentType "application/json;odata=nometadata" -ErrorAction Stop
#####################
# Do business logic #
######################
## Clean up the page definition to make it ready for manipulation and to write back at some point
$bodyJson = $result | ConvertTo-Json -Depth 100
$newBody = @()
$matchStr = "`@odata|^\s\s`"id`":"
foreach ($line in $($bodyJson -split "\n")) {
if ($line -notmatch $matchStr) {
$newBody += $line
}
}
## Replace the text
$newbody = $newBody -replace 'more','much more'
## Convert to a single string
$body = $newBody -join '' #IMPORTANT: Convert the result to a single string
###################
# Update the Page #
###################
## place the PATCH call to update page contents
$uri = "https://$SiteUrl/_api/v2.0/sites('$siteid')/pages/$pageid"
$result = Invoke-PnPSPRestMethod -Url "$($uri)" -Method "PATCH" -Content $body -Raw -ContentType "application/json;odata=nometadata" -ErrorAction Stop
####################
# Publish the Page #
####################
## place the POST call to publish
$uri = "https://$SiteUrl/_api/v2.0/sites('$siteid')/pages/$pageid/oneDrive.publish"
$result = Invoke-PnPSPRestMethod -Url "$($uri)" -Method "POST" -ContentType "application/json" -ErrorAction Stop
Method 2: Microsoft Graph beta API
WARNING: The API endpoints (prefixed with https://graph.microsoft.com/) used in this example can only be used with premium HTTP connector in Power Automate.
Please pay attention to the ‘ONE TIME
’ remarks (there are two per method) in the scripts below, you will have to fill in something yourself.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
######################
# Fetch Page Details #
######################
## place the GET call for sites
$uri = "beta/sites?`$select=id,webUrl,displayName"
$result = Invoke-PnPGraphMethod -Url "$($uri)" -Method "GET" -ContentType "application/json;odata=nometadata" -ErrorAction Stop
### ONE TIME: print the '$result' variable and put the desired site id in the '$siteid' variable below
$siteid = "$SiteUrl,11111111-1111-1111-1111-111111111111,22222222-2222-2222-2222-22222222222"
## place the GET call for pages passing the site id
$uri = "beta/sites/$siteid/pages?`$select=id,title"
$result = Invoke-PnPGraphMethod -Url "$($uri)" -Method "GET" -ContentType "application/json;odata=nometadata" -ErrorAction Stop
### ONE TIME:print the '$result' variable and put the desired page id in the '$pageid' variable below
$pageid = "33333333-3333-3333-3333-333333333333"
## place the GET call for page canvas passing the site id and page id
$uri = "beta/sites/$siteid/pages/$pageid/?`$select=id,title&`$expand=canvaslayout"
$result = Invoke-PnPGraphMethod -Url "$($uri)" -Method "GET" -ContentType "application/json;odata=nometadata" -ErrorAction Stop
#####################
# Do business logic #
######################
## Clean up the page definition to make it ready for manipulation and to write back at some point
$bodyJson = $result | ConvertTo-Json -Depth 100
$newBody = @()
$matchStr = "`@odata|^\s\s`"id`":"
foreach ($line in $($bodyJson -split "\n")) {
if ($line -notmatch $matchStr) {
$newBody += $line
}
}
## Replace the text
$newbody = $newBody -replace 'more','much more'
## Convert to a single string
$body = $newBody -join '' #IMPORTANT: Convert the result to a single string
###################
# Update the Page #
###################
## place the PATCH call to update page contents
$uri = "beta/sites/$siteid/pages/$pageid"
$results = Invoke-PnPGraphMethod -Url "$($uri)"-Method "PATCH" -Content $body -Raw -ContentType "application/json" -ErrorAction Stop
####################
# Publish the Page #
####################
## place the POST call to publish
$uri = "beta/sites/$siteid/pages/$pageid/publish"
$result = Invoke-PnPGraphMethod -Url "$($uri)" -Method "POST" -ContentType "application/json" -ErrorAction Stop
Result
After running these scripts you should see the text on the page being replaced. After repeated runs it would spell ‘I want much much much more.’. In my case I need a more Sashimi now. Cheers!