Here is a script that comes very handy when you need to replace some SharePoint groups with other ones. While doing so you definitely want to make sure no one has lost access. So you need to know where each group has been used.
To create this script I used Salaudeen Rajack’s PnP PowerShell to Export Document Library Permissions in SharePoint Online script posted here (https://www.sharepointdiary.com/2019/02/sharepoint-online-pnp-powershell-to-export-document-library-permissions.html), then did some adjustments to it including converting it to a function. Then added my code to go through each site collection and subsite.
What this script does and how it might be beneficial to you:
- goes through every site collection and subsite, then library and list;
- has a list of site collections to exclude;
- does not include Office 365 Group sites;
- outputs the following data – site/subsite URL, library/list title, user/group name, user/group type, permission level, how it’s granted.
This script might take hours if you have lots of site collections and/or subsites.
#--------------------------------------[Variables]-------------------------------------- $AdminSiteUrl = "https://contoso-admin.sharepoint.com/" $Exclusions = @( "https://contoso.sharepoint.com/portals/hub", "https://contoso-my.sharepoint.com/", "https://contoso.sharepoint.com/portals/Community", "https://contoso.sharepoint.com/sites/dev", "https://contoso.sharepoint.com/search" ) $OutputFile = "C:\Temp\AllPermissions.csv" #-------------------------------------[Function 1]-------------------------------------- Function Get-AllPermissions { [CmdletBinding(DefaultParameterSetName="MainSiteSet")] param ( [Parameter(ParameterSetName="MainSiteSet", Mandatory=$True)] $Title, [Parameter(ParameterSetName="SubsiteSet", Mandatory=$True)] $SubsiteUrl, [Parameter(ParameterSetName="SubsiteSet", Mandatory=$True)] $SubsiteTitle ) Begin{} Process{ # Get the document library depending if it's within a main Site in the Site Collection or Subsite If ($PSCmdlet.ParameterSetName -eq "MainSiteSet") { $Library = Get-PnpList -Identity $Title -Includes RoleAssignments } Else { $Library = Get-PnpList -Identity $SubsiteTitle -Web $SubsiteUrl -Includes RoleAssignments } # Get all users and groups who has access $RoleAssignments = $Library.RoleAssignments $PermissionCollection = @() Foreach ($RoleAssignment in $RoleAssignments) { #Get the Permission Levels assigned and Member Get-PnPProperty -ClientObject $roleAssignment -Property RoleDefinitionBindings, Member #Get the Principal Type: User, SP Group, AD Group $PermissionType = $RoleAssignment.Member.PrincipalType $PermissionLevels = $RoleAssignment.RoleDefinitionBindings | Select -ExpandProperty Name #Get all permission levels assigned (Excluding:Limited Access) $PermissionLevels = ($PermissionLevels | Where { $_ βne "Limited Access"}) -join "," If($PermissionLevels.Length -eq 0) {Continue} #Get SharePoint group members If($PermissionType -eq "SharePointGroup") { #Get Group Members $GroupMembers = Get-PnPGroupMembers -Identity $RoleAssignment.Member.LoginName #Leave Empty Groups If($GroupMembers.count -eq 0){Continue} ForEach($User in $GroupMembers) { $outObject = "" | Select Site,Title,User,Type,Permissions,GrantedThrough $outObject."Site" = If ($PSCmdlet.ParameterSetName -eq "MainSiteSet") { $SiteCollection.URL } Else { $Subsite.Url } $outObject."Title" = $Library.Title $outObject."User" = $User.Title $outObject."Type" = $PermissionType $outObject."Permissions" = $PermissionLevels $outObject."GrantedThrough" = "SharePoint Group: $($RoleAssignment.Member.LoginName)" $PermissionCollection += $outObject } } Else { $outObject = "" | Select Site,Title,User,Type,Permissions,GrantedThrough $outObject."Site" = If ($PSCmdlet.ParameterSetName -eq "MainSiteSet") { $SiteCollection.URL } Else { $Subsite.Url } $outObject."Title" = $Library.Title $outObject."User" = $RoleAssignment.Member.Title $outObject."Type" = $PermissionType $outObject."Permissions" = $PermissionLevels $outObject."GrantedThrough" = "Direct Permissions" $PermissionCollection += $outObject } } $PermissionCollection } End{} } #--------------------------------------------------------------------------------------- # Start timer $elapsed = [System.Diagnostics.Stopwatch]::StartNew() # Connect to SharePoint Online Admin Connect-PnPOnline -Url $AdminSiteUrl -Credentials $UserCredential # Get all Site Collections that are not Office 365 Group Site Collections and not in the Exclusion List $SiteCollections = Get-SPOSite | Where-Object {($Exclusions -cnotcontains $_.Url) -and ($_.Template -ne "GROUP#0")} Foreach ($SiteCollection in $SiteCollections) { # Connect to each site collection Connect-PnPOnline -Url $SiteCollection.Url -Credentials $UserCredential # Get libraries within the main site of the site collection $ListsAndLibraries = Get-PnPList $AllPermissionsCollection = @() Foreach ($ListAndLibrary in $ListsAndLibraries) { Write-Host "Processing" $ListAndLibrary.RootFolder.ServerRelativeUrl.Substring(1) -ForegroundColor Yellow $Permissions = Get-AllPermissions -Title $ListAndLibrary.Title $AllPermissionsCollection += $Permissions } # Get all subsites $Subsites = Get-PnPSubWebs -Recurse foreach ($Subsite in $Subsites) { $ListsAndLibraries = Get-PnPList -Web $Subsite.ServerRelativeUrl Foreach ($ListAndLibrary in $ListsAndLibraries) { Write-host $ListAndLibrary.Url Write-Host "Processing" $ListAndLibrary.RootFolder.ServerRelativeUrl.Substring(1) -ForegroundColor Yellow $Permissions = Get-AllPermissions -SubsiteTitle $ListAndLibrary.Title -SubsiteUrl $Subsite.ServerRelativeUrl $AllPermissionsCollection += $Permissions } } $AllSitesPermissionsCollection += $AllPermissionsCollection } # Export results to CSV $AllSitesPermissionsCollection | Export-CSV $OutputFile -NoTypeInformation Write-host "Done!" -ForegroundColor Green # End Timer Write-Host "Total Time: $($elapsed.Elapsed.ToString())"
Hello! Thanks for such a beautifully written script, it helped me a lot π
But i got a little issue here… After i run it, the output file doesn’t show me the name of the root site, only the subsites. It’s strange because it still iterates through every site and shows me all permissions for all available libraries and lists, but it doesn’t return the name of the root. Any idea how to fix this? Thanks!
Hi Adrian,
You can feed that value into the function itself as a property and use it there just as another column, so not doing anything with that property other than putting it into the column.
He guys i got this
Last 200 Keys:
Space Space Space Space Space Space $ P e r m i s s i o n T y p e Space = Space $ R o l e A s s i g n m e n t . M e m b e r . P r i n c i p a l T y p e Enter
Space Space Space Space Space Space Space Space Space Space Space Space $ P e r m i s s i o n L e v e l s Space = Space $ R o l e A s s i g n m e n t . R o l e D e f i n i t i o n B i n d i
n g s Space | Space S e l e c t Space – E x p a n d P r o p e r t y Space N a m e Enter
Space Space Space Space Space Enter
Space Space Space Space Space Space Space Space Space Space Space Space # G e t Space a l l Space p e r m i s s i o n Space
Exception:
System.ArgumentOutOfRangeException: De waarde moet groter zijn dan of gelijk zijn aan nul en minder zijn dan de buffergrootte van de console in die dimensie.
Parameternaam: top
Werkelijke waarde was -11.
bij System.Console.SetCursorPosition(Int32 left, Int32 top)
bij Microsoft.PowerShell.PSConsoleReadLine.ReallyRender(RenderData renderData, String defaultColor)
bij Microsoft.PowerShell.PSConsoleReadLine.ForceRender()
bij Microsoft.PowerShell.PSConsoleReadLine.Insert(Char c)
bij Microsoft.PowerShell.PSConsoleReadLine.SelfInsert(Nullable`1 key, Object arg)
bij Microsoft.PowerShell.PSConsoleReadLine.ProcessOneKey(ConsoleKeyInfo key, Dictionary`2 dispatchTable, Boolean ignoreIfNoAction, Object arg)
bij Microsoft.PowerShell.PSConsoleReadLine.InputLoop()
bij Microsoft.PowerShell.PSConsoleReadLine.ReadLine(Runspace runspace, EngineIntrinsics engineIntrinsics)
Hi Quinttin,
It’s hard to read your message, could you please reformat it and provide more info? Thanks!
Good afternoon, you will save me a bunch of time, i have run other scripts but this is the one i am looking for permission on subsites, i am getting the below error on the code, Could you shed some light
below is the output on my PowerShell
kind regards
Andrew
Get-SPOSite : The term ‘Get-SPOSite’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:96 char:20
+ $SiteCollections = Get-SPOSite | Where-Object {($Exclusions -cnotcont …
+ ~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Get-SPOSite:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Export-Csv : Cannot bind argument to parameter ‘InputObject’ because it is null.
At line:123 char:34
+ … itesPermissionsCollection | Export-CSV $OutputFile -NoTypeInformation
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Export-Csv], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.ExportCsvCommand
Hi Andrew,
Sorry for the late reply – are you sure your SharePoint Online Management Shell module is installed and loaded in the current PowerShell session, and you are connected to it?