The question appears when a site owner is playing withe the permission set and remove his group from the object (list or site). The same question is existing when you are loading the content from an On Premise SharePoint farm (in which the site collection admin could be given to the site owner) to SharePoint Online, and you need to reset the Permission set for that Site Owner group.
This PowerShell script is using CSOM Object to loop into all the site collection (in all the subsites) and apply the Full Control permission into any list and Subsite (it's not going deeper in the List content level).
In the same script, the Site Access request is configured with the given email address to delegate the permission management to the defined Site Onwer.
[string]$username = "LoginAccount@tenant.onmicrosoft.com" [string]$PwdTXTPath = "C:\\SECUREDPWD\ExportedPWD-$($username).txt" [string]$SPOSiteCollectionURLToSet = “https://tenant.sharepoint.com/sites/MySiteCollection” [string]$RootSiteOwnerGroupToSet = "MySiteCollection Owners" [string]$SiteOwnerEmailAdress = "SiteOnwerEmail@mycompany.com" [boolean]$ChangeRequestAccessEmail = $false [string]$RoleTypeToApply = "Administrator" function Load-DLLandAssemblies { [string]$defaultDLLPath = "" # Load assemblies to PowerShell session $defaultDLLPath = "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.dll" [System.Reflection.Assembly]::LoadFile($defaultDLLPath) $defaultDLLPath = "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.Runtime.dll" [System.Reflection.Assembly]::LoadFile($defaultDLLPath) $defaultDLLPath = "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.Online.SharePoint.Client.Tenant.dll" [System.Reflection.Assembly]::LoadFile($defaultDLLPath) } Function Invoke-LoadMethod() { param( [Microsoft.SharePoint.Client.ClientObject]$Object = $(throw "Please provide a Client Object"), [string]$PropertyName ) $ctx = $Object.Context $load = [Microsoft.SharePoint.Client.ClientContext].GetMethod("Load") $type = $Object.GetType() $clientLoad = $load.MakeGenericMethod($type) $Parameter = [System.Linq.Expressions.Expression]::Parameter(($type), $type.Name) $Expression = [System.Linq.Expressions.Expression]::Lambda( [System.Linq.Expressions.Expression]::Convert( [System.Linq.Expressions.Expression]::PropertyOrField($Parameter,$PropertyName), [System.Object] ), $($Parameter) ) $ExpressionArray = [System.Array]::CreateInstance($Expression.GetType(), 1) $ExpressionArray.SetValue($Expression, 0) $clientLoad.Invoke($ctx,@($Object,$ExpressionArray)) } Function Add-Group-As-FullPermission-InSPWeb() { Param( [Microsoft.SharePoint.Client.ClientContext]$Context, [string]$SPGroupName, [Microsoft.SharePoint.Client.Web]$SPWeb, [string]$RoleType ) Write-Host " ---------------------------------------------------------" # get group/principal $Mygroups = $SPWeb.SiteGroups $MyRootWeb = $Context.Site.RootWeb $context.Load($MyRootWeb) $Context.Load($Mygroups) $Context.ExecuteQuery() <# foreach($MyTempGroup in $Mygroups) { Write-Host " --->>> Group In Group List: ", $MyTempGroup.Title } #> $Mygroup = $Mygroups | where {$_.Title -eq $SPGroupName} #$MyRoleType = [Microsoft.SharePoint.Client.RoleType]$Roletype $roleDefs = $SPWeb.RoleDefinitions $Context.Load($roleDefs) $Context.ExecuteQuery() $roleDef = $roleDefs | where {$_.RoleTypeKind -eq $Roletype} Write-Host " --- Role Definition: ", $roleDef.Name Write-Host " --- Group Name: ", $Mygroup.Title, " - Original Name:", $SPGroupName try{ $collRdb = new-object Microsoft.SharePoint.Client.RoleDefinitionBindingCollection($Context) $collRdb.Add($roleDef) $collRoleAssign = $SPWeb.RoleAssignments $rollAssign = $collRoleAssign.Add($Mygroup, $collRdb) $Context.ExecuteQuery() Write-Host " >>>>> Permissions assigned successfully." -ForegroundColor Green } catch{ write-host " !!!!!!! info: $($_.Exception.Message)" -foregroundcolor red } Write-Host " ---------------------------------------------------------" } Function Add-Group-As-FullPermission-InSPList() { Param( [Microsoft.SharePoint.Client.ClientContext]$Context, [string]$SPGroupName, [Microsoft.SharePoint.Client.Web]$SPWeb, [Microsoft.SharePoint.Client.List]$SPList, [string]$RoleType ) Write-Host " ---------------------------------------------------------" # get group/principal $Mygroups = $SPWeb.SiteGroups $MyRootWeb = $Context.Site.RootWeb $context.Load($MyRootWeb) $Context.Load($Mygroups) $context.Load($SPList) $Context.ExecuteQuery() <# foreach($MyTempGroup in $Mygroups) { Write-Host " --->>> Group In Group List: ", $MyTempGroup.Title } #> $Mygroup = $Mygroups | where {$_.Title -eq $SPGroupName} #$MyRoleType = [Microsoft.SharePoint.Client.RoleType]$Roletype # get role definition $roleDefs = $SPWeb.RoleDefinitions $Context.Load($roleDefs) $Context.ExecuteQuery() $roleDef = $roleDefs | where {$_.RoleTypeKind -eq $Roletype} Write-Host " --- Role Definition: ", $roleDef.Name Write-Host " --- Group Name: ", $Mygroup.Title, " - Original Name:", $SPGroupName try{ $collRdb = new-object Microsoft.SharePoint.Client.RoleDefinitionBindingCollection($Context) $collRdb.Add($roleDef) $collRoleAssign = $SPList.RoleAssignments $rollAssign = $collRoleAssign.Add($Mygroup, $collRdb) $Context.ExecuteQuery() Write-Host " >>>>> Permissions assigned successfully." -ForegroundColor Green } catch{ write-host " !!!!!!! info: $($_.Exception.Message)" -foregroundcolor red } Write-Host " ---------------------------------------------------------" } function Check-Permission-InLists { Param( [Microsoft.SharePoint.Client.ClientContext]$Context, [Microsoft.SharePoint.Client.Web]$SPWeb ) $Mylists = $SPWeb.Lists; $Context.Load($Mylists) #you can use one execute per multiple loads $Context.ExecuteQuery(); Write-host " ---- CHECK IN LISTS --- " foreach($myList in $MyLists) { Write-host " ==== List Name:", $mylist.Title if($mylist.hidden -eq $false) { Write-host " ====> List Name not hidden:", $mylist.Title -ForegroundColor Yellow Invoke-LoadMethod -Object $myList -PropertyName "HasUniqueRoleAssignments" $context.ExecuteQuery() if($myList.HasUniqueRoleAssignments) { Write-Host " -->> List in the SPWeb:", $myList.Title -ForegroundColor Yellow Write-Host " -->> Has Unique Permissions:", $myList.HasUniqueRoleAssignments Add-Group-As-FullPermission-InSPList -Context $Context -SPGroupName $RootSiteOwnerGroupToSet -SPWeb $SPWeb -SPList $myList -RoleType $RoleTypeToApply } } } } function Get-SPOSubWebs { Param( [Microsoft.SharePoint.Client.ClientContext]$Context, [Microsoft.SharePoint.Client.Web]$RootWeb ) $Webs = $RootWeb.Webs $Context.Load($Webs) $Context.ExecuteQuery() ForEach ($sWeb in $Webs) { Write-host " -------------------------------------------------------- " Write-host " -->> SubSite:", $sWeb.URL -ForegroundColor green Invoke-LoadMethod -Object $sWeb -PropertyName "HasUniqueRoleAssignments" $context.ExecuteQuery() Write-Host " -->> Has Unique Permissions:", $sWeb.HasUniqueRoleAssignments if($sWeb.HasUniqueRoleAssignments) { Invoke-LoadMethod -Object $sWeb -PropertyName "RequestAccessEmail" $context.ExecuteQuery() Write-Host " -->> Request Access Email Before change:", $sWeb.RequestAccessEmail if(($ChangeRequestAccessEmail) -and ($sWeb.RequestAccessEmail -ne $SiteOwnerEmailAdress)) { Write-Host " ===->> Request Access Email to change" $sWeb.RequestAccessEmail = $SiteOwnerEmailAdress $sWeb.Update() $context.ExecuteQuery() Invoke-LoadMethod -Object $sWeb -PropertyName "RequestAccessEmail" $context.ExecuteQuery() Write-Host " -->> Request Access Email After change:", $sWeb.RequestAccessEmail } Add-Group-As-FullPermission-InSPWeb -Context $Context -SPGroupName $RootSiteOwnerGroupToSet -SPWeb $sWeb -RoleType $RoleTypeToApply } Check-Permission-InLists -Context $Myctx -SPWeb $sWeb Get-SPOSubWebs -Context $Context -RootWeb $sWeb } } function Reset-Group-OwnerShip { Param( [Microsoft.SharePoint.Client.ClientContext]$Context, # [Microsoft.SharePoint.Client.Web]$SPWeb, [string]$GroupOwnerName ) $MyRootWeb = $Context.Site.RootWeb $Mygroups = $MyRootWeb.SiteGroups $context.Load($MyRootWeb) $Context.Load($Mygroups) $Context.ExecuteQuery() foreach($MyTempGroup in $Mygroups) { Write-Host " --->>> Group Name: ", $MyTempGroup.Title $ThegroupOwner = $Context.Web.SiteGroups.GetByName($GroupOwnerName); $MyTempGroup.Owner = $ThegroupOwner $MyTempGroup.Update() $Context.ExecuteQuery() } } function SetGroupAsFullOwner([string]$MyRootWebURL) { [bool]$CreateSGSDocLibList = $false $Myctx = New-Object Microsoft.SharePoint.Client.ClientContext($MyRootWebURL) $secureStringPwd = ConvertTo-SecureString -string (Get-Content $PwdTXTPath) $creds = New-Object System.Management.Automation.PSCredential -ArgumentList $username, $secureStringPwd $Myctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($creds.UserName,$creds.Password) $Myctx.RequestTimeout = 1000000 # milliseconds $MyspoRootweb = $Myctx.Web $Myctx.Load($MyspoRootweb) $Myctx.ExecuteQuery() Write-Host " " Write-Host " ---------------------------------------------------------" Write-Host " >>>> # Server Version:" $Myctx.ServerVersion " # <<<<<<" -ForegroundColor Green Write-Host " ---------------------------------------------------------" Write-Host " " Write-host " -------------------------------------------------------- " Write-host " -->> RootSite:", $MyspoRootweb.URL -ForegroundColor green Reset-Group-OwnerShip -Context $Myctx -GroupOwnerName $RootSiteOwnerGroupToSet Invoke-LoadMethod -Object $MyspoRootweb -PropertyName "RequestAccessEmail" $Myctx.ExecuteQuery() Write-Host " -->> Request Access Email Before change:", $MyspoRootweb.RequestAccessEmail if($MyspoRootweb.RequestAccessEmail -ne $SiteOwnerEmailAdress) { Write-Host " ===->> Request Access Email to change" $MyspoRootweb.RequestAccessEmail = $SiteOwnerEmailAdress $MyspoRootweb.Update() $Myctx.ExecuteQuery() Invoke-LoadMethod -Object $MyspoRootweb -PropertyName "RequestAccessEmail" $Myctx.ExecuteQuery() Write-Host " -->> Request Access Email After change:", $MyspoRootweb.RequestAccessEmail } Check-Permission-InLists -Context $Myctx -SPWeb $MyspoRootweb Get-SPOSubWebs -Context $Myctx -RootWeb $MyspoRootweb } cls Load-DLLandAssemblies SetGroupAsFullOwner $SPOSiteCollectionURLToSet
I'm using that script since many months to reapply the permission set after site content migration from SharePoint 2007 to SharePoint Online, the maximum test validation was done with one site collection having more than 2900 Subsites.
Original Post message:
Fabrice Romelard [MBA Risk Management]
Sites Sources:
- https://gist.github.com/star-crossed/0062ec1e5ef622a4c8e8
- http://sharepoint.stackexchange.com/questions/126221/spo-retrieve-hasuniqueroleassignements-property-using-powershell
- https://social.technet.microsoft.com/wiki/contents/articles/31157.manage-sharepoint-online-access-requests-using-powershell.aspx
- https://blogs.msdn.microsoft.com/chandru/2015/12/31/sharepoint-onlinecsom-change-access-requests-settings/
- http://www.sharepointdiary.com/2014/12/delete-unique-permissions-reset-broken-inheritance-using-powershell.html
- https://gist.github.com/asadrefai/b854cf8ca1719a3fcc73
- https://gallery.technet.microsoft.com/office/Script-to-get-All-Webs-in-c2c1cdaf