Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save glapointe/d0f688f6278ad66cb643 to your computer and use it in GitHub Desktop.
Save glapointe/d0f688f6278ad66cb643 to your computer and use it in GitHub Desktop.
Sends an HTTP or HTTPS request to a SharePoint Online REST-compliant web service using Windows PowerShell.
The functions in this Gist are part of an article series on ITUnity.com.
<#
.Synopsis
Clears the SharePoint Online credentials stored in the global variable.
.DESCRIPTION
Clears the SharePoint Online credentials stored in the global variable.
You can also manually clear the variable by explicitly setting
$global:spoCred = $null.
.EXAMPLE
Clear-SPORestCredentials
#>
function global:Clear-SPORestCredentials {
$global:spoCred = $null
}
<#
.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.
When connecting, if Set-SPORestCredentials is not called then you will be
prompted for your credentials. Those credentials are stored in a global
variable $global:spoCred so that it will be available on subsequent calls.
Call Clear-SPORestCredentials to clear the variable.
.EXAMPLE
Invoke-SPORestMethod -Url "https://contoso.sharepoint.com/_api/web"
.EXAMPLE
Invoke-SPORestMethod -Url "https://contoso.sharepoint.com/_api/contextinfo" -Method "Post"
#>
function global:Invoke-SPORestMethod {
[CmdletBinding()]
[OutputType([int])]
Param (
# The REST endpoint URL to call.
[Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
[ValidateNotNullOrEmpty()]
[System.Uri]$Url,
# Specifies the method used for the web request. The default value is "Get".
[Parameter(Mandatory = $false, Position = 1)]
[ValidateNotNullOrEmpty()]
[ValidateSet("Get", "Head", "Post", "Put", "Delete", "Trace", "Options", "Merge", "Patch")]
[string]$Method = "Get",
# Additional metadata that should be provided as part of the Body of the request.
[Parameter(Mandatory = $false, Position = 2)]
[ValidateNotNullOrEmpty()]
[object]$Metadata,
# The "X-RequestDigest" header to set. This is most commonly used to provide the form digest variable. Use "(Invoke-SPORestMethod -Url "https://contoso.sharepoint.com/_api/contextinfo" -Method "Post").GetContextWebInformation.FormDigestValue" to get the Form Digest value.
[Parameter(Mandatory = $false, Position = 3)]
[ValidateNotNullOrEmpty()]
[string]$RequestDigest,
# The "If-Match" header to set. Provide this to make sure you are not overwritting an item that has changed since you retrieved it.
[Parameter(Mandatory = $false, Position = 4)]
[ValidateNotNullOrEmpty()]
[string]$ETag,
# To work around the fact that many firewalls and other network intermediaries block HTTP verbs other than GET and POST, specify PUT, DELETE, or MERGE requests for -XHTTPMethod with a POST value for -Method.
[Parameter(Mandatory = $false, Position = 5)]
[ValidateNotNullOrEmpty()]
[ValidateSet("Get", "Head", "Post", "Put", "Delete", "Trace", "Options", "Merge", "Patch")]
[string]$XHTTPMethod,
[Parameter(Mandatory = $false, Position = 6)]
[ValidateNotNullOrEmpty()]
[ValidateSet("Verbose", "MinimalMetadata", "NoMetadata")]
[string]$JSONVerbosity = "Verbose",
# If the returned data is a binary data object such as a file from a SharePoint site specify the output file name to save the data to.
[Parameter(Mandatory = $false, Position = 7)]
[ValidateNotNullOrEmpty()]
[string]$OutFile
)
Begin {
if ($global:spoCred -eq $null) {
$global:spoCred = Get-Credential -Message "Enter your credentials for SharePoint Online:"
}
}
Process {
function Get-SPOSecurityToken($cred) {
function ConvertTo-UnsecureString([System.Security.SecureString]$string) {
$unmanagedString = [System.Runtime.InteropServices.Marshal]::SecureStringToGlobalAllocUnicode($string)
$unsecureString = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($unmanagedString)
[System.Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocUnicode($unmanagedString)
return $unsecureString
}
$pwd = [System.Security.SecurityElement]::Escape((ConvertTo-UnsecureString $cred.Password))
$userName = [System.Security.SecurityElement]::Escape($cred.UserName)
$soap = @"
<?xml version="1.0" encoding="UTF-8"?>
<S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust">
<S:Header>
<wsa:Action S:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</wsa:Action>
<wsa:To S:mustUnderstand="1">https://login.microsoftonline.com/rst2.srf</wsa:To>
<ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/LiveID/SoapServices/v1" Id="PPAuthInfo">
<ps:BinaryVersion>5</ps:BinaryVersion>
<ps:HostingApp>Managed IDCRL</ps:HostingApp>
</ps:AuthInfo>
<wsse:Security>
<wsse:UsernameToken wsu:Id="user">
<wsse:Username>$($userName)</wsse:Username>
<wsse:Password>$($pwd)</wsse:Password>
</wsse:UsernameToken>
<wsu:Timestamp Id="Timestamp">
<wsu:Created>$(([DateTime]::UtcNow.ToString("o")))</wsu:Created>
<wsu:Expires>$(([DateTime]::UtcNow.AddDays(1).ToString("o")))</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</S:Header>
<S:Body>
<wst:RequestSecurityToken xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust" Id="RST0">
<wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>
<wsp:AppliesTo>
<wsa:EndpointReference>
<wsa:Address>sharepoint.com</wsa:Address>
</wsa:EndpointReference>
</wsp:AppliesTo>
<wsp:PolicyReference URI="MBI"></wsp:PolicyReference>
</wst:RequestSecurityToken>
</S:Body>
</S:Envelope>
"@
$loginUrl = "https://login.microsoftonline.com/rst2.srf"
$body = [System.Text.Encoding]::UTF8.GetBytes($soap)
$request = [System.Net.WebRequest]::Create($loginUrl)
$request.Method = "POST"
$request.ContentType = "application/soap+xml; charset=utf-8"
$stream = $request.GetRequestStream()
$stream.Write($body, 0, $body.Length)
$response = $request.GetResponse()
$data = $null
try {
$streamReader = New-Object System.IO.StreamReader $response.GetResponseStream()
try {
[xml]$data = $streamReader.ReadToEnd()
} finally {
$streamReader.Dispose()
}
} finally {
$response.Dispose()
}
return $data.Envelope.Body.RequestSecurityTokenResponse.RequestedSecurityToken.BinarySecurityToken.InnerText
}
function Get-SPOSecurityCookie($url, $securityToken) {
$uri = [uri]$url
$baseUrl = "$($uri.Scheme)://$($uri.Host)/_vti_bin/idcrl.svc/"
$request = [System.Net.WebRequest]::Create($baseUrl)
$cookieContainer = New-Object System.Net.CookieContainer
$request.CookieContainer = $cookieContainer
$request.Method = "GET"
$request.Headers["Authorization"] = "BPOSIDCRL $securityToken"
$response = $request.GetResponse()
$cookieHeader = $cookieContainer.GetCookieHeader($baseUrl)
$response.Close()
return $cookieHeader
}
$request = [System.Net.WebRequest]::Create($Url)
$odata = ";odata=$($JSONVerbosity.ToLower())"
$request.Accept = "application/json$odata"
$request.ContentType = "application/json$odata"
$request.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f")
$request.Method = $Method.ToUpper()
$token = Get-SPOSecurityToken $global:spoCred
$cookieValue = Get-SPOSecurityCookie $Url $token
$cookieContainer = New-Object System.Net.CookieContainer
$uri = [uri]$Url
$cookie = New-Object System.Net.Cookie "SPOIDCRL", $cookieValue.Substring(9), "", $uri.Authority
$cookieContainer.Add($cookie)
$request.CookieContainer = $cookieContainer
if(![string]::IsNullOrEmpty($RequestDigest)) {
$request.Headers.Add("X-RequestDigest", $RequestDigest)
}
if(![string]::IsNullOrEmpty($ETag)) {
$request.Headers.Add("If-Match", $ETag)
}
if($XHTTPMethod -ne $null) {
$request.Headers.Add("X-HTTP-Method", $XHTTPMethod.ToUpper())
}
if ($Metadata -is [string] -and ![string]::IsNullOrEmpty($Metadata)) {
$body = [System.Text.Encoding]::UTF8.GetBytes($Metadata)
$request.ContentLength = $body.Length
$stream = $request.GetRequestStream()
$stream.Write($body, 0, $body.Length)
} elseif ($Metadata -is [byte[]] -and $Metadata.Count -gt 0) {
$request.ContentLength = $Metadata.Length
$stream = $request.GetRequestStream()
$stream.Write($Metadata, 0, $Metadata.Length)
} else {
$request.ContentLength = 0
}
$response = $request.GetResponse()
try {
$streamReader = New-Object System.IO.StreamReader $response.GetResponseStream()
try {
# If the response is a file (a binary stream) then save the file our output as-is.
if ($response.ContentType.Contains("application/octet-stream")) {
if (![string]::IsNullOrEmpty($OutFile)) {
$fs = [System.IO.File]::Create($OutFile)
try {
$streamReader.BaseStream.CopyTo($fs)
} finally {
$fs.Dispose()
}
return
}
$memStream = New-Object System.IO.MemoryStream
try {
$streamReader.BaseStream.CopyTo($memStream)
Write-Output $memStream.ToArray()
} finally {
$memStream.Dispose()
}
return
}
# We don't have a file so assume JSON data.
$data = $streamReader.ReadToEnd()
# In many cases we might get two ID properties with different casing.
# While this is legal in C# and JSON it is not with PowerShell so the
# duplicate ID value must be renamed before we convert to a PSCustomObject.
if ($data.Contains("`"ID`":") -and $data.Contains("`"Id`":")) {
$data = $data.Replace("`"ID`":", "`"ID-dup`":");
}
$results = ConvertFrom-Json -InputObject $data
# The JSON verbosity setting changes the structure of the object returned.
if ($JSONVerbosity -ne "verbose") {
Write-Output $results
} else {
Write-Output $results.d
}
} finally {
$streamReader.Dispose()
}
} finally {
$response.Dispose()
}
}
End {
}
}
<#
.Synopsis
Stores the credentials for Invoke-SPORestMethod.
.DESCRIPTION
Stores the credentials for Invoke-SPORestMethod. This is done so that you
don't have to provide your credentials on every call to Invoke-SPORestMethod.
.EXAMPLE
Set-SPORestCredentials
.EXAMPLE
Set-SPORestCredentials -Credential $cred
#>
function global:Set-SPORestCredentials {
Param (
[Parameter(ValueFromPipeline = $true)]
[ValidateNotNull()]
$Credential = (Get-Credential -Message "Enter your credentials for SharePoint Online:")
)
Process {
$global:spoCred = $Credential
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment