SharePoint REST API PowerShell Update Page Content

Goal: Ability to update SharePoint page content via REST API

Prerequisites

Set Azure $tenant_id (can be retrieved from Azure Active Directory) and $sharepoint website subdomain

$tenant_id='xxxxxxxxxxxx'
$sharepoint='YOUR_SHAREPOINT_SITE'

App registration

https://YOUR_SHAREPOINT_SITE.sharepoint.com/_layouts/15/appregnew.aspx

set $client_id and $client_secret somewhere

$client_id='xxxxxxxxxxxx'
$client_secret='xxxxxxxxxxxx'

Notes:

  • generate both client identifier and secret
  • links for web site and callback url does not matter

App invitation

https://YOUR_SHAREPOINT_SITE.sharepoint.com/_layouts/15/appinv.aspx

<AppPermissionRequests AllowAppOnlyPolicy="true">
<AppPermissionRequest Scope="http://sharepoint/content/sitecollection" Right="FullControl"/>
</AppPermissionRequests>

Notes:

  • put your client identifier and press lookup button whole form should be filled in automatically
  • modify permissions request to desired one

PowerShell

Retrieve Access Token

Before moving further we need to exchange our application credentials to access token which will be used in all subsequent requests

$res = Invoke-RestMethod -Method Post -Uri "https://accounts.accesscontrol.windows.net/$tenant_id/tokens/OAuth/2" -ContentType "application/x-www-form-urlencoded" -Body @{
    grant_type="client_credentials"
    client_id="$client_id@$tenant_id"
    client_secret=$client_secret
    resource="00000003-0000-0ff1-ce00-000000000000/$sharepoint.sharepoint.com@$tenant_id"
}

$headers = @{
    Authorization = "Bearer $($res.access_token)"
    Accept = "application/json;odata=verbose"
}

Enumerate Lists

SharePoint site consists of lists of items, there are lists of files, documents, forms, what ever else and desired website pages

Invoke-RestMethod "https://$sharepoint.sharepoint.com/_api/web/lists" -Headers $headers | Select-Object -ExpandProperty d | Select-Object -ExpandProperty results | Select-Object Id, Title

# 976b19d8-52db-489d-a6e3-2557a1520c79 Сторінки сайту # Site Pages
$guid='976b19d8-52db-489d-a6e3-2557a1520c79'

Note that depending on your locale titles may differ, thats why we are going to use GUID instead

Enumerate List Items

Each list consists of items

(Invoke-RestMethod "https://$sharepoint.sharepoint.com/_api/Web/Lists(guid'$guid')/Items?select=Id,Title" -Headers $headers | ConvertFrom-Json -AsHashtable).d.results | Select-Object id, title

# 1 Home page
# 2 mac
$id=2

Get List Item By Id

$page = Invoke-RestMethod "https://$sharepoint.sharepoint.com/_api/Web/Lists(guid'$guid')/items($id)" -Headers $headers | ConvertFrom-Json -AsHashtable
$page = $page.d
$page.Title
$page.CanvasContent1

CanvasContent1 holds page content, take a note that it is usual HTML enriched by SharePoint metadata, to avoid broken pages create template first, save it, and then update content from it

Update Page Content

$h = @{
    "Authorization" = "Bearer $($res.access_token)"
    "Accept"        = "application/json;odata=verbose"
    "Content-Type"  = "application/json"
    "If-Match"      = "*"
}

Invoke-RestMethod -Method Patch "https://$sharepoint.sharepoint.com/_api/Web/Lists(guid'$guid')/items($id)" -Headers $h -Body (@{ CanvasContent1 = @"
<div>
<div data-sp-canvascontrol="" data-sp-canvasdataversion="1.0" data-sp-controldata='&#123;"controlType"&#58;4,"id"&#58;"9db886a5-6767-498a-856a-76852dfb4a42","position"&#58;&#123;"zoneIndex"&#58;2,"sectionIndex"&#58;1,"controlIndex"&#58;1,"layoutIndex"&#58;1&#125;,"emphasis"&#58;&#123;&#125;,"zoneGroupMetadata"&#58;&#123;"type"&#58;0&#125;,"addedFromPersistedData"&#58;true&#125;'>
<p>mac was here</p>
</div>
</div>
"@} | ConvertTo-Json)

Here we can start building our page by replacing <p>mac was here</p> with almost anything we want

Publish Page

Invoke-RestMethod -Method Post "https://$sharepoint.sharepoint.com/_api/web/GetFileByServerRelativeUrl('/SitePages/mac.aspx')/Publish()" -Headers $headers

Note that after modifying content you also need to publish page, also did not bother of finding a way to do it via page identifier and just used relative url found somethere in internet

Demo

Here is an use case, suppose we want page describing Kubernetes cluster, and want it to be always up to date

So we need to prepare some cronjob which will grab data about cluster, form HTML from it and update page in SharePoint - profit

$nodes = kubectl get no -o json | ConvertFrom-Json | Select-Object -ExpandProperty items | Select-Object @{n='name';e={ $_.metadata.name }}, @{n='group';e={ $_.metadata.labels.'node.deckhouse.io/group' }}, @{n='cpu';e={ $_.status.allocatable.cpu }}, @{n='ram';e={ [Math]::Ceiling($_.status.allocatable.memory / 1Gb) }}

$nodes # array of nodes (name, role, cpu, ram)

$table = $nodes | ConvertTo-Html -Fragment # create html table from a given array

$table = $table.Replace('<table', '<table width="100%"') # make our table fill whole page

Invoke-RestMethod -Method Patch "https://$sharepoint.sharepoint.com/_api/Web/Lists(guid'$guid')/items($id)" -Headers $h -Body (@{ CanvasContent1 = @"
<div>
<div data-sp-canvascontrol="" data-sp-canvasdataversion="1.0" data-sp-controldata='&#123;"controlType"&#58;4,"id"&#58;"9db886a5-6767-498a-856a-76852dfb4a42","position"&#58;&#123;"zoneIndex"&#58;2,"sectionIndex"&#58;1,"controlIndex"&#58;1,"layoutIndex"&#58;1&#125;,"emphasis"&#58;&#123;&#125;,"zoneGroupMetadata"&#58;&#123;"type"&#58;0&#125;,"addedFromPersistedData"&#58;true&#125;'>
<h1>Kubernetes Cluster Nodes</h1>
$table
</div>
</div>
"@} | ConvertTo-Json)

Invoke-RestMethod -Method Post "https://$sharepoint.sharepoint.com/_api/web/GetFileByServerRelativeUrl('/SitePages/mac.aspx')/Publish()" -Headers $headers