Copy document library to document library sharepoint online with powershell - powershell

I am making a PowerShell script to copy content from a document library to a different document library. So far I have the following code
{
# Define final variables
$SourceUrlComplete = $SourceUrl + $SourceSite + "/" + $SourceSubSite
$DestinationUrlComplete = $SourceUrl + "Contracts"
# Set connections
Write-host "Start copy process..." -f Yellow
Write-host "1. Connect to $sourceUrlComplete" -f Yellow
$SourceConnection = Connect-PnPOnline -Url $SourceUrlComplete -ClientId $ClientId -ClientSecret $ClientSecret -WarningAction Ignore -ReturnConnection
Write-host " Connected" -f DarkGreen
Write-host "2. Connect to $TargetUrlComplete" -f Yellow
$DestinationConnection = Connect-PnPOnline -Url $DestinationUrlComplete -ClientId $ClientId -ClientSecret $ClientSecret -WarningAction Ignore -ReturnConnection
Write-host " Connected" -f DarkGreen
$SourceList = Get-PnPList -Identity $SourceLibrary -Includes RootFolder -Connection $SourceConnection
$SourceListUrl = $SourceList.RootFolder.ServerRelativeUrl
$DestinationList = Get-PnPList -Identity $DesitinationLibrary -Includes RootFolder -Connection $DestinationConnection
$DestinationListUrl = $DestinationList.RootFolder.ServerRelativeUrl
}
This works, I have connections, I have the list too. I can also use Get-PnpListItem and will get all list items. What I'm now left with is the following:
I need to look through the items and based on a custom column check a folder exists on the target
Copy the file to the target
Unfortunately, the column 'title' and 'name' are not always populated in the source item
Note: the lists are on two different sites. But if the solution is to create a temp list on-site A then copy that to site B please let me know that too.
Can someone advise on the next step? I get stuck with either coping one on one of the whole list or messages saying to use spFileCollection.add().

So what I've done now is make this function
Function ProcessFiles {
$SourceUrlComplete = $SourceUrl + $Site + "/" + $SubSite
$DestinationUrlComplete = $SourceUrl + "Contracts"
Write-host "Start copy process..." -f Yellow
Write-host " 1. Connect to $sourceUrlComplete" -f Yellow
$SourceConnection = Connect-PnPOnline -Url $SourceUrlComplete -ClientId $ClientId -ClientSecret $ClientSecret -WarningAction Ignore -ReturnConnection
Write-host " Connected" -f DarkGreen
Write-host " 2. Connect to $DestinationUrlComplete" -f Yellow
$DestinationConnection = Connect-PnPOnline -Url $DestinationUrlComplete -ClientId $ClientId -ClientSecret $ClientSecret -WarningAction Ignore -ReturnConnection
Write-host " Connected" -f DarkGreen
Write-host " 3. Process files" -f Yellow
$SourceFiles = Get-PnPFolderItem -Connection $SourceConnection -FolderSiteRelativeUrl $SourceList -ItemType File
$SourceListItems = Get-PnPListItem -List $SourceList -Connection $SourceConnection
foreach ($SourceFile in $SourceFiles) {
$process = "yes"
foreach ($SourceListItem in $SourceListItems)
{
if ($SourceListItem["FileRef"] -eq $SourceFile.ServerRelativeUrl -and $process -eq "yes")
{
$process = "no"
Write-host " - $($SourceFile.ServerRelativeUrl)" -f DarkYellow
Get-PnPFile -Url $SourceFile.ServerRelativeUrl -Path $ProcessFolder -FileName $SourceFile.Name -AsFile -Connection $SourceConnection -Force
$SourcePath = $ProcessFolder + "\" + $SourceFile.Name
$DestinationFolder = $SourceListItem["CustomerName"]
$DestinationItemValues = #{
"CustomerName" = $SourceListItem["CustomerName"]
"Title" = $SourceListItem["Title"]
"CustomerNo" = $SourceListItem["CustomerNo"]
"DocType" = $SourceListItem["DocType"]
"DocDate" = $SourceListItem["DocDate"]
"DocCode" = $SourceListItem["DocCode"]
"Modified" = $SourceListItem["Modified"]
"Created" = $SourceListItem["Created"]
"Visibility" = $SourceListItem["Visibility"]
"CompanyID" = $SourceListItem["CompanyID"]
}
$DestinationFolder = "Contracts"
if ($SourceSubSite -eq "BE")
{
$DestinationFolder = $DestinationFolder + "\SOME_FOLDER\" + $SourceListItem["CustomerName"]
Add-PnPFile -Path $SourcePath -Connection $DestinationConnection -Folder "$DestinationFolder" -Values $DestinationItemValues | Out-Null
Remove-Item $SourcePath -Recurse
Write-host " - DONE" -f DarkGreen
}
else
{
Write-host " - FAILED" -f Red
}
}
}
}
Write-host " Done" -f DarkGreen
}
Right now it works, but I download the file temporarily. I need to add something to replace special characters where the new folder is determined. I'm not sure this is the best way currently.

Related

How to delete files from Sharepoint with exclusions list using Powershell

I’m trying to delete certain files from a Sharepoint folder, however no matter what I try, I cannot get it to delete. I have an exclusions list, for the names of pictures that should not be deleted. I have attached one of many tries. Anyone know what to do?
The exclusionslist and the loop can write out the names to delete without any problems, and the below code gives no error-codes.
$siteurl = “https://sharepoint.com/sites/Some/Sharepoint/Folder”
$username = “AdminAccount”
$securePassword = ConvertTo-SecureString “MuchSecurePassWord” -AsPlainText -Force
$O365Credential = New-Object System.Management.Automation.PsCredential($username, $securePassword)
$count = 0
$exclusions = import-csv “Downloads\exclusions.txt”
$excounter = $exclusions.Count
write-host “Files to not delete $excounter”
Connect-PnPOnline -Url $siteurl -Credentials $O365Credential
$items = Get-PnPFolderItem -FolderSiteRelativeUrl "/Picture Folder/Test" -ItemType File
$ListItemCount = $items.Count
Write-Host $ListItemCount
$Found = 0
foreach ($item in $items)
{
foreach ($User in $exclusions)
{
#Write-Host $item.Name " " $User.User
if ($item.Name -eq $User.User)
{
# Write-Host $item.Name " " $User.User
$Found = 1
}
}
if($Found -eq 0)
{
$Name = $item.Name
Write-host "Delete: " $item.Name $Found
$item.DeleteObject()
}
$Found=0
}
I'm hoping to delete photos, that are not on the exclusions list.
Instead of:
$item.DeleteObject()
Use:
Remove-PnPFile -ServerRelativeUrl $item.ServerRelativeURL
Docs:
https://pnp.github.io/powershell/cmdlets/Remove-PnPFile.html

Get List with 5000 items on the Site Collections

Im trying to Get all the of the list in the Site Collections.
The script below just get the List in the specific site collection and this is working properly.
#environment variables
$username = "user.name#xxx.com"
$password = Read-Host -Prompt "Enter your password: " -AsSecureString
$url = "https://xxx.sharepoint.com/sites/xxxxxx"
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
#add SharePoint Online DLL - update the location if required
$programFiles = [environment]::getfolderpath("programfiles")
add-type -Path $programFiles'\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.dll'
# connect/authenticate to SharePoint Online and get ClientContext object..
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($url)
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $securePassword)
$ctx.Credentials = $credentials
#get all the sub webs
$Web = $ctx.Web
$ctx.Load($web)
$ctx.Load($web.Webs)
$ctx.executeQuery()
Write-Host -ForegroundColor Yellow "There are:" $web.Webs.Count "sub webs in this site collection"
#get all the lists
foreach ($subweb in $web.Webs)
{
$lists = $subweb.Lists
$ctx.Load($lists)
$ctx.ExecuteQuery()
#output the list details
Foreach ($list in $lists)
{
if ($list.ItemCount -gt 5000)
{
Write-Host -ForegroundColor Yellow "The site URL is" $subweb.Url
Write-Host "List title is: " $list.Title". This list has: " $list.ItemCount " items"
}
}
}
When i try to modify it to get lists in all of the site collection. It errored out. Here is the script that i modify.
#environment variables
$username = "user.name#xxxx.com"
$password = Read-Host -Prompt "Enter your password: " -AsSecureString
$url = get-content "C:\Users\user.name\Documents\PowershellScripts\sites.txt"
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
#add SharePoint Online DLL - update the location if required
$programFiles = [environment]::getfolderpath("programfiles")
add-type -Path $programFiles'\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.dll'
# connect/authenticate to SharePoint Online and get ClientContext object..
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($url)
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $securePassword)
$ctx.Credentials = $credentials
#get all the sub webs
$Web = $ctx.Web
$ctx.Load($web)
$ctx.Load($web.Webs)
$ctx.executeQuery()
foreach($urls in $url)
{
Write-Host -ForegroundColor Yellow "There are:" $web.Webs.Count "sub webs in this site collection"
#get all the lists
foreach ($subweb in $web.Webs)
{
$lists = $subweb.Lists
$ctx.Load($lists)
$ctx.ExecuteQuery()
#output the list details
Foreach ($list in $lists)
{
if ($list.ItemCount -gt 5000)
{
Write-Host -ForegroundColor Yellow "The site URL is" $subweb.Url
Write-Host "List title is: " $list.Title". This list has: " $list.ItemCount " items"
}
}
}
}
This is the error.
You cannot call a method on a null-valued expression. At
C:\Users\joshua.maniquiz\Documents\PowershellScripts\GetAllSubWebsandListswithItemCounts.ps1:22
char:1 + $ctx.executeQuery() + ~~~~~~~~~~~~~~~~~~~ + CategoryInfo :
InvalidOperation: (:) [], RuntimeException + FullyQualifiedErrorId :
InvokeMethodOnNull There are: 0 sub webs in this site collection There
are: 0 sub webs in this site collection There are: 0 sub webs in this
site collection There are: 0 sub webs in this site collection There
are: 0 sub webs in this site collection
Thank you in advance for your help!
Your mixing of singular/plural with $url and $urls is at best unlucky.
If your credentials match for all sites and the $ctx object hasn't to be unloaded/released this could probaply work.
#environment variables
$username = "user.name#xxx.com"
$password = Read-Host -Prompt "Enter your password: " -AsSecureString
$sites = get-content "C:\Users\user.name\Documents\PowershellScripts\sites.txt"
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
#add SharePoint Online DLL - update the location if required
$programFiles = [environment]::getfolderpath("programfiles")
add-type -Path $programFiles'\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.dll'
ForEach($site in $sites) {
# connect/authenticate to SharePoint Online and get ClientContext object..
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($site)
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $securePassword)
$ctx.Credentials = $credentials
#get all the sub webs
$Web = $ctx.Web
$ctx.Load($web)
$ctx.Load($web.Webs)
$ctx.executeQuery()
Write-Host -ForegroundColor Yellow "There are:" $web.Webs.Count "sub webs in this site collection"
#get all the lists
ForEach ($subweb in $web.Webs) {
$lists = $subweb.Lists
$ctx.Load($lists)
$ctx.ExecuteQuery()
#output the list details
ForEach ($list in $lists) {
if ($list.ItemCount -gt 5000) {
Write-Host -ForegroundColor Yellow "The site URL is" $subweb.Url
Write-Host "List title is: " $list.Title". This list has: " $list.ItemCount " items"
}
}
}
}
I rewrote your script a bit using the SharePoint PnP PowerShell cmdlets to do the same thing. I think I got implemented what you are trying to do :)
$urls = Get-Content "C:\Users\user.name\Documents\PowershellScripts\sites.txt"
$username = "user.name#xxxx.com"
$encpassword = Read-Host -Prompt "Enter your password: " -AsSecureString
$cred = New-Object -typename System.Management.Automation.PSCredential -argumentlist $username, $encpassword
foreach($url in $urls) {
Connect-PnPOnline -Url $url -Credentials $cred
$rootweb = Get-PnPWeb -Includes Webs
foreach($web in $rootweb.Webs) {
$currentWeb = Get-PnPWeb $web.Id -Includes Lists
foreach($list in $currentWeb.Lists) {
if ($list.ItemCount -gt 5000) {
Write-Host -ForegroundColor Yellow "The site URL is" $currentWeb.Url
Write-Host "List title is: " $list.Title". This list has: " $list.ItemCount " items"
}
}
}
}
Before you can run the above script you need to install the SharePoint PnP cmdlets as well, that you can do by running
Install-Module SharePointPnPPowerShellOnline
if you are running Windows 10. In other cases, see this page for more information.
With the code above I want to illustrate how you can write the same thing shorter using more high level cmdlets.
SharePoint PnP PowerShell cmdlets are cmdlets created by the SharePoint PnP community and are distributed for free to use.

Getting Error while running Powershell Script to add Site Content Link

$adminUPN="xxxxx#Home500.onmicrosoft.com"
$orgName="xxxxxx"
$userCredential = Get-Credential -UserName $adminUPN -Message "Type the password."
Connect-SPOService -Url https://$orgName-admin.sharepoint.com -Credential $userCredential
# Begin the process
$loadInfo1 = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
$loadInfo2 = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")
$loadInfo3 = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.UserProfiles")
#Add SharePoint PowerShell SnapIn if not already added
$snapin = Get-PSSnapin | Where-Object {$_.Name -eq 'Microsoft.SharePoint.Powershell'}
if ($snapin -eq $null)
{
Write-Host "Loading SharePoint Powershell Snapin"
Add-PSSnapin "Microsoft.SharePoint.Powershell" -EA SilentlyContinue
}
CLS
$StartTime = $(get-date -f F)
$timeStamp = Get-Date -format "MM_dd_yy_hh_mm"
#Get Current folder file path
$invocation = (Get-Variable MyInvocation).Value
$currentPath = Split-Path $invocation.MyCommand.Path
$currentPath = $currentPath + "\"
#Config File Path
#$configPath = $currentPath + "Config.xml"
$configPath = "C:\Users\EMXBG\Downloads\Script_AddSiteContent\Script_AddSiteContent\Config.xml"
#fetching details from config.xml
[xml]$configXML = Get-Content $configPath
$inputFileName = [string]$configXML.Config.Constants.InputFileName
$errorFileName = [string]$configXML.Config.Constants.ErrorFileName
$outFilePath = [string]$configXML.Config.Constants.OutputFileName
#Source File path containing list of WebApplications in a farm.
$webApplFilePath = $currentPath + $inputFileName
#Output File path of the exported AD Security Groups with Site collection and Group Name details.
$sitesFilePath = $currentPath + $outFilePath
#File path of the file which will capture all the errors while running the script.
$errorPath = $currentPath + $errorFileName + $timeStamp + ".csv"
# Creating object to write logging into the error and output file
$sitesFile = New-Object System.IO.StreamWriter $sitesFilePath
$errorfile = New-Object System.IO.StreamWriter $errorPath
# Fetching SharePoint WebApplications list from a CSV file
$CSVData = Import-CSV -path $webApplFilePath
$sitesFile.WriteLine("SiteCollectionName"+","+"SiteURL")
$errorfile.WriteLine("SiteURL"+"`t"+"ExceptionLevel"+"`t"+"ExceptionMsg");
addSiteContentLink $CSVData
$sitesFile.Close()
$errorfile.Close()
# Function to add Site Content link in thes where it does not exists
function addSiteContentLink($CSVData)
{
try
{
$compareText = "Site contents"
foreach ($row in $CSVData)
{
$webUrl = $row.webUrl
#$username = $row.username
#$password = $row.password
#Get Web Application and credentials
#$securePass = ConvertTo-SecureString $password -AsPlainText -Force
#$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($webUrl)
#$ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $securePass)
# Get the collection of navigation nodes from the quick launch bar
#$web = $ctx.Web
$quickLaunch = $webUrl.Navigation.QuickLaunch
try
{
#Iterate through each iten in Quick launch menu
foreach($quickLaunch in $web)
{
if ($quickLaunch -contains $compareText)
{
Write-Host "Site Content link Exists!"
}
else
{
# Add a new navigation node
$navNode = New-Object Microsoft.SharePoint.Client.NavigationNodeCreationInformation
$navNode.AsLastNode = $true
$navNode.Title = "Site Contents"
$navNode.Url = $web.Url + "_layouts/15/viewlsts.aspx"
$navNode.IsExternal = $false
$ctx.Load($quickLaunchColl.Add($navNode))
$ctx.ExecuteQuery()
}
}
}
catch
{
Write-Host("Exception at Site Collection Url :" + $currentSite.Url)
$errorfile.WriteLine($currentSite.Url+"`t"+"`t"+$_.Exception.Message)
}
}
#Export Data to CSV
$sitesCollection | export-csv $sitesFile -notypeinformation
$site.Dispose()
}
catch
{
Write-Host("Exception at Site Collection Url :" +$currentSite.Url)
$errorfile.WriteLine($currentSite.Url+"`t"+"SiteCollection"+"`t"+$_.Exception.Message)
}
}
Below is the Error I am getting
Export-Csv : Cannot bind argument to parameter 'InputObject' because it is null.
At C:\Users\EMXBG\Downloads\Script_AddSiteContent\Script_AddSiteContent\ScriptForSiteContentLinkQuickLaunch - Copy.ps1:126 char:29
+ $sitesCollection | export-csv $sitesFile -notypeinformation
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Export-Csv], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.ExportCsvCommand
This error is probably because $sitesCollection is empty/null. I can't see anything in your code that assigns it a value.

Powershell - "Invalid assignment expression." error

I've got a couple things that i'm working on. One of them is sort of an import/export thing i found on here. but i'm getting the following error
PS C:\Users\joeblogs> C:\Users\joeblogs\Scripts\Copy user data.ps1 Invalid assignment expression. The left hand side of an assignment
operator needs to be something that can be assigned to like a variable
or a property. At C:\Users\delpillay\Documents\Scripts\Copy user
data.ps1:16 char:12
+ $username = <<<< gc env:userame
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : InvalidLeftHandSide
I don't know where to start and i'm not sure what to try...
Below is the code:
$destination = "E:\Users\%username%\Backup"
$folder = "Desktop",
#"Downloads",
"Favorites",
"My Documents",
#"Music",
#"Pictures",
#"Videos",
#"AppData\Local\Mozilla",
#"AppData\Local\Google",
#"AppData\Roaming\Mozilla"
######################################
$username = gc env:userame
$userprofile = gc env:userprofile
##$appData = gc env:localAPPDATA
###### Restore data section ######
if ([IO.Directory]::Exists($destination + "\" + $username + "\"))
{
$caption = "Choose Action";
$message = "A backup folder for $username already exists, would you like to restore the data to the local machine?";
$Yes = new-Object System.Management.Automation.Host.ChoiceDescription "&Yes","Yes";
$No = new-Object System.Management.Automation.Host.ChoiceDescription "&No","No";
$choices = [System.Management.Automation.Host.ChoiceDescription[]]($Yes,$No);
$answer = $host.ui.PromptForChoice($caption,$message,$choices,0)
if ($answer -eq 0)
{
write-host -ForegroundColor green "Restoring data to local machine for $username"
foreach ($f in $folder)
{
$currentLocalFolder = $userprofile + "\" + $f
$currentRemoteFolder = $destination + "\" + $username + "\" + $f
write-host -ForegroundColor cyan " $f..."
Copy-Item -ErrorAction silentlyContinue -recurse $currentRemoteFolder $userprofile
if ($f -eq "AppData\Local\Mozilla") { rename-item $currentLocalFolder "$currentLocalFolder.old" }
if ($f -eq "AppData\Roaming\Mozilla") { rename-item $currentLocalFolder "$currentLocalFolder.old" }
if ($f -eq "AppData\Local\Google") { rename-item $currentLocalFolder "$currentLocalFolder.old" }
}
rename-item "$destination\$username" "$destination\$username.restored"
write-host -ForegroundColor green "Restore Complete!"
}
else
{
write-host -ForegroundColor yellow "Aborting process"
exit
}
}
###### Backup Data section ########
#else
{
Write-Host -ForegroundColor green "Outlook is about to close, save any unsaved emails then press any key to continue ..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Get-Process | Where { $_.Name -Eq "OUTLOOK" } | Kill
write-host -ForegroundColor green "Backing up data from local machine for $username"
foreach ($f in $folder)
{
$currentLocalFolder = $userprofile + "\" + $f
$currentRemoteFolder = $destination + "\" + $username + "\" + $f
$currentFolderSize = (Get-ChildItem -ErrorAction silentlyContinue $currentLocalFolder -Recurse -Force | Measure-Object -ErrorAction silentlyContinue -Property Length -Sum ).Sum / 1MB
$currentFolderSizeRounded = [System.Math]::Round($currentFolderSize)
write-host -ForegroundColor cyan " $f... ($currentFolderSizeRounded MB)"
Copy-Item -ErrorAction silentlyContinue -recurse $currentLocalFolder $currentRemoteFolder
}
$oldStylePST = [IO.Directory]::GetFiles($appData + "\Microsoft\Outlook", "*.pst")
foreach($pst in $oldStylePST)
{
if ((test-path -path ($destination + "\" + $username + "\Documents\Outlook Files\oldstyle")) -eq 0){new-item -type directory -path ($destination + "\" + $username + "\Documents\Outlook Files\oldstyle") | out-null}
write-host -ForegroundColor yellow " $pst..."
Copy-Item $pst ($destination + "\" + $username + "\Documents\Outlook Files\oldstyle")
}
write-host -ForegroundColor green "Backup complete!"
}
Few observations:
You are not commenting the Favourites and My Documents. If you want to use them then use comma separated directly.
Use this:
$destination = "E:\Users\%username%\Backup"
$folder = "Desktop","Favorites","My Documents"
#"Downloads",
#"Favorites",
#"My Documents",
#"Music",
#"Pictures",
#"Videos",
#"AppData\Local\Mozilla",
#"AppData\Local\Google",
#"AppData\Roaming\Mozilla"
You have missed the n in username:
$username = gc env:username
Donot use gc env:username. Instead directly access them like this below: Completely reframed:
$destination = "E:\Users\%username%\Backup"
$folder = "Desktop","Favorites","My Documents"
#"Downloads",
#"Favorites",
#"My Documents",
#"Music",
#"Pictures",
#"Videos",
#"AppData\Local\Mozilla",
#"AppData\Local\Google",
#"AppData\Roaming\Mozilla"
######################################
$username = $env:username
$userprofile = $env:userprofile
##$appData = gc env:localAPPDATA
These are the major things that have been fixed. Hope it helps you.

SPO Powershell Set Permissions Error in RoleDefinitionBindingCollection Call

On Sharepoint Online, using Powershell, I am trying to set list item permissions, and am finding dozens of tutorials that use a RoleDefinitionBindingCollection($ctx) call...
When I do this, though, I get the following error:
New-Object : Cannot find an overload for "RoleDefinitionBindingCollection" and
the argument count: "1".At
C:\Users\thebear\Desktop\SEDA\SEDASetIPPermissions.ps1:172 char:31
+ ... entReader = New-Object Microsoft.SharePoint.Client.RoleDefinitionBind ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [New-Object], MethodException
+ FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand
I am iterating through Doclib folders, then through the Folder.Files, checking for a value in a custom field, and setting the permissions on the matching items. EDIT: Here is the full code:
# cd 'C:\Users\thebear\Desktop\SEDA'
# .\SEDASetIPPermissions test KLY KLY1
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
If($($args.Count) -ne 3)
{
Write-Host “Usage: .\SEDASetIPPermissions <'prod' or 'test'> <ProgCode i.e. 'LCL' or 'All'> <IPGroup i.e. 'KLY1' or 'All'>"
break
}
$Site = if($args[0] -eq 'prod') {'sedasearch'} elseif ($args[0] -eq 'test') {'sedasearchtest'}
$Lib = $args[1]
$IPGroup = $args[2]
# Get Connected
$Cred = Get-Credential
$Credentials = New-Object -TypeName System.Management.Automation.PSCredential -argumentlist $Cred.UserName, $Cred.Password
$Url = "https://MySite.sharepoint.com/sites/$Site"
Connect-SPOnline -Url $Url -Credentials $Credentials
# Get Client Context
$ctx = Get-SPOContext
$ctx.RequestTimeout = 1000000
$ctx.ExecuteQuery()
# Get Web & Lists
$web = $ctx.Web
$ctx.Load($web)
$ctx.Load($web.Lists)
$ctx.Load($web.RoleDefinitions)
$ctx.ExecuteQuery()
$lists = $web.Lists
# Get Site Groups
$groups = $web.SiteGroups
$ctx.Load($groups)
$ctx.ExecuteQuery()
# Get Target Group
$groupFound = $false
$ScriptStart = Get-Date
foreach ($group in $groups)
{
if ($group.Title -eq "SEDA Admins")
{
$AdminGroupID = $group.Id
}
elseif($group.Title -eq $IPGroup + " Security Group")
{
$groupFound = $true
$IPGroupID = $group.Id
Write-Host "`n'$IPGroup Security Group' Found...`n" -ForegroundColor Green
}
}
if (!$groupFound) { Write-Host "`n'$IPGroup Security Group' NOT Found...`n" -ForegroundColor Red; break }
# Get Target List
$list = $lists.GetByTitle($Lib + " Library")
$ctx.Load($list)
$ctx.Load($list.RootFolder)
$ctx.Load($list.Fields)
$ctx.ExecuteQuery()
if($list -ne $null)
{ "`n'{0}' Found...`n" -f $list.Title | Write-Host -ForegroundColor Green }
else
{ "`n'{0}' NOT Found...`n" -f $list.Title | Write-Host -ForegroundColor Red; break }
# Get List Folders
$folders = $list.RootFolder.Folders
$ctx.Load($folders)
$ctx.ExecuteQuery()
$folders = $folders | sort Name
# Set Up Group and Admin Permissions (if not already there)
$RoleDefinitions = $web.RoleDefinitions
$ctx.Load($RoleDefinitions)
$ctx.ExecuteQuery()
$foundIPGroupRole = $false
$foundIPAdminRole = $false
foreach ($role in $RoleDefinitions)
{
if ($role.Name -eq "Read")
{
$IPGroupRole = $role
$foundIPGroupRole = $true
}
elseif ($role.Name -eq "Full Control")
{
$IPAdminRole = $role
$foundIPAdminRole = $true
}
}
# Set the permissions for 'IP Group'
$roleAssignmentReader = New-Object Microsoft.SharePoint.Client.RoleDefinitionBindingCollection($ctx)
$roleAssignmentReader.Add($IPGroupRole)
# Set the permissions for 'IP Admin'
$roleAssignmentAdmin = New-Object Microsoft.SharePoint.Client.RoleDefinitionBindingCollection($ctx)
$roleAssignmentAdmin.Add($IPAdminRole)
# Set Counters
$FileCount = 0
$FailCount = 0
foreach ($folder in $folders)
{
$FolderFileCount = 0
$ctx.Load($folder)
$ctx.Load($folder.ListItemAllFields)
$ctx.ExecuteQuery()
if ($folder.ItemCount -lt 5000)
{
$files = $folder.Files
$ctx.Load($files)
$ctx.ExecuteQuery()
"`nProcessing Folder {0}..." -f $folder.Name | Write-Host -ForegroundColor Green
}
else
{ "`nFolder {0} Exceeds 5000 Items...`n" -f $folder.Url | Write-Host -ForegroundColor Red; continue }
foreach ($file in $files)
{
$ctx.Load($file)
$ctx.Load($file.ListItemAllFields)
$ctx.ExecuteQuery()
$item = $file.ListItemAllFields
$ctx.Load($item)
$ctx.ExecuteQuery()
$name = $file.Name
$group = $item.get_item('IPGroup')
if($group -eq $IPGroup)
{
"`nProcessing File {0}...`n" -f $name | Write-Host -ForegroundColor Green;
# Break inheritance on the list item and remove existing permissons.
# NOTE: Use $item.ResetRoleInheritance() to Restore Roll Inheritance
$item.BreakRoleInheritance($false, $true)
# Apply the two permission roles to the list item.
$ctx.Load($item.RoleAssignments.Add($IPGroupID, $roleAssignmentReader))
$ctx.Load($item.RoleAssignments.Add($AdminGroupID, $roleAssignmentAdmin))
# Update the list item and execute
$item.Update()
$ctx.ExecuteQuery()
"`nProcessed File {0}...`n" -f $name | Write-Host -ForegroundColor Green;
}
$FolderFileCount += 1
if($FolderFileCount % 1000 -eq 0) { "{0}K" -f ($FolderFileCount/1000).ToString() | Write-Host }
elseif($FolderFileCount % 100 -eq 0) {Write-Host '*'}
else {Write-Host -NoNewline '.'}
}
}
“`n{0} Files Processed, {1} Error(s), Elapsed Time: {2}" -f $FileCount, $FailCount, $((Get-Date) - $ScriptStart) | Write-Host
$ctx appears to be legit... what else could be causing this error (for a day now)?
This error occurs since RoleDefinitionBindingCollection constructor expects ClientRuntimeContext object but the following line:
$ctx = Get-SPOContext
returns object of OfficeDevPnP.Core.PnPClientContext type. Even though it inherits from ClientRuntimeContext object (PnPClientContext -> ClientContext -> ClientRuntimeContext) it could not be used for instantiating of Microsoft.SharePoint.Client.RoleDefinitionBindingCollection object.
Solution
One option would be to replace the lines:
Connect-SPOnline -Url $Url -Credentials $Credentials
#Get Client Context
$ctx = Get-SPOContext
with
$ctx = Get-Context -WebUrl $Url -UserName $Credentials.UserName -Password $Credentials.Password
where
Function Get-Context([String]$WebUrl,[String]$UserName,[System.Security.SecureString]$Password) {
$context = New-Object Microsoft.SharePoint.Client.ClientContext($WebUrl)
$context.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($UserName, $Password)
return $context
}
which returns Microsoft.SharePoint.Client.ClientContext object.
According to this link below. The Microsoft.SharePoint.Client.RoleDefinitionBindingCollection($ctx) is looking for a url as an argument, not a filename.
https://msdn.microsoft.com/en-us/library/microsoft.sharepoint.client.roledefinitionbindingcollection.aspx