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='{"controlType":4,"id":"9db886a5-6767-498a-856a-76852dfb4a42","position":{"zoneIndex":2,"sectionIndex":1,"controlIndex":1,"layoutIndex":1},"emphasis":{},"zoneGroupMetadata":{"type":0},"addedFromPersistedData":true}'>
<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='{"controlType":4,"id":"9db886a5-6767-498a-856a-76852dfb4a42","position":{"zoneIndex":2,"sectionIndex":1,"controlIndex":1,"layoutIndex":1},"emphasis":{},"zoneGroupMetadata":{"type":0},"addedFromPersistedData":true}'>
<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