PowerShell – SharePoint – Permission report for all lists and libraries within every site collection and subsite

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.

$AdminSiteUrl = "https://contoso-admin.sharepoint.com/"

$Exclusions = @(

$OutputFile = "C:\Temp\AllPermissions.csv"
#-------------------------------------[Function 1]--------------------------------------
Function Get-AllPermissions {

    param (
        [Parameter(ParameterSetName="MainSiteSet", Mandatory=$True)] $Title,
        [Parameter(ParameterSetName="SubsiteSet", Mandatory=$True)] $SubsiteUrl,
        [Parameter(ParameterSetName="SubsiteSet", Mandatory=$True)] $SubsiteTitle


        # 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") {
                    Else {
                    $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") {
                Else {
                $outObject."Title" = $Library.Title
                $outObject."User" = $RoleAssignment.Member.Title
                $outObject."Type" = $PermissionType
                $outObject."Permissions" = $PermissionLevels
                $outObject."GrantedThrough" = "Direct Permissions"
                $PermissionCollection += $outObject


# 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())"


This Post Has 4 Comments

  1. Adrian

    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!

    1. Pavel Bludov

      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.

  2. Quinttin

    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

    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)

    1. Paul Bludov

      Hi Quinttin,
      It’s hard to read your message, could you please reformat it and provide more info? Thanks!

Leave a Reply