Skip to content

Instantly share code, notes, and snippets.

@svarukala
Last active July 13, 2020 20:22
Show Gist options
  • Select an option

  • Save svarukala/f9a09fc9eb7037af293df5dd3b0a8a0e to your computer and use it in GitHub Desktop.

Select an option

Save svarukala/f9a09fc9eb7037af293df5dd3b0a8a0e to your computer and use it in GitHub Desktop.
Sample PowerShell script to upload small and large files to SPO that uses the ACS (AppRegNew.aspx) based registered app
<#
DISCLAIMER
----------
The scripts is provided is not to be treated as an official recommendations from Microsoft. The information provided is as-is with no warranty and use at your personal risk.
PURPOSE
-------
Sample script to upload small to large files to SPO that uses the ACS (AppRegNew.aspx) based registered app.
Example SPO ACS APP REGISTRATION (Using AppRegNew.aspx page)
------------------------------------------------------------
Client Id: 7596a681-a2da-4850-8f1b-280f05bd08bc
Client Secret: +MCeNH4NGNXRBF5aJsG9vos+dtioOtBrVb0wEhabWyU=
Title: PSScriptUploadFiles
App Domain: localhost
Redirect URI: https://YOURTENANT.sharepoint.com
PERMISSION XML SET USING APPINV.ASPX
-------------------------------------
<AppPermissionRequests AllowAppOnlyPolicy="true">
<AppPermissionRequest Scope="http://sharepoint/content/sitecollection" Right="Manage" />
</AppPermissionRequests>
SAMPLE RUN
-----------
UploadFileInSlice <clientContext> <libraryName> <fileLocation> <chunkSize>
CREDITS TO BELOW REFERENCES
----------------------------
http://www.wictorwilen.se/sharepoint-online-app-only-policy-powershell-tasks-with-acs
https://blog.vgrem.com/2015/01/27/consuming-the-sharepoint-online-rest-api-from-powershell-part-2/
https://gist.github.com/asadrefai/ecfb32db81acaa80282d
#>
Import-Module Microsoft.Online.SharePoint.PowerShell
Function Get-SPOAccessToken([string]$ClientId,[string]$ClientSecret,[Uri]$WebUri,[string]$TenantId){
$SharePointPrincipal = "00000003-0000-0ff1-ce00-000000000000"
#$realm = GetRealmFromTargetUrl -TargetApplicationUri $WebUri
$realm = $TenantId
$accessToken = GetAppOnlyAccessToken -ClientId $ClientId -ClientSecret $ClientSecret -TargetPrincipalName $SharePointPrincipal -TargetHost $WebUri.Authority -TargetRealm $realm
return $accessToken.access_token
}
function GetRealmFromTargetUrl([Uri]$TargetApplicationUri)
{
$url = $TargetApplicationUri.AbsoluteUri + "/_vti_bin/client.svc"
$headers = @{}
$headers.Add('Authorization','Bearer')
try {
$response = Invoke-WebRequest -Uri $url -Headers $headers -Method Get
}
catch [Net.WebException]{
$authResponseHeader = $_.Exception.Response.Headers["WWW-Authenticate"]
#$bearerKey = "Bearer realm="
$bearer = $authResponseHeader.Split(",")[0]
$targetRealm = $bearer.Split("=")[1]
Write-Host $targetRealm.Substring(1,$targetRealm.Length-2) -ForegroundColor Green
return $targetRealm.Substring(1,$targetRealm.Length-2)
}
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
}
Write-Host $oauth2Request
return $oauth2Request
}
function GetFormattedPrincipal([string]$PrincipalName, [string]$HostName, [string]$Realm)
{
if ($HostName)
{
return "$PrincipalName/$HostName@$Realm"
}
return "$PrincipalName@$Realm"
}
Function UploadFileInSlice ($ctx, $libraryName, $fileName, $fileChunkSizeInMB) {
#$fileChunkSizeInMB = 9
# Each sliced upload requires a unique ID.
$UploadId = [GUID]::NewGuid()
# Get the name of the file.
$UniqueFileName = [System.IO.Path]::GetFileName($fileName)
# Get the folder to upload into.
$Docs = $ctx.Web.Lists.GetByTitle($libraryName)
$ctx.Load($Docs)
$ctx.Load($Docs.RootFolder)
$ctx.ExecuteQuery()
# Get the information about the folder that will hold the file.
#$ServerRelativeUrlOfRootFolder = $Docs.RootFolder.ServerRelativeUrl
# File object.
[Microsoft.SharePoint.Client.File] $upload
# Calculate block size in bytes.
$BlockSize = $fileChunkSizeInMB * 1024 * 1024
# Get the size of the file.
$FileSize = (Get-Item $fileName).length
if ($FileSize -le $BlockSize)
{
# Use regular approach.
$FileStream = New-Object IO.FileStream($fileName,[System.IO.FileMode]::Open)
$FileCreationInfo = New-Object Microsoft.SharePoint.Client.FileCreationInformation
$FileCreationInfo.Overwrite = $true
$FileCreationInfo.ContentStream = $FileStream
$FileCreationInfo.URL = $UniqueFileName
$Upload = $Docs.RootFolder.Files.Add($FileCreationInfo)
$ctx.Load($Upload)
$ctx.ExecuteQuery()
return $Upload
}
else
{
# Use large file upload approach.
$BytesUploaded = $null
$Fs = $null
Try {
$Fs = [System.IO.File]::Open($fileName, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
$br = New-Object System.IO.BinaryReader($Fs)
$buffer = New-Object System.Byte[]($BlockSize)
$lastBuffer = $null
$fileoffset = 0
$totalBytesRead = 0
$bytesRead
$first = $true
$last = $false
# Read data from file system in blocks.
while(($bytesRead = $br.Read($buffer, 0, $buffer.Length)) -gt 0) {
$totalBytesRead = $totalBytesRead + $bytesRead
# You've reached the end of the file.
if($totalBytesRead -eq $FileSize) {
$last = $true
# Copy to a new buffer that has the correct size.
$lastBuffer = New-Object System.Byte[]($bytesRead)
[array]::Copy($buffer, 0, $lastBuffer, 0, $bytesRead)
}
If($first)
{
$ContentStream = New-Object System.IO.MemoryStream
# Add an empty file.
$fileInfo = New-Object Microsoft.SharePoint.Client.FileCreationInformation
$fileInfo.ContentStream = $ContentStream
$fileInfo.Url = $UniqueFileName
$fileInfo.Overwrite = $true
$Upload = $Docs.RootFolder.Files.Add($fileInfo)
$ctx.Load($Upload)
# Start upload by uploading the first slice.
$s = [System.IO.MemoryStream]::new($buffer)
# Call the start upload method on the first slice.
$BytesUploaded = $Upload.StartUpload($UploadId, $s)
$ctx.ExecuteQuery()
# fileoffset is the pointer where the next slice will be added.
$fileoffset = $BytesUploaded.Value
# You can only start the upload once.
$first = $false
}
Else
{
# Get a reference to your file.
$Upload = $ctx.Web.GetFileByServerRelativeUrl($Docs.RootFolder.ServerRelativeUrl + [System.IO.Path]::AltDirectorySeparatorChar + $UniqueFileName);
If($last) {
# Is this the last slice of data?
$s = [System.IO.MemoryStream]::new($lastBuffer)
# End sliced upload by calling FinishUpload.
$Upload = $Upload.FinishUpload($UploadId, $fileoffset, $s)
$ctx.ExecuteQuery()
Write-Host "File upload complete"
# Return the file object for the uploaded file.
return $Upload
}
else {
$s = [System.IO.MemoryStream]::new($buffer)
# Continue sliced upload.
$BytesUploaded = $Upload.ContinueUpload($UploadId, $fileoffset, $s)
$ctx.ExecuteQuery()
# Update fileoffset for the next slice.
$fileoffset = $BytesUploaded.Value
}
}
} #// while ((bytesRead = br.Read(buffer, 0, buffer.Length)) > 0)
}
Catch {
Write-Host $_.Exception.Message -ForegroundColor Red
}
Finally {
if ($null -ne $Fs)
{
$Fs.Dispose()
}
}
}
return $null
}
$WebUri = "https://YOURTEANT.sharepoint.com/sites/SITENAME"
$ClientId = "SPO-ACS-APP-ID-GUID"
$ClientSecret = "SPO-ACS-APP-Secret"
$TenantId = "TENANT-GUID"
$accessToken = Get-SPOAccessToken -ClientId $ClientId -ClientSecret $ClientSecret -WebUri $WebUri -TenantId $TenantId
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($WebUri)
$ctx.add_ExecutingWebRequest(
{
param($Source, $EventArgs)
$EventArgs.WebRequestExecutor.RequestHeaders.Add("Authorization", "Bearer "+ $accessToken)
})
$web = $ctx.Web
$ctx.Load($web)
$ctx.ExecuteQuery()
$title = $web.Title
Write-Host "Site Title: $title"
#UploadFileInSlice <clientContext> <libraryName> <fileLocation> <chunkSize>
$file = UploadFileInSlice $ctx "Documents" "c:\temp\2020.02.Office365 Technical Update Briefing.pptx" 30
$ctx.Dispose()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment