Consuming the SharePoint Online REST API from PowerShell: Part 2

Introduction

A while back ago we already discussed how to  consume SharePoint Online (SPO) REST in PowerShell. Here is a brief recap:

This time I would like demonstrate another approach, in particular  how PowerShell can gain authorization to SharePoint resources by passing an access token to SharePoint with each HTTP request. To issue an access token from Microsoft Azure Access Control Service (ACS) that allows the app access to the resources in the SharePoint tenancy we will implement the corresponding PowerShell function. Let’s get started.

Getting Access Token from Microsoft Azure Access Control Service

The Get-SPOAccessToken function demonstrates how to obtain the access token from a Microsoft Azure Access Control Service (ACS) account that is associated with the customer’s Microsoft Office 365 tenancy:

<#
.Synopsis
Obtain an app-only access token from ACS.
.DESCRIPTION
Retrieves an app-only access token from ACS to call the specified principal
at the specified targetHost. The targetHost must be registered for target principal. If specified realm is
null, the "Realm" setting in web.config will be used instead
.EXAMPLE
Get-SPOAccessToken -Url "https://contoso.sharepoint.com/_api/web&quot; -ClientId "" -ClientSecret ""
#>
Function Get-SPOAccessToken([string]$ClientId,[string]$ClientSecret,[Uri]$WebUri){
$SharePointPrincipal = "00000003-0000-0ff1-ce00-000000000000"
$realm = GetRealmFromTargetUrl TargetApplicationUri $WebUri
$accessToken = GetAppOnlyAccessToken ClientId $ClientId ClientSecret $ClientSecret TargetPrincipalName $SharePointPrincipal TargetHost $WebUri.Authority TargetRealm $realm
return $accessToken.access_token
}
function GetRealmFromTargetUrl([Uri]$TargetApplicationUri)
{
$url = $WebUrl + "/_vti_bin/client.svc"
$headers = @{}
$headers.Add('Authorization','Bearer')
try {
$response = Invoke-WebRequest Uri $TargetApplicationUri Headers $headers Method Get
}
catch [Net.WebException] {
$authResponseHeader = $_.Exception.Response.Headers["WWW-Authenticate"]
#$bearerKey = "Bearer realm="
$bearer = $authResponseHeader.Split(",")[0]
$targetRealm = $bearer.Split("=")[1]
return $targetRealm.Substring(1,$targetRealm.Length2)
}
return $null
}
Function GetAppOnlyAccessToken([string]$ClientId,[string]$ClientSecret,[string]$TargetPrincipalName,[string]$TargetHost,[string]$TargetRealm)
{
$resource = GetFormattedPrincipal PrincipalName $TargetPrincipalName HostName $TargetHost Realm $TargetRealm
$ClientId = GetFormattedPrincipal PrincipalName $ClientId Realm $TargetRealm
$contentType = 'application/x-www-form-urlencoded'
$stsUrl = GetSecurityTokenServiceUrl Realm $TargetRealm
$oauth2Request = CreateAccessTokenRequestWithClientCredentials ClientId $ClientId ClientSecret $ClientSecret Scope $resource
$oauth2Response = Invoke-RestMethod Method Post Uri $stsUrl ContentType $contentType Body $oauth2Request
return $oauth2Response
}
Function GetSecurityTokenServiceUrl([string]$Realm)
{
return "https://accounts.accesscontrol.windows.net/$Realm/tokens/OAuth/2"
}
Function CreateAccessTokenRequestWithClientCredentials([string]$ClientId,[string]$ClientSecret,[string]$Scope)
{
$oauth2Request = @{
'grant_type' = 'client_credentials';
'client_id' = $ClientId;
'client_secret' = $ClientSecret;
'scope' = $Scope;
'resource' = $Scope
}
return $oauth2Request
}
function GetFormattedPrincipal([string]$PrincipalName, [string]$HostName, [string]$Realm)
{
if ($HostName)
{
return "$PrincipalName/$HostName@$Realm"
}
return "$PrincipalName@$Realm"
}

Get-SPOAccessToken function is intended for requesting an access token from Azure ACS, it accepts  Client Id and Client Secret parameters that are generated while App registration with Azure ACS (see “How to register App” for a more details).

Using Invoke-RestMethod in Office 365

Invoke-SPORestMethod function demonstrates how to  include the access token to make a REST API call  to SharePoint, passing the OAuth access token in the HTTP Authorization header:

.\Get-SPOAccessToken.ps1
<#
.Synopsis
Sends an HTTP or HTTPS request to a SharePoint Online REST-compliant web service.
.DESCRIPTION
This function sends an HTTP or HTTPS request to a Representational State
Transfer (REST)-compliant ("RESTful") SharePoint Online web service.
.EXAMPLE
Invoke-SPORestMethod -Uri "https://contoso.sharepoint.com/_api/web&quot; -AccessToken $accessToken
#>
Function Invoke-SPORestMethod()
{
Param(
[Uri]$Uri,
[Object]$Body,
[Hashtable]$Headers,
[Microsoft.PowerShell.Commands.WebRequestMethod]$Method = [Microsoft.PowerShell.Commands.WebRequestMethod]::Get,
[string]$AccessToken
)
$contentType = 'application/json;odata=verbose'
if(-not $Headers) {
$Headers = @{}
}
$Headers["Accept"] = "application/json;odata=verbose"
$Headers.Add('Authorization','Bearer ' + $AccessToken)
Invoke-RestMethod Method $Method Uri $Uri ContentType $contentType Headers $Headers Body $Body
}

Examples

The following example demonstrates how to retrieve List resource properties:

param (
$ClientId,
$ClientSecret,
$WebUri,
$ListTitle
)
.\Get-SPOAccessToken.ps1
Function Get-SPOList([Uri]$WebUri,[string]$ClientId,[string]$ClientSecret,[Uri]$ListTitle)
{
$accessToken = Get-SPOAccessToken ClientId $ClientId ClientSecret $ClientSecret WebUri $WebUri
$endpointUri = "$WebUri/_api/web/lists/getbytitle('$ListTitle')"
$data = Invoke-SPORestMethod Uri $endpointUri AccessToken $accessToken
return $data.d
}
Get-SPOList WebUri $WebUri ClientId $ClientId ClientSecret $ClientSecret ListTitle $ListTitle

view raw
Get-SPOList.ps1
hosted with ❤ by GitHub

But before running the specified script we need to perform one more step in order to grant permissions to the app principal otherwise the unauthorized error will occur as shown on picture below:
Rest401

  • Navigate to http://<SharePointWebsite>/_layouts/15/AppInv.aspx
  • Look up the app based on the Client ID that you just generated and click Lookup, it will find the app principal.  Then paste the AppPermissionRequests XML into the Permissions text box and click CreateAppInv
    Once you click Create, the Trust dialog will appear, click Trust
    AppInv_Trust

That’s it.  Now, after executing the specified script, the output will look like shown below

Get-SPOList-Results

How to register App

Below is provided a step by step instruction how to register an App, for a complete guide follow this article:

  • To create the app identity, navigate to http://<SharePointWebsite>/_layouts/15/AppRegNew.aspx on the tenancy or farm
  • Enter values for the form fields as shown below on picture
    AppNewReg_NewForm
    where
    App ID: App ID, also known as client ID, is a GUID that can be generated (when you click Generate) or pasted into AppRegNew.aspx. The value must be unique for each app, and must be lower case
    App Secret: The app secret, also known as the client secret, is an opaque string. It is generated on the AppRegNew.aspx page by using the Generate button. The following is an example of an app secret: Ywjaoz7DJBGhoLQ2t0IbVCA5pfqqI722ZIVt+ENLk0g=
    Title: Choose your own user-friendly title; for example, PowerShell Console
    App Domain:
    The host name of the remote component of the app for SharePoint
    Redirect URI: The endpoint in your remote application or service to which ACS sends an authentication code
  • Click Create on the form. The page will reload and show a confirmation of the values you entered as shown on picture below
    AppNewReg
  • Save Client Id and Client Secret values. After that you could verify whether Get-SPOAccessToken function returns access token. The picture below shows  the output after executing the command:
    Get-SPOAccessToken -ClientId “1523cf05-b437-4e73-9ad1-a652da8f2ae5” -ClientSecret “Ywjaoz7DJBGhoLQ2t0IbVCA5pfqqI722ZIVt+ENLk0g=” -WebUri “https://contoso.sharepoint.com/&#8221;
    ISE_AccessToken

References

Working with folders and files via SharePoint 2013 REST in PowerShell

Overview

In the previous post we’ve already discussed how to perform CRUD operations by sending HTTPS requests to SharePoint RESTful web services in PoweShell. The Invoke-RestSPO function was introduced for that purpose since  Invoke-RestMethod cmdlet does not support claims based authentication and it makes this cmdlet impossible to use in O365 and SharePoint Online scenarios.

This time I am going to demonstrate how  to perform basic create, read, update, and delete (CRUD) operations on folders and files with the SharePoint 2013 REST interface using Invoke-RestSPO function.

Explore the REST service files and folder syntax

SharePoint 20123 Files and Folders REST syntax

 

Working with folders

Folder resource: represents a folder on a SharePoint Web site

Endpoint URI: http://<site url>/_api/web/getfolderbyserverrelativeurl(‘/<folder name>‘)

Supported HTTP methods:  GET  |  POST  |  DELETE  |  MERGE  |  PUT

The following examples demonstrates how to perform basic CRUD operations with Folder resource.

 

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")
. ".\Invoke-RestSPO.ps1"
<#
.SYNOPSIS
Retieve Folder
.DESCRIPTION
Read Folder operation via SharePoint 2013 REST API
url: http://site url/_api/web/GetFolderByServerRelativeUrl('/Shared Documents')
method: GET
headers:
Authorization: "Bearer " + accessToken
accept: "application/json;odata=verbose" or "application/atom+xml"
.NOTES
Prerequisite : Invoke-RestSPO function
.EXAMPLE
$Folder = Get-SPOFolder -WebUrl $WebUrl -UserName $UserName -Password $Password -FolderUrl '/Shared Documents/Folder To Read'
#>
Function Get-SPOFolder(){
Param(
[Parameter(Mandatory=$True)]
[String]$WebUrl,
[Parameter(Mandatory=$True)]
[String]$UserName,
[Parameter(Mandatory=$False)]
[String]$Password,
[Parameter(Mandatory=$True)]
[String]$FolderUrl
)
$Url = $WebUrl + "/_api/web/GetFolderByServerRelativeUrl('" + $FolderUrl + "')"
Invoke-RestSPO $Url Get $UserName $Password
}
<#
.SYNOPSIS
Create Folder
.DESCRIPTION
Create Folder operation via SharePoint 2013 REST API.
url: http://site url/_api/web/folders
method: POST
body: { '__metadata': { 'type': 'SP.Folder' }, 'ServerRelativeUrl': '/document library relative url/folder name'}
Headers:
Authorization: "Bearer " + accessToken
X-RequestDigest: form digest value
accept: "application/json;odata=verbose"
content-type: "application/json;odata=verbose"
content-length:length of post body
.NOTES
Prerequisite : Invoke-RestSPO function
.EXAMPLE
$Folder = Create-SPOFolder -WebUrl $WebUrl -UserName $UserName -Password $Password -FolderUrl '/Shared Documents/Folder To Create'
#>
Function Create-SPOFolder(){
Param(
[Parameter(Mandatory=$True)]
[String]$WebUrl,
[Parameter(Mandatory=$True)]
[String]$UserName,
[Parameter(Mandatory=$False)]
[String]$Password,
[Parameter(Mandatory=$True)]
[String]$FolderUrl
)
$Url = $WebUrl + "/_api/web/folders"
$folderPayload = @{
__metadata = @{'type' = 'SP.Folder' };
ServerRelativeUrl = $FolderUrl;
} | ConvertTo-Json
$contextInfo = Get-SPOContextInfo $WebUrl $UserName $Password
Invoke-RestSPO Url $Url Method Post UserName $UserName Password $Password Metadata $folderPayload RequestDigest $contextInfo.GetContextWebInformation.FormDigestValue
}
<#
.SYNOPSIS
Update Folder
.DESCRIPTION
Update Folder operation via SharePoint 2013 REST API.
url: http://site url/_api/web/GetFolderByServerRelativeUrl('/Folder Name')
method: POST
body: { '__metadata': { 'type': 'SP.Folder' }, 'Name': 'New name' }
Headers:
Authorization: "Bearer " + accessToken
X-RequestDigest: form digest value
"IF-MATCH": etag or "*"
"X-HTTP-Method":"MERGE",
accept: "application/json;odata=verbose"
content-type: "application/json;odata=verbose"
content-length:length of post body
.NOTES
Prerequisite : Invoke-RestSPO function
.EXAMPLE
Update-SPOFolder -WebUrl $WebUrl -UserName $UserName -Password $Password -FolderUrl '/Shared Documents/Folder To Update' -FolderName "New Folder Name"
#>
Function Update-SPOFolder(){
Param(
[Parameter(Mandatory=$True)]
[String]$WebUrl,
[Parameter(Mandatory=$True)]
[String]$UserName,
[Parameter(Mandatory=$False)]
[String]$Password,
[Parameter(Mandatory=$True)]
[String]$FolderUrl,
[Parameter(Mandatory=$True)]
[String]$FolderName
)
$Url = $WebUrl + "/_api/web/GetFolderByServerRelativeUrl('" + $FolderUrl + "')"
$folderPayload = @{
__metadata = @{'type' = 'SP.Folder' };
}
if($FolderName) {
$folderPayload['Name'] = $FolderName
}
$folderPayload = $folderPayload | ConvertTo-Json
$contextInfo = Get-SPOContextInfo $WebUrl $UserName $Password
Invoke-RestSPO Url $Url Method Post UserName $UserName Password $Password Metadata $folderPayload RequestDigest $contextInfo.GetContextWebInformation.FormDigestValue ETag "*" XHTTPMethod "MERGE"
}
<#
.SYNOPSIS
Delete Folder
.DESCRIPTION
Delete Folder operation via SharePoint 2013 REST API.
url: http://site url/_api/web/GetFolderByServerRelativeUrl('/Folder Name')
method: POST
Headers:
Authorization: "Bearer " + accessToken
X-RequestDigest: form digest value
"IF-MATCH": etag or "*"
"X-HTTP-Method":"DELETE"
.NOTES
Prerequisite : Invoke-RestSPO function
.EXAMPLE
Delete-SPOFolder -WebUrl $WebUrl -UserName $UserName -Password $Password -FolderUrl '/Shared Documents/Folder To Delete'
#>
Function Delete-SPOFolder(){
Param(
[Parameter(Mandatory=$True)]
[String]$WebUrl,
[Parameter(Mandatory=$True)]
[String]$UserName,
[Parameter(Mandatory=$False)]
[String]$Password,
[Parameter(Mandatory=$True)]
[String]$FolderUrl
)
$Url = $WebUrl + "/_api/web/GetFolderByServerRelativeUrl('" + $FolderUrl + "')"
$contextInfo = Get-SPOContextInfo $WebUrl $UserName $Password
Invoke-RestSPO Url $Url Method Post UserName $UserName Password $Password RequestDigest $contextInfo.GetContextWebInformation.FormDigestValue ETag "*" XHTTPMethod "DELETE"
}

view raw
Folders-RestSPO.ps1
hosted with ❤ by GitHub

 

Working with files

Folder resource: represents a file in a SharePoint Web site that can be a Web Part Page, an item in a document library, or a file in a folder.

Endpoint URI: http://<site url>/_api/web/getfilebyserverrelativeurl(‘/<folder name>/<file name>‘)

Supported HTTP methods:  GET  |  DELETE  |  POST  (File resource)

The following examples demonstrates how to perform basic operations with File resource including:

  • upload file into SharePoint
  • download  file from a SharePoint

 

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")
. ".\Invoke-RestSPO.ps1"
<#
.SYNOPSIS
Retieve Files in Folder
.DESCRIPTION
Read Files operation via SharePoint 2013 REST API
url: http://site url/_api/web/GetFolderByServerRelativeUrl('/Folder Name')/Files
method: GET
headers:
Authorization: "Bearer " + accessToken
accept: "application/json;odata=verbose" or "application/atom+xml"
.NOTES
Prerequisite : Invoke-RestSPO function
.EXAMPLE
$Files = Get-SPOFiles -WebUrl $WebUrl -UserName $UserName -Password $Password -FolderUrl '/Shared Documents/Folder'
#>
Function Get-SPOFiles(){
Param(
[Parameter(Mandatory=$True)]
[String]$WebUrl,
[Parameter(Mandatory=$True)]
[String]$UserName,
[Parameter(Mandatory=$False)]
[String]$Password,
[Parameter(Mandatory=$True)]
[String]$FolderUrl
)
$Url = $WebUrl + "/_api/web/GetFolderByServerRelativeUrl('" + $FolderUrl + "')/Files"
Invoke-RestSPO $Url Get $UserName $Password | % { $_.results }
}
<#
.SYNOPSIS
Delete File
.DESCRIPTION
Delete Files operation via SharePoint 2013 REST API
url: http://site url/_api/web/GetFileByServerRelativeUrl('/Folder Name/file name')
method: POST
headers:
Authorization: "Bearer " + accessToken
X-RequestDigest: form digest value
IF-MATCH: etag or "*"
X-HTTP-Method:"DELETE"
.NOTES
Prerequisite : Invoke-RestSPO function
.EXAMPLE
Delete-SPOFile -WebUrl $WebUrl -UserName $UserName -Password $Password -FileUrl '/Shared Documents/Folder/File To Delete'
#>
Function Delete-SPOFile(){
Param(
[Parameter(Mandatory=$True)]
[String]$WebUrl,
[Parameter(Mandatory=$True)]
[String]$UserName,
[Parameter(Mandatory=$False)]
[String]$Password,
[Parameter(Mandatory=$True)]
[String]$FileUrl
)
$Url = $WebUrl + "/_api/web/GetFileByServerRelativeUrl('" + $FileUrl + "')"
$contextInfo = Get-SPOContextInfo $WebUrl $UserName $Password
Invoke-RestSPO Url $Url Method Post UserName $UserName Password $Password RequestDigest $contextInfo.GetContextWebInformation.FormDigestValue ETag "*" XHTTPMethod "DELETE"
}
<#
.SYNOPSIS
Download File
.DESCRIPTION
Read File operation via SharePoint 2013 REST API
url: http://site url/_api/web/GetFileByServerRelativeUrl('/Folder Name/file name')/$value
method: GET
headers:
Authorization: "Bearer " + accessToken
.NOTES
Prerequisite : Invoke-RestSPO function
.EXAMPLE
Download-SPOFile -WebUrl $WebUrl -UserName $UserName -Password $Password -FileUrl '/Shared Documents/Folder/File To Download' -DownloadPath 'c:\downloads'
#>
Function Download-SPOFile(){
Param(
[Parameter(Mandatory=$True)]
[String]$WebUrl,
[Parameter(Mandatory=$True)]
[String]$UserName,
[Parameter(Mandatory=$True)]
[String]$Password,
[Parameter(Mandatory=$True)]
[String]$FileUrl,
[Parameter(Mandatory=$True)]
[String]$DownloadPath
)
$Url = $WebUrl + "/_api/web/GetFileByServerRelativeUrl('" + $FileUrl + "')/`$value"
$fileContent = Invoke-RestSPO Url $Url Method Get UserName $UserName Password $Password BinaryStringResponseBody $True
#Save
$fileName = [System.IO.Path]::GetFileName($FileUrl)
$downloadFilePath = [System.IO.Path]::Combine($DownloadPath,$fileName)
[System.IO.File]::WriteAllBytes($downloadFilePath,$fileContent)
}
<#
.SYNOPSIS
Upload File
.DESCRIPTION
Create File operation via SharePoint 2013 REST API
url: http://site url/_api/web/GetFileByServerRelativeUrl('/Folder Name/file name')
method: POST
headers:
Authorization: "Bearer " + accessToken
X-RequestDigest: form digest value
IF-MATCH: etag or "*"
X-HTTP-Method:"DELETE"
.NOTES
Prerequisite : Invoke-RestSPO function
.EXAMPLE
Upload-SPOFile -WebUrl $WebUrl -UserName $UserName -Password $Password -FolderUrl '/Shared Documents/Folder' -UploadFilePath 'Physical Path to File'
#>
Function Upload-SPOFile(){
Param(
[Parameter(Mandatory=$True)]
[String]$WebUrl,
[Parameter(Mandatory=$True)]
[String]$UserName,
[Parameter(Mandatory=$True)]
[String]$Password,
[Parameter(Mandatory=$True)]
[String]$FolderUrl,
[Parameter(Mandatory=$True)]
[String]$UploadFilePath
)
$FileInfo = New-Object System.IO.FileInfo($UploadFilePath)
$Url = $WebUrl + "/_api/web/GetFolderByServerRelativeUrl('" + $FolderUrl + "')/Files/add(url='" + $FileInfo.Name + "',overwrite=true)"
$FileContent = [System.IO.File]::ReadAllBytes($FileInfo.FullName)
$contextInfo = Get-SPOContextInfo $WebUrl $UserName $Password
Invoke-RestSPO Url $Url Method Post UserName $UserName Password $Password Body $FileContent RequestDigest $contextInfo.GetContextWebInformation.FormDigestValue
}

view raw
Files-RestSPO.ps1
hosted with ❤ by GitHub

Summary

To summarize, it was demonstrates how to perform basic operations with files and folders, in particular how to  download and upload files via REST. For that purpose we  utilized Invoke-RestSPO function  that is intended for sending HTTPS requests to O365/SharePoint Online REST service.

References

Consuming the SharePoint 2013 REST API from PowerShell

Introduction

SharePoint 2013 introduces a Representational State Transfer (REST) service that is comparable to the  SharePoint CSOM and in addition to CSOM, REST API opens up a huge capabilities, in particular for administering and automating SharePoint Online when used with PowerShell.

Sending  REST requests to a SharePoint Online 

In the previous post we’ve already covered how to perform read operations by sending HTTPS requests to SharePoint RESTful web services. This time we are going to extend PowerShell script  in order to support all the CRUD operations.

The Invoke-RestSPO function sends  HTTPS requests to SharePoint REST web services that returns richly structured data (JSON)

Add-Type –Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type –Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
<#
.Synopsis
Sends an HTTP or HTTPS request to a SharePoint Online REST-compliant web service.
.DESCRIPTION
This function sends an HTTP or HTTPS request to a Representational State
Transfer (REST)-compliant ("RESTful") SharePoint Online web service.
.EXAMPLE
Invoke-SPORestMethod -Url "https://contoso.sharepoint.com/_api/web&quot;
.EXAMPLE
Invoke-SPORestMethod -Url "https://contoso.sharepoint.com/_api/contextinfo&quot; -Method "Post"
#>
Function Invoke-RestSPO(){
Param(
[Parameter(Mandatory=$True)]
[String]$Url,
[Parameter(Mandatory=$False)]
[Microsoft.PowerShell.Commands.WebRequestMethod]$Method = [Microsoft.PowerShell.Commands.WebRequestMethod]::Get,
[Parameter(Mandatory=$True)]
[String]$UserName,
[Parameter(Mandatory=$False)]
[String]$Password,
[Parameter(Mandatory=$False)]
[String]$Metadata,
[Parameter(Mandatory=$False)]
[System.Byte[]]$Body,
[Parameter(Mandatory=$False)]
[String]$RequestDigest,
[Parameter(Mandatory=$False)]
[String]$ETag,
[Parameter(Mandatory=$False)]
[String]$XHTTPMethod,
[Parameter(Mandatory=$False)]
[System.String]$Accept = "application/json;odata=verbose",
[Parameter(Mandatory=$False)]
[String]$ContentType = "application/json;odata=verbose",
[Parameter(Mandatory=$False)]
[Boolean]$BinaryStringResponseBody = $False
)
if([string]::IsNullOrEmpty($Password)) {
$SecurePassword = Read-Host Prompt "Enter the password" AsSecureString
}
else {
$SecurePassword = $Password | ConvertTo-SecureString AsPlainText Force
}
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($UserName, $SecurePassword)
$request = [System.Net.WebRequest]::Create($Url)
$request.Credentials = $credentials
$request.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f")
$request.ContentType = $ContentType
$request.Accept = $Accept
$request.Method=$Method
if($RequestDigest) {
$request.Headers.Add("X-RequestDigest", $RequestDigest)
}
if($ETag) {
$request.Headers.Add("If-Match", $ETag)
}
if($XHTTPMethod) {
$request.Headers.Add("X-HTTP-Method", $XHTTPMethod)
}
if($Metadata -or $Body) {
if($Metadata) {
$Body = [byte[]][char[]]$Metadata
}
$request.ContentLength = $Body.Length
$stream = $request.GetRequestStream()
$stream.Write($Body, 0, $Body.Length)
}
else {
$request.ContentLength = 0
}
#Process Response
$response = $request.GetResponse()
try {
if($BinaryStringResponseBody -eq $False) {
$streamReader = New-Object System.IO.StreamReader $response.GetResponseStream()
try {
$data=$streamReader.ReadToEnd()
$results = $data | ConvertFrom-Json
$results.d
}
finally {
$streamReader.Dispose()
}
}
else {
$dataStream = New-Object System.IO.MemoryStream
try {
StreamCopyTo Source $response.GetResponseStream() Destination $dataStream
$dataStream.ToArray()
}
finally {
$dataStream.Dispose()
}
}
}
finally {
$response.Dispose()
}
}
# Get Context Info
Function Get-SPOContextInfo(){
Param(
[Parameter(Mandatory=$True)]
[String]$WebUrl,
[Parameter(Mandatory=$True)]
[String]$UserName,
[Parameter(Mandatory=$False)]
[String]$Password
)
$Url = $WebUrl + "/_api/contextinfo"
Invoke-RestSPO $Url Post $UserName $Password
}
Function Stream-CopyTo([System.IO.Stream]$Source, [System.IO.Stream]$Destination)
{
$buffer = New-Object Byte[] 8192
$bytesRead = 0
while (($bytesRead = $Source.Read($buffer, 0, $buffer.Length)) -gt 0)
{
$Destination.Write($buffer, 0, $bytesRead)
}
}

view raw
Invoke-RestSPO.ps1
hosted with ❤ by GitHub

Request Digests

Since SharePoint requires the user to include a request digest value with each create, update and delete operation, an additional request is invoked using Get-SPOContextInfo function to request Context Info entity that contains request digest value.

 ETag

In order to avoid an additional request, “*” eTag value is used to match any eTag value resulting in the operation being performed regardless of the actual value.

Lists manipulation using REST API in PowerShell

This section contains sample code for all of the CRUD operations.

#———————————————————————————————-
# List CRUD operations via SharePoint REST API
#———————————————————————————————-
# Create a List
Function New-SPOList(){
Param(
[Parameter(Mandatory=$True)]
[String]$WebUrl,
[Parameter(Mandatory=$True)]
[String]$UserName,
[Parameter(Mandatory=$False)]
[String]$Password,
[Parameter(Mandatory=$True)]
[String]$Title,
[Parameter(Mandatory=$True)]
[String]$BaseTemplate
)
$listMetadata = @{
__metadata = @{'type' = 'SP.List' };
Title = $Title;
BaseTemplate = $BaseTemplate} | ConvertTo-Json
$Url = $WebUrl + "/_api/lists"
$contextInfo = Get-SPOContextInfo $WebUrl $UserName $Password
Invoke-RestSPO $Url Post $UserName $Password $listMetadata $contextInfo.GetContextWebInformation.FormDigestValue
}
# Update a List
Function Set-SPOList(){
Param(
[Parameter(Mandatory=$True)]
[String]$WebUrl,
[Parameter(Mandatory=$True)]
[String]$UserName,
[Parameter(Mandatory=$False)]
[String]$Password,
[Parameter(Mandatory=$True)]
[String]$Identity,
[Parameter(Mandatory=$False)]
[String]$Title,
[Parameter(Mandatory=$False)]
[String]$Description
)
$listMetadata = @{
__metadata = @{'type' = 'SP.List' };
}
if($Title) {
$listMetadata['Title'] = $Title
}
if($Description) {
$listMetadata['Description'] = $Description
}
$listMetadata = $listMetadata | ConvertTo-Json
$Url = $WebUrl + "/_api/lists/getbytitle('" + $Identity + "')"
$contextInfo = Get-SPOContextInfo $WebUrl $UserName $Password
Invoke-RestSPO Url $Url Method Post UserName $UserName Password $Password RequestDigest $contextInfo.GetContextWebInformation.FormDigestValue Metadata $listMetadata ETag "*" XHTTPMethod "MERGE"
}
#Get List(s)
Function Get-SPOList(){
Param(
[Parameter(Mandatory=$True)]
[String]$WebUrl,
[Parameter(Mandatory=$True)]
[String]$UserName,
[Parameter(Mandatory=$False)]
[String]$Password
)
$Url = $WebUrl + "/_api/lists"
$data = Invoke-RestSPO $Url Get $UserName $Password
$data.results
}
#Delete a List
Function Remove-SPOList(){
Param(
[Parameter(Mandatory=$True)]
[String]$WebUrl,
[Parameter(Mandatory=$True)]
[String]$UserName,
[Parameter(Mandatory=$False)]
[String]$Password,
[Parameter(Mandatory=$True)]
[String]$Identity
)
$Url = $WebUrl + "/_api/lists/getbytitle('" + $Identity + "')"
$contextInfo = Get-SPOContextInfo $WebUrl $UserName $Password
Invoke-RestSPO Url $Url Method Post UserName $UserName Password $Password RequestDigest $contextInfo.GetContextWebInformation.FormDigestValue ETag "*" XHTTPMethod "DELETE"
}

view raw
ListOps-RestSPO.ps1
hosted with ❤ by GitHub

References

 

Some tips and tricks of using SharePoint Client Object Model in PowerShell. Part 1

Overview

When it comes to using SharePoint 2010 Client Object Model (CSOM) we need to be ready for certain kind of  limitations  in PowerShell. First of all, it concerns the usage of Generics Methods, for the example ClientRuntimeContext.Load<T> method:

public void Load<T>(
T clientObject,
params Expression<Func<T, Object>>[] retrievals
)
where T : ClientObject

An attempt to call the method ClientRuntimeContext.Load<T> directly will result in the following error PSGenericMethods

This is a limitation of PowerShell  (V1, V2) AFIK. There are several options how to bypass this limitation but in this post I would like to concentrate only on one technique that was originally described in the post Invoking Generic Methods on Non-Generic Classes in PowerShell. The basic idea is to replace the call for ClientRuntimeContext.Load<T> method with the following one:

Function Invoke-LoadMethod() {
param(
$ClientObject = $(throw "Please provide an Client Object instance on which to invoke the generic method")
)
$ctx = $ClientObject.Context
$load = [Microsoft.SharePoint.Client.ClientContext].GetMethod("Load")
$type = $ClientObject.GetType()
$clientObjectLoad = $load.MakeGenericMethod($type)
$clientObjectLoad.Invoke($ctx,@($ClientObject,$null))
}

For invoking a generic methods we utilize MethodInfo.MakeGenericMethod method. Below are demonstrated some examples of usage SharePoint 2010 Client Object Model (CSOM) in PowerShell.

Example: load Web client object

Let’s start with a simple example for loading Web Client Object:

Add-Type Path 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI\Microsoft.SharePoint.Client.dll'
Add-Type Path 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI\Microsoft.SharePoint.Client.Runtime.dll'
function PrintWebProperties()
{
param(
[Parameter(Mandatory=$true)][string]$url,
[Parameter(Mandatory=$false)][System.Net.NetworkCredential]$credentials
)
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($url)
$ctx.Credentials = $credentials
$web = $ctx.Web
Invoke-LoadMethod ClientObject $web
$ctx.ExecuteQuery()
Write-Host "Web Properties:"
Write-Host "Title: $($web.Title)"
Write-Host "Url: $($web.ServerRelativeUrl)"
}
$credentials = New-Object System.Net.NetworkCredential('username', 'password','domain')
$url = 'http://contoso.intranet.com/'
PrintWebProperties $url $credentials

Example: create Wiki page via CSOM

The example below demonstrates how to create wiki page via CSOM.

C# version:

/// <summary>
/// Create Wiki page via CSOM
/// </summary>
/// <param name="webUrl"></param>
/// <param name="pageName"></param>
/// <param name="pageContent"></param>
public static void CreateWikiPage(string webUrl, string pageName,string pageContent)
{
const string templateRedirectionPageMarkup = "<%@ Page Inherits=\"Microsoft.SharePoint.Publishing.TemplateRedirectionPage,Microsoft.SharePoint.Publishing,Version=14.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c\" %> <%@ Reference VirtualPath=\"~TemplatePageUrl\" %> <%@ Reference VirtualPath=\"~masterurl/custom.master\" %>";
using (var ctx = new ClientContext(webUrl))
{
var wikiPages = ctx.Web.Lists.GetByTitle("Pages");
ctx.Load(wikiPages);
ctx.ExecuteQuery();
var file = new FileCreationInformation
{
Url = pageName,
Content = Encoding.UTF8.GetBytes(templateRedirectionPageMarkup),
Overwrite = true
};
var wikiFile = wikiPages.RootFolder.Files.Add(file);
ctx.Load(wikiFile);
ctx.ExecuteQuery();
var wikiPage = wikiFile.ListItemAllFields;
wikiPage["PublishingPageContent"] = pageContent;
wikiPage["PublishingPageLayout"] = "/_catalogs/masterpage/EnterpriseWiki.aspx, Basic Page";
wikiPage.Update();
ctx.ExecuteQuery();
}
}

view raw
CreateWikiCSOM.cs
hosted with ❤ by GitHub

PowerShell version:

Add-Type Path 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI\Microsoft.SharePoint.Client.dll'
Add-Type Path 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI\Microsoft.SharePoint.Client.Runtime.dll'
function CreateWikiPage()
{
param(
[Parameter(Mandatory=$true)][string]$webUrl,
[Parameter(Mandatory=$false)][System.Net.NetworkCredential]$credentials,
[Parameter(Mandatory=$true)][string]$pageName,
[Parameter(Mandatory=$true)][string]$pageContent
)
$templateRedirectionPageMarkup = '<%@ Page Inherits="Microsoft.SharePoint.Publishing.TemplateRedirectionPage,Microsoft.SharePoint.Publishing,Version=14.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" %> <%@ Reference VirtualPath="~TemplatePageUrl" %> <%@ Reference VirtualPath="~masterurl/custom.master" %>';
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($webUrl)
$ctx.Credentials = $credentials
$wikiPages = $ctx.Web.Lists.GetByTitle("Pages")
Invoke-LoadMethod ClientObject $wikiPages
$ctx.ExecuteQuery()
$file = New-Object Microsoft.SharePoint.Client.FileCreationInformation
$file.Url = $pageName
$file.Content = [System.Text.Encoding]::UTF8.GetBytes($templateRedirectionPageMarkup)
$file.Overwrite = $true
$wikiFile = $wikiPages.RootFolder.Files.Add($file)
Invoke-LoadMethod ClientObject $wikiFile
$wikiPage = $wikiFile.ListItemAllFields
$wikiPage["PublishingPageContent"] = $pageContent
$wikiPage["PublishingPageLayout"] = "/_catalogs/masterpage/EnterpriseWiki.aspx, Basic Page"
$wikiPage.Update()
$ctx.ExecuteQuery();
}
$credentials = New-Object System.Net.NetworkCredential('username', 'password','domain')
$webUrl = 'http://contoso.intranet.com/knowledgebase/'
$pageName = 'MyFirstWikiPage.aspx'
$pageContent = '<h1>Welcome to the Knowledge Base!</h1>'
CreateWikiPage $webUrl $credentials $pageName $pageContent

view raw
CreateWikiPage.ps1
hosted with ❤ by GitHub

MyFirstWikiPage

Summary

In contrary to article Using PowerShell to Get Data from a SharePoint 2010 List that explains how to execute generic methods via inline C#  in PowerShell, this post demonstrates how to utilize Generics Methods in PowerShell natively.

References