Last active
August 29, 2015 14:18
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.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 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.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 { | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.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