SharePoint Online: User Permissions Audit Report for a Site Collection using PnP PowerShell

Requirement: Need a report to audit SharePoint Online site collection permissions.

SharePoint Online: User Permissions report in a Site Collection using PnP PowerShell

Auditing SharePoint Online Site permissions is critical for security, as misconfigured permissions may enable users to access to data that they should not have access to. So, regular auditing of SharePoint permissions is crucial to minimizing the risk of data leaks and compliance violations. As there are no out-of-the-box ways to generate a permission report, we can utilize PowerShell to get a list of effective permissions for your SharePoint site collections. This PowerShell script generates a permission report on all objects, such as the SharePoint Online Site collection and its subsites, lists and libraries, folders, and list items with unique permissions on the given site collection. It scans through the following securables and produces a SharePoint Online security report:

  • Site collection administrators group
  • Given site collection and sub-sites with unique permissions
  • All lists and libraries with unique permissions
  • All list items (and folders) with unique permissions.
You must add the account that runs this PowerShell Scripts as “Site Collection Administrator” to all sites!
How to Add Site Collection Administrator to All SharePoint Online Sites?

SharePoint Online PowerShell Permissions Report

Let’s build a SharePoint Online security report with PowerShell. Here is the PnP PowerShell script to generate a user permissions report for a SharePoint Online site collection! Set the SiteURL and ReportFile parameters in the script according to your environment and execute the script. You’ll get a prompt to enter username and Password.

#Function to Get Permissions Applied on a particular Object, such as: Web, List, Folder or List Item
Function Get-PnPPermissions([Microsoft.SharePoint.Client.SecurableObject]$Object)
{
    #Determine the type of the object
    Switch($Object.TypedObject.ToString())
    {
        "Microsoft.SharePoint.Client.Web"  { $ObjectType = "Site" ; $ObjectURL = $Object.URL; $ObjectTitle = $Object.Title }
        "Microsoft.SharePoint.Client.ListItem"
        { 
            If($Object.FileSystemObjectType -eq "Folder")
            {
                $ObjectType = "Folder"
                #Get the URL of the Folder 
                $Folder = Get-PnPProperty -ClientObject $Object -Property Folder
                $ObjectTitle = $Object.Folder.Name
                $ObjectURL = $("{0}{1}" -f $Web.Url.Replace($Web.ServerRelativeUrl,''),$Object.Folder.ServerRelativeUrl)
            }
            Else #File or List Item
            {
                #Get the URL of the Object
                Get-PnPProperty -ClientObject $Object -Property File, ParentList
                If($Object.File.Name -ne $Null)
                {
                    $ObjectType = "File"
                    $ObjectTitle = $Object.File.Name
                    $ObjectURL = $("{0}{1}" -f $Web.Url.Replace($Web.ServerRelativeUrl,''),$Object.File.ServerRelativeUrl)
                }
                else
                {
                    $ObjectType = "List Item"
                    $ObjectTitle = $Object["Title"]
                    #Get the URL of the List Item
                    $DefaultDisplayFormUrl = Get-PnPProperty -ClientObject $Object.ParentList -Property DefaultDisplayFormUrl                     
                    $ObjectURL = $("{0}{1}?ID={2}" -f $Web.Url.Replace($Web.ServerRelativeUrl,''), $DefaultDisplayFormUrl,$Object.ID)
                }
            }
        }
        Default 
        { 
            $ObjectType = "List or Library"
            $ObjectTitle = $Object.Title
            #Get the URL of the List or Library
            $RootFolder = Get-PnPProperty -ClientObject $Object -Property RootFolder     
            $ObjectURL = $("{0}{1}" -f $Web.Url.Replace($Web.ServerRelativeUrl,''), $RootFolder.ServerRelativeUrl)
        }
    }
  
    #Get permissions assigned to the object
    Get-PnPProperty -ClientObject $Object -Property HasUniqueRoleAssignments, RoleAssignments

    #Check if Object has unique permissions
    $HasUniquePermissions = $Object.HasUniqueRoleAssignments
    
    #Loop through each permission assigned and extract details
    $PermissionCollection = @()
    Foreach($RoleAssignment in $Object.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
   
        #Get the Permission Levels assigned
        $PermissionLevels = $RoleAssignment.RoleDefinitionBindings | Select -ExpandProperty Name

        #Remove Limited Access
        $PermissionLevels = ($PermissionLevels | Where { $_ -ne "Limited Access"}) -join ","

        #Leave Principals with no Permissions
        If($PermissionLevels.Length -eq 0) {Continue}

        #Get SharePoint group members
        If($PermissionType -eq "SharePointGroup")
        {
            #Get Group Members
            $GroupMembers = Get-PnPGroupMember -Identity $RoleAssignment.Member.LoginName
                
            #Leave Empty Groups
            If($GroupMembers.count -eq 0){Continue}
            $GroupUsers = ($GroupMembers | Select -ExpandProperty Title) -join ","

            #Add the Data to Object
            $Permissions = New-Object PSObject
            $Permissions | Add-Member NoteProperty Object($ObjectType)
            $Permissions | Add-Member NoteProperty Title($ObjectTitle)
            $Permissions | Add-Member NoteProperty URL($ObjectURL)
            $Permissions | Add-Member NoteProperty HasUniquePermissions($HasUniquePermissions)
            $Permissions | Add-Member NoteProperty Users($GroupUsers)
            $Permissions | Add-Member NoteProperty Type($PermissionType)
            $Permissions | Add-Member NoteProperty Permissions($PermissionLevels)
            $Permissions | Add-Member NoteProperty GrantedThrough("SharePoint Group: $($RoleAssignment.Member.LoginName)")
            $PermissionCollection += $Permissions
        }
        Else
        {
            #Add the Data to Object
            $Permissions = New-Object PSObject
            $Permissions | Add-Member NoteProperty Object($ObjectType)
            $Permissions | Add-Member NoteProperty Title($ObjectTitle)
            $Permissions | Add-Member NoteProperty URL($ObjectURL)
            $Permissions | Add-Member NoteProperty HasUniquePermissions($HasUniquePermissions)
            $Permissions | Add-Member NoteProperty Users($RoleAssignment.Member.Title)
            $Permissions | Add-Member NoteProperty Type($PermissionType)
            $Permissions | Add-Member NoteProperty Permissions($PermissionLevels)
            $Permissions | Add-Member NoteProperty GrantedThrough("Direct Permissions")
            $PermissionCollection += $Permissions
        }
    }
    #Export Permissions to CSV File
    $PermissionCollection | Export-CSV $ReportFile -NoTypeInformation -Append
}
  
#Function to get sharepoint online site permissions report
Function Generate-PnPSitePermissionRpt()
{
[cmdletbinding()]

    Param  
    (    
        [Parameter(Mandatory=$false)] [String] $SiteURL, 
        [Parameter(Mandatory=$false)] [String] $ReportFile,         
        [Parameter(Mandatory=$false)] [switch] $Recursive,
        [Parameter(Mandatory=$false)] [switch] $ScanItemLevel,
        [Parameter(Mandatory=$false)] [switch] $IncludeInheritedPermissions        
    )  
    Try {
        #Connect to the Site
        Connect-PnPOnline -URL $SiteURL -Interactive
        #Get the Web
        $Web = Get-PnPWeb

        Write-host -f Yellow "Getting Site Collection Administrators..."
        #Get Site Collection Administrators
        $SiteAdmins = Get-PnPSiteCollectionAdmin
        
        $SiteCollectionAdmins = ($SiteAdmins | Select -ExpandProperty Title) -join ","
        #Add the Data to Object
        $Permissions = New-Object PSObject
        $Permissions | Add-Member NoteProperty Object("Site Collection")
        $Permissions | Add-Member NoteProperty Title($Web.Title)
        $Permissions | Add-Member NoteProperty URL($Web.URL)
        $Permissions | Add-Member NoteProperty HasUniquePermissions("TRUE")
        $Permissions | Add-Member NoteProperty Users($SiteCollectionAdmins)
        $Permissions | Add-Member NoteProperty Type("Site Collection Administrators")
        $Permissions | Add-Member NoteProperty Permissions("Site Owner")
        $Permissions | Add-Member NoteProperty GrantedThrough("Direct Permissions")
              
        #Export Permissions to CSV File
        $Permissions | Export-CSV $ReportFile -NoTypeInformation
  
        #Function to Get Permissions of All List Items of a given List
        Function Get-PnPListItemsPermission([Microsoft.SharePoint.Client.List]$List)
        {
            Write-host -f Yellow "`t `t Getting Permissions of List Items in the List:"$List.Title
 
            #Get All Items from List in batches
            $ListItems = Get-PnPListItem -List $List -PageSize 500
 
            $ItemCounter = 0
            #Loop through each List item
            ForEach($ListItem in $ListItems)
            {
                #Get Objects with Unique Permissions or Inherited Permissions based on 'IncludeInheritedPermissions' switch
                If($IncludeInheritedPermissions)
                {
                    Get-PnPPermissions -Object $ListItem
                }
                Else
                {
                    #Check if List Item has unique permissions
                    $HasUniquePermissions = Get-PnPProperty -ClientObject $ListItem -Property HasUniqueRoleAssignments
                    If($HasUniquePermissions -eq $True)
                    {
                        #Call the function to generate Permission report
                        Get-PnPPermissions -Object $ListItem
                    }
                }
                $ItemCounter++
                Write-Progress -PercentComplete ($ItemCounter / ($List.ItemCount) * 100) -Activity "Processing Items $ItemCounter of $($List.ItemCount)" -Status "Searching Unique Permissions in List Items of '$($List.Title)'"
            }
        }

        #Function to Get Permissions of all lists from the given web
        Function Get-PnPListPermission([Microsoft.SharePoint.Client.Web]$Web)
        {
            #Get All Lists from the web
            $Lists = Get-PnPProperty -ClientObject $Web -Property Lists
  
            #Exclude system lists
            $ExcludedLists = @("Access Requests","App Packages","appdata","appfiles","Apps in Testing","Cache Profiles","Composed Looks","Content and Structure Reports","Content type publishing error log","Converted Forms",
            "Device Channels","Form Templates","fpdatasources","Get started with Apps for Office and SharePoint","List Template Gallery", "Long Running Operation Status","Maintenance Log Library", "Images", "site collection images"
            ,"Master Docs","Master Page Gallery","MicroFeed","NintexFormXml","Quick Deploy Items","Relationships List","Reusable Content","Reporting Metadata", "Reporting Templates", "Search Config List","Site Assets","Preservation Hold Library",
            "Site Pages", "Solution Gallery","Style Library","Suggested Content Browser Locations","Theme Gallery", "TaxonomyHiddenList","User Information List","Web Part Gallery","wfpub","wfsvc","Workflow History","Workflow Tasks", "Pages")
            
            $Counter = 0
            #Get all lists from the web   
            ForEach($List in $Lists)
            {
                #Exclude System Lists
                If($List.Hidden -eq $False -and $ExcludedLists -notcontains $List.Title)
                {
                    $Counter++
                    Write-Progress -PercentComplete ($Counter / ($Lists.Count) * 100) -Activity "Exporting Permissions from List '$($List.Title)' in $($Web.URL)" -Status "Processing Lists $Counter of $($Lists.Count)"

                    #Get Item Level Permissions if 'ScanItemLevel' switch present
                    If($ScanItemLevel)
                    {
                        #Get List Items Permissions
                        Get-PnPListItemsPermission -List $List
                    }

                    #Get Lists with Unique Permissions or Inherited Permissions based on 'IncludeInheritedPermissions' switch
                    If($IncludeInheritedPermissions)
                    {
                        Get-PnPPermissions -Object $List
                    }
                    Else
                    {
                        #Check if List has unique permissions
                        $HasUniquePermissions = Get-PnPProperty -ClientObject $List -Property HasUniqueRoleAssignments
                        If($HasUniquePermissions -eq $True)
                        {
                            #Call the function to check permissions
                            Get-PnPPermissions -Object $List
                        }
                    }
                }
            }
        }
  
        #Function to Get Webs's Permissions from given URL
        Function Get-PnPWebPermission([Microsoft.SharePoint.Client.Web]$Web) 
        {
            #Call the function to Get permissions of the web
            Write-host -f Yellow "Getting Permissions of the Web: $($Web.URL)..."  
            Get-PnPPermissions -Object $Web
  
            #Get List Permissions
            Write-host -f Yellow "`t Getting Permissions of Lists and Libraries..."
            Get-PnPListPermission($Web)

            #Recursively get permissions from all sub-webs based on the "Recursive" Switch
            If($Recursive)
            {
                #Get Subwebs of the Web
                $Subwebs = Get-PnPProperty -ClientObject $Web -Property Webs

                #Iterate through each subsite in the current web
                Foreach ($Subweb in $web.Webs)
                {
                    #Get Webs with Unique Permissions or Inherited Permissions based on 'IncludeInheritedPermissions' switch
                    If($IncludeInheritedPermissions)
                    {
                        Get-PnPWebPermission($Subweb)
                    }
                    Else
                    {
                        #Check if the Web has unique permissions
                        $HasUniquePermissions = Get-PnPProperty -ClientObject $SubWeb -Property HasUniqueRoleAssignments
  
                        #Get the Web's Permissions
                        If($HasUniquePermissions -eq $true) 
                        { 
                            #Call the function recursively                            
                            Get-PnPWebPermission($Subweb)
                        }
                    }
                }
            }
        }

        #Call the function with RootWeb to get site collection permissions
        Get-PnPWebPermission $Web
  
        Write-host -f Green "`n*** Site Permission Report Generated Successfully!***"
     }
    Catch {
        write-host -f Red "Error Generating Site Permission Report!" $_.Exception.Message
   }
}
  
#region ***Parameters***
$SiteURL="https://crescent.sharepoint.com/sites/marketing"
$ReportFile="C:\Temp\SitePermissionRpt.csv"
#endregion

#Call the function to generate permission report
Generate-PnPSitePermissionRpt -SiteURL $SiteURL -ReportFile $ReportFile -Recursive
#Generate-PnPSitePermissionRpt -SiteURL $SiteURL -ReportFile $ReportFile -Recursive -ScanItemLevel -IncludeInheritedPermissions
    

This PowerShell script gets the SharePoint Online site and subsites permissions. Here is the permission report generated by this script:

sharepoint online permission report using PnP PowerShell

PowerShell Script to Generate Permission Report for SharePoint Online Site

While the above script serves the purpose, What if you want to include only up to folders but not list items or files in the permission report? In other words, the above script takes a lot of time when dealing with larger lists and libraries when you use -scanitemlevel switch.

#Function to Get Permissions Applied on a particular Object, such as: Web, List or Folder
Function Get-PnPPermissions([Microsoft.SharePoint.Client.SecurableObject]$Object)
{
    #Determine the type of the object
    Switch($Object.TypedObject.ToString())
    {
        "Microsoft.SharePoint.Client.Web"  { $ObjectType = "Site" ; $ObjectURL = $Object.URL; $ObjectTitle = $Object.Title }
        "Microsoft.SharePoint.Client.ListItem"
        {
            $ObjectType = "Folder"
            #Get the URL of the Folder 
            $Folder = Get-PnPProperty -ClientObject $Object -Property Folder
            $ObjectTitle = $Object.Folder.Name
            $ObjectURL = $("{0}{1}" -f $Web.Url.Replace($Web.ServerRelativeUrl,''),$Object.Folder.ServerRelativeUrl)
        }
        Default 
        { 
            $ObjectType = $Object.BaseType #List, DocumentLibrary, etc
            $ObjectTitle = $Object.Title
            #Get the URL of the List or Library
            $RootFolder = Get-PnPProperty -ClientObject $Object -Property RootFolder     
            $ObjectURL = $("{0}{1}" -f $Web.Url.Replace($Web.ServerRelativeUrl,''), $RootFolder.ServerRelativeUrl)
        }
    }
   
    #Get permissions assigned to the object
    Get-PnPProperty -ClientObject $Object -Property HasUniqueRoleAssignments, RoleAssignments
 
    #Check if Object has unique permissions
    $HasUniquePermissions = $Object.HasUniqueRoleAssignments
     
    #Loop through each permission assigned and extract details
    $PermissionCollection = @()
    Foreach($RoleAssignment in $Object.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
    
        #Get the Permission Levels assigned
        $PermissionLevels = $RoleAssignment.RoleDefinitionBindings | Select -ExpandProperty Name
 
        #Remove Limited Access
        $PermissionLevels = ($PermissionLevels | Where { $_ -ne "Limited Access"}) -join "; "
 
        #Leave Principals with no Permissions assigned
        If($PermissionLevels.Length -eq 0) {Continue}
 
        #Check if the Principal is SharePoint group
        If($PermissionType -eq "SharePointGroup")
        {
            #Get Group Members
            $GroupMembers = Get-PnPGroupMember -Identity $RoleAssignment.Member.LoginName
                 
            #Leave Empty Groups
            If($GroupMembers.count -eq 0){Continue}
            $GroupUsers = ($GroupMembers | Select -ExpandProperty Title | Where { $_ -ne "System Account"}) -join "; "
            If($GroupUsers.Length -eq 0) {Continue}

            #Add the Data to Object
            $Permissions = New-Object PSObject
            $Permissions | Add-Member NoteProperty Object($ObjectType)
            $Permissions | Add-Member NoteProperty Title($ObjectTitle)
            $Permissions | Add-Member NoteProperty URL($ObjectURL)
            $Permissions | Add-Member NoteProperty HasUniquePermissions($HasUniquePermissions)
            $Permissions | Add-Member NoteProperty Users($GroupUsers)
            $Permissions | Add-Member NoteProperty Type($PermissionType)
            $Permissions | Add-Member NoteProperty Permissions($PermissionLevels)
            $Permissions | Add-Member NoteProperty GrantedThrough("SharePoint Group: $($RoleAssignment.Member.LoginName)")
            $PermissionCollection += $Permissions
        }
        Else #User
        {
            #Add the Data to Object
            $Permissions = New-Object PSObject
            $Permissions | Add-Member NoteProperty Object($ObjectType)
            $Permissions | Add-Member NoteProperty Title($ObjectTitle)
            $Permissions | Add-Member NoteProperty URL($ObjectURL)
            $Permissions | Add-Member NoteProperty HasUniquePermissions($HasUniquePermissions)
            $Permissions | Add-Member NoteProperty Users($RoleAssignment.Member.Title)
            $Permissions | Add-Member NoteProperty Type($PermissionType)
            $Permissions | Add-Member NoteProperty Permissions($PermissionLevels)
            $Permissions | Add-Member NoteProperty GrantedThrough("Direct Permissions")
            $PermissionCollection += $Permissions
        }
    }
    #Export Permissions to CSV File
    $PermissionCollection | Export-CSV $ReportFile -NoTypeInformation -Append
}
   
#Function to get sharepoint online site permissions report
Function Generate-PnPSitePermissionRpt()
{
[cmdletbinding()]

    Param  
    (    
        [Parameter(Mandatory=$false)] [String] $SiteURL, 
        [Parameter(Mandatory=$false)] [String] $ReportFile,         
        [Parameter(Mandatory=$false)] [switch] $Recursive,
        [Parameter(Mandatory=$false)] [switch] $ScanFolders,
        [Parameter(Mandatory=$false)] [switch] $IncludeInheritedPermissions
    )  
    Try {
        #Connect to the Site
        Connect-PnPOnline -URL $SiteURL -Interactive
        #Get the Web
        $Web = Get-PnPWeb
 
        Write-host -f Yellow "Getting Site Collection Administrators..."
        #Get Site Collection Administrators
        $SiteAdmins = Get-PnPSiteCollectionAdmin
         
        $SiteCollectionAdmins = ($SiteAdmins | Select -ExpandProperty Title) -join "; "
        #Add the Data to Object
        $Permissions = New-Object PSObject
        $Permissions | Add-Member NoteProperty Object("Site Collection")
        $Permissions | Add-Member NoteProperty Title($Web.Title)
        $Permissions | Add-Member NoteProperty URL($Web.URL)
        $Permissions | Add-Member NoteProperty HasUniquePermissions("TRUE")
        $Permissions | Add-Member NoteProperty Users($SiteCollectionAdmins)
        $Permissions | Add-Member NoteProperty Type("Site Collection Administrators")
        $Permissions | Add-Member NoteProperty Permissions("Site Owner")
        $Permissions | Add-Member NoteProperty GrantedThrough("Direct Permissions")
               
        #Export Permissions to CSV File
        $Permissions | Export-CSV $ReportFile -NoTypeInformation
   
        #Function to Get Permissions of Folders in a given List
        Function Get-PnPFolderPermission([Microsoft.SharePoint.Client.List]$List)
        {
            Write-host -f Yellow "`t `t Getting Permissions of Folders in the List:"$List.Title
            
            #Get All Folders from List
            $ListItems = Get-PnPListItem -List $List -PageSize 2000
            $Folders = $ListItems | Where { ($_.FileSystemObjectType -eq "Folder") -and ($_.FieldValues.FileLeafRef -ne "Forms") -and (-Not($_.FieldValues.FileLeafRef.StartsWith("_")))}

            $ItemCounter = 0
            #Loop through each Folder
            ForEach($Folder in $Folders)
            {
                #Get Objects with Unique Permissions or Inherited Permissions based on 'IncludeInheritedPermissions' switch
                If($IncludeInheritedPermissions)
                {
                    Get-PnPPermissions -Object $Folder
                }
                Else
                {
                    #Check if Folder has unique permissions
                    $HasUniquePermissions = Get-PnPProperty -ClientObject $Folder -Property HasUniqueRoleAssignments
                    If($HasUniquePermissions -eq $True)
                    {
                        #Call the function to generate Permission report
                        Get-PnPPermissions -Object $Folder
                    }
                }
                $ItemCounter++
                Write-Progress -PercentComplete ($ItemCounter / ($Folders.Count) * 100) -Activity "Getting Permissions of Folders in List '$($List.Title)'" -Status "Processing Folder '$($Folder.FieldValues.FileLeafRef)' at '$($Folder.FieldValues.FileRef)' ($ItemCounter of $($Folders.Count))" -Id 2 -ParentId 1
            }
        }
 
        #Function to Get Permissions of all lists from the given web
        Function Get-PnPListPermission([Microsoft.SharePoint.Client.Web]$Web)
        {
            #Get All Lists from the web
            $Lists = Get-PnPProperty -ClientObject $Web -Property Lists
   
            #Exclude system lists
            $ExcludedLists = @("Access Requests","App Packages","appdata","appfiles","Apps in Testing","Cache Profiles","Composed Looks","Content and Structure Reports","Content type publishing error log","Converted Forms",
            "Device Channels","Form Templates","fpdatasources","Get started with Apps for Office and SharePoint","List Template Gallery", "Long Running Operation Status","Maintenance Log Library", "Images", "site collection images"
            ,"Master Docs","Master Page Gallery","MicroFeed","NintexFormXml","Quick Deploy Items","Relationships List","Reusable Content","Reporting Metadata", "Reporting Templates", "Search Config List","Site Assets","Preservation Hold Library",
            "Site Pages", "Solution Gallery","Style Library","Suggested Content Browser Locations","Theme Gallery", "TaxonomyHiddenList","User Information List","Web Part Gallery","wfpub","wfsvc","Workflow History","Workflow Tasks", "Pages")
             
            $Counter = 0
            #Get all lists from the web   
            ForEach($List in $Lists)
            {
                #Exclude System Lists
                If($List.Hidden -eq $False -and $ExcludedLists -notcontains $List.Title)
                {
                    $Counter++
                    Write-Progress -PercentComplete ($Counter / ($Lists.Count) * 100) -Activity "Exporting Permissions from List '$($List.Title)' in $($Web.URL)" -Status "Processing Lists $Counter of $($Lists.Count)" -Id 1
 
                    #Get Item Level Permissions if 'ScanFolders' switch present
                    If($ScanFolders)
                    {
                        #Get Folder Permissions
                        Get-PnPFolderPermission -List $List
                    }
 
                    #Get Lists with Unique Permissions or Inherited Permissions based on 'IncludeInheritedPermissions' switch
                    If($IncludeInheritedPermissions)
                    {
                        Get-PnPPermissions -Object $List
                    }
                    Else
                    {
                        #Check if List has unique permissions
                        $HasUniquePermissions = Get-PnPProperty -ClientObject $List -Property HasUniqueRoleAssignments
                        If($HasUniquePermissions -eq $True)
                        {
                            #Call the function to check permissions
                            Get-PnPPermissions -Object $List
                        }
                    }
                }
            }
        }
   
        #Function to Get Webs's Permissions from given URL
        Function Get-PnPWebPermission([Microsoft.SharePoint.Client.Web]$Web) 
        {
            #Call the function to Get permissions of the web
            Write-host -f Yellow "Getting Permissions of the Web: $($Web.URL)..." 
            Get-PnPPermissions -Object $Web
   
            #Get List Permissions
            Write-host -f Yellow "`t Getting Permissions of Lists and Libraries..."
            Get-PnPListPermission($Web)
 
            #Recursively get permissions from all sub-webs based on the "Recursive" Switch
            If($Recursive)
            {
                #Get Subwebs of the Web
                $Subwebs = Get-PnPProperty -ClientObject $Web -Property Webs
 
                #Iterate through each subsite in the current web
                Foreach ($Subweb in $web.Webs)
                {
                    #Get Webs with Unique Permissions or Inherited Permissions based on 'IncludeInheritedPermissions' switch
                    If($IncludeInheritedPermissions)
                    {
                        Get-PnPWebPermission($Subweb)
                    }
                    Else
                    {
                        #Check if the Web has unique permissions
                        $HasUniquePermissions = Get-PnPProperty -ClientObject $SubWeb -Property HasUniqueRoleAssignments
   
                        #Get the Web's Permissions
                        If($HasUniquePermissions -eq $true) 
                        { 
                            #Call the function recursively                            
                            Get-PnPWebPermission($Subweb)
                        }
                    }
                }
            }
        }
 
        #Call the function with RootWeb to get site collection permissions
        Get-PnPWebPermission $Web
   
        Write-host -f Green "`n*** Site Permission Report Generated Successfully!***"
     }
    Catch {
        write-host -f Red "Error Generating Site Permission Report!" $_.Exception.Message
   }
}
   
#region ***Parameters***
$SiteURL="https://crescent.sharepoint.com/sites/legal/"
$ReportFile="C:\Temp\Legal-SitePermissionRpt.csv"
#endregion
 
#Call the function to generate permission report
Generate-PnPSitePermissionRpt -SiteURL $SiteURL -ReportFile $ReportFile -ScanFolders

#Generate-PnPSitePermissionRpt -SiteURL $SiteURL -ReportFile $ReportFile -Recursive -ScanFolders -IncludeInheritedPermissions

Use the “-Recursive” switch to get the SharePoint Online site and subsite permissions using PowerShell. This report can be helpful in auditing site permissions and ensuring that only authorized users have access to sensitive information.

How to Generate Report for All Site Collections in the Tenant?

We can just call the function for all sites in the tenant and generate permission reports for sites:

#Connect to Admin Center
$TenantAdminURL = "https://crescent-admin.SharePoint.com"
$Cred = Get-Credential
Connect-PnPOnline -Url $TenantAdminURL -Credentials $Cred
  
#Get All Site collections - Exclude: Seach Center, Redirect site, Mysite Host, App Catalog, Content Type Hub, eDiscovery and Bot Sites
$SitesCollections = Get-PnPTenantSite | Where -Property Template -NotIn ("SRCHCEN#0","REDIRECTSITE#0", "SPSMSITEHOST#0", "APPCATALOG#0", "POINTPUBLISHINGHUB#0", "EDISC#0", "STS#-1")
  
#Loop through each site collection
ForEach($Site in $SitesCollections)
{
    #Connect to site collection
    $SiteConn = Connect-PnPOnline -Url $Site.Url -Credentials $Cred
    Write-host "Generating Report for Site:"$Site.Url

    #Call the Function for site collection
    $ReportFile = "C:\Temp\$($Site.URL.Replace('https://','').Replace('/','_')).CSV"
    Generate-PnPSitePermissionRpt -SiteURL $Site.URL -ReportFile $ReportFile -Recursive
}

Replace the #Region *** Parameters *** with the above script to generate a report for all sites in your tenant.

Conclusion

In conclusion, creating a permissions report in SharePoint Online using PowerShell can be a powerful and efficient way to manage and monitor access to your content. By using the cmdlets provided by the PnP PowerShell module, you can automate the process of generating a report, and quickly identify any issues or inconsistencies in the permissions of your sites and content. Additionally, PowerShell scripts can be scheduled to run on a recurring basis, so you can have a regularly updated report. With this report, you can ensure that your SharePoint Online environment is secure and that users have the appropriate access to the content they need.

Salaudeen Rajack

Salaudeen Rajack - Information Technology Expert with Two-decades of hands-on experience, specializing in SharePoint, PowerShell, Microsoft 365, and related products. He has held various positions including SharePoint Architect, Administrator, Developer and consultant, has helped many organizations to implement and optimize SharePoint solutions. Known for his deep technical expertise, He's passionate about sharing the knowledge and insights to help others, through the real-world articles!

154 thoughts on “SharePoint Online: User Permissions Audit Report for a Site Collection using PnP PowerShell

  • WOW!
    This is so perfect! hundreds of thank you! Im speechless, copy pasted, change the url and I got results. (I’m not using my work email because)
    Thank you for sharing. This saves me hours !

    Reply
  • Error Generating Site Permission Report! The term ‘Get-PnPGroupMember’ 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.

    I tried to
    Uninstall-Module -Name SharePointPnPPowerShellOnline -AllVersions -Force
    Install-Module -Name PnP.PowerShell

    but no success

    Reply
    • its typo. make it get-pnpgroupmembers

      Reply
  • Is it possible to scan only the top level folders (root) without subfolders?

    Reply
  • Hi

    Thx for the script, i don’t see the sharepoint groups in the csv file only security groups and users?
    Can you also add guests that have access to it?

    Reply
  • Hi Salaudeen,

    This looks great! I’m having a problem, though.

    When I run your script, I get the following: “Error Generating Site Permission Report! Attempted to perform an unauthorized operation.” I saw the link you provided in answer to “chewy”, https://www.sharepointdiary.com/2019/06/sharepoint-online-remote-server-returned-an-error-401-unauthorized.html, but this doesn’t seem to be the same error condition. Any tips for this issue?

    Thanks in advance.

    Reply
    • Hi Paul,

      I assigned my account directly as a Site Admin to the site from the SP Admin Center. That resolved the error message. Maybe it helps for you too.

      Reply
  • Hi, when I run this script it’s getting stuck on processing lists 1 of 20. what can I try to get this working again?

    Thank you so much!

    Reply
  • Great script Salaudeen only issue is not all folders and their permissions are pulled to the csv from the site specified in the script. Why would this be? I have tested this on multiple sites as well with the same results.

    Reply
  • Excellent script. Thank you very much for sharing!
    The only problem is that it cannot handle large libraries or lists well, due to the many requests and the throttling imposed by Microsoft (in my case it took about 4 hours for a site with 70’000 documents).
    I, therefore, suggest getting the items with unique permissions through the REST API, which allows the script to essentially perform only one request per 5000 items. Example code:
    Function Get-PnPListItemsPermission([Microsoft.SharePoint.Client.List]$List)
    {
    Write-host -f Green “`t `t Getting Permissions of List Items in the List:”$List.Title
    $siteUrl = $Web.Url
    $RestMethodURL = $SiteUrl +
    @”
    /_api/web/lists(guid’$($List.Id)’)/items?`$select=HasUniqueRoleAssignments,id&`$top=5000
    “@
    $items = Invoke-PnPSPRestMethod -Url $RestMethodURL
    $itemsWithUniquePermissions = $items.value | Where {$_.HasUniqueRoleAssignments -eq $true}
    ForEach($itemWithUniquePermissions in $itemsWithUniquePermissions)
    {
    $ListItem = Get-PnPListItem -List $List -Id $itemWithUniquePermissions.ID
    Get-PnPPermissions -Object $ListItem
    }
    }
    The above example is missing paging to handle more than 5000 elements.

    Reply
  • I just can’t get it to work no matter what I try

    PS C:\Temp> Install-Module PnP.PowerShell
    PS C:\Temp> Connect-PnPOnline -Url “https://xxxxxxxxxx.sharepoint.com” -TenantAdminUrl “https://xxxxxxxxx-admin.sharepoint.com” -UseWebLogin
    PS C:\Temp> .\spo-permissions3.ps1
    Getting Site Collection Administrators…
    Getting Permissions of the Web: https://xxxxxxxxxx.sharepoint.com…
    Error Generating Site Permission Report! Attempted to perform an unauthorized operation.
    PS C:\Temp>

    PS C:\Temp> Get-Module PnP.PowerShell |fl

    Name : PnP.PowerShell
    Path : C:\Users\Administrator\OneDrive\Profile\Documents\PowerShell\Modules\PnP.PowerShell\2.2.0\PnP.PowerShell.psd1
    Description : Microsoft 365 Patterns and Practices PowerShell Cmdlets
    ModuleType : Manifest
    Version : 2.2.0
    PreRelease :
    NestedModules : {PnP.PowerShell}
    ExportedFunctions :
    ExportedCmdlets : {Add-PnPAlert, Add-PnPApp, Add-PnPApplicationCustomizer, Add-PnPAvailableSiteClassification…}
    ExportedVariables : WebRequestCounter
    ExportedAliases : {Add-PnPAdaptiveScopeProperty, Add-PnPPropertyBagValue, Add-PnPSiteClassification, Copy-PnPFolder…}

    Reply
  • Hi, struggling with issue few days.
    In the function below the last line make an axception: “Error Generating Site Permission Report! The collection has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested.”
    what can cause this?

    Function Get-Permissions([Microsoft.SharePoint.Client.SecurableObject]$Object)
    {
    #Determine the type of the object
    Switch($Object.TypedObject.ToString())
    {
    “Microsoft.SharePoint.Client.Web”
    { $ObjectType = “Site”
    $ObjectURL = $Object.URL
    $ObjectTitle = $Object.Title }
    “Microsoft.SharePoint.Client.ListItem”
    {
    $ObjectType = “Folder”
    #Get the URL of the Folder
    #$Folder = Get-PnPProperty -ClientObject $Object -Property Folder
    $ObjectTitle = $Object.Folder.Name
    $ObjectURL = $(“{0}{1}” -f $Web.Url.Replace($Web.ServerRelativeUrl,”),$Object.Folder.ServerRelativeUrl)
    }
    Default
    {
    $ObjectType = $Object.BaseType #List, DocumentLibrary, etc
    $ObjectTitle = $Object.Title
    #Get the URL of the List or Library
    $RootFolder = Get-PnPProperty -ClientObject $Object -Property RootFolder
    $ObjectURL = $(“{0}{1}” -f $Web.Url.Replace($Web.ServerRelativeUrl,”), $RootFolder.ServerRelativeUrl)
    }
    }
    #Get permissions assigned to the object
    Get-PnPProperty -ClientObject $Object -Connection $SiteConn -Property HasUniqueRoleAssignments, RoleAssignments

    Reply
  • This code works, but I don’t get a report on all the files of the site, but only the top directory. I need to scan all directories and subdirectories. How to do it?

    Reply
    • Yes! That’s what it does, unless you specify -ScanFolders or -ScanItemLevel switch.

      Reply
      • Great scripts, thank you. Where do I adjust for -ScanFolders?

        Reply
        • That’s a switch! If you use that switch while calling the function, Folder level scan will be executed.

          Reply
          • I played with it and figured it out. Thank you. Appreciate your scripts.

            Reply
            • Hi Steve, can you share which part of the script you amended for it to display the files and folder from subdirectories?

              Reply
  • Is this possible with using PNP? (Non PNP script)

    Reply
  • SOOO after HOURS of battling with the first scrip “SharePoint Online: User Permissions report in a Site Collection using PnP PowerShell”

    Turns out the LAST line of the command was inadvertently commented out once you remove the “#” the command will run as it should.

    All of the others are like that also.

    CHeers!

    Reply
  • Great post, thanks for all the information you have shared here.
    I wonder if it’s possible to process only a single library in the second script as there’s no such passable parameter on the command line and the $siteurl parameter must be a site.

    Reply
  • I’m by no means a shark when it comes to PowerShell, but could someone provide some pointers on how I could extend the above to look for a specific user?

    That is, i run the command with a “-user someone@company.com” or similar and then the generated report shows if the user has access. Either directly or through a group that he/she is a member of.

    Reply
  • Can this be used on a On-Premises environment?

    Reply
  • Error Generating User Permission Report! Exception calling “ExecuteQuery” with “0” argument(s): “Root element is missing.”

    This is error I’m getting when I run this report

    Reply
  • This s exactly what I am looking for but I seem to be getting this error:

    Getting Site Collection Administrators…
    Error Generating Site Permission Report! Cannot process argument transformation on parameter ‘Web’. Cannot convert the “Microsoft.SharePoint.Client.Web” value of type “Microsoft.SharePoint.Client.Web” to type “Microsoft.SharePoint
    .Client.Web”.

    Reply
    • Same issue for me, can’t get it to work…

      Reply
    • Correction, I ran it in the PowerShell ISE and it works.

      Reply
  • Awesome script..

    Only question i have is what variable do i need to change in order to have it output the users email address rather than the display name.

    I need to identify all users with access that belong to specific domains

    Reply
  • This doesn’t work on an mfa-enabled admin account, the user/password popup is the old basic auth style and there’s no mfa prompt whatsoever.
    “Error Generating Site Permission Report! AADSTS50076: Due to a configuration change made by your administrator, or because you moved to a new location, you must use multi-factor authentication to access ‘00000003-0000-0ff1-ce00-000000000000’.”
    Is there a newer script that works with Microsoft Sharepoint Online in 2023?
    Thanks.

    Reply
    • You can still use an application password on your admin account.

      Reply
    • Works for mine. You might need to check 365 Admin Center to see if PowerShell has been enabled for MFA. Our tenant is also fully MFA with no app passwords allowed, and I get in fine no issues. Make sure you’re leveraging the latest pnp.powershell 1.12 module!

      Reply
    • Use Connect-PnPOnline -URL $TenantAdminURL -Interactive

      Reply
  • Error Generating Site Permission Report! Cannot process argument transformation on parameter ‘Web’. Cannot convert the “Microsoft.SharePoint.Client.Web” value of type “Microsoft.SharePoint.Client.Web” to type “Microsoft.SharePoint.Client.Web”.

    Plz help

    Reply
    • Maybe set the ExecutionPolicy PowerShell :
      Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope CurrentUser
      For testing

      Reply
    • Were you able to find a solution?

      Reply
    • Make sure you run the powershell window as an admin i.e. right click run as administrator , otherwise you may hit that issue cannot process argument transformation error

      Reply
  • How long does the script takes to generate the full report. I get following Output and it seems that not every data is being collected:

    Exporting Permissions from List ‘Events’ in https://4wpp0w… [Processing Lists 2 of 23 ]

    I’ve been running this script about 20 minutes and nothing happened.
    What might be the problem?

    Kind Regards
    Simon

    Reply
  • How long does the script takes to generate the full report. I get following Output and it seems that not every data is being collected:

    Exporting Permissions from List ‘Events’ in https://4wpp0w… [Processing Lists 2 of 23 ]

    I’ve been running this script about 20 minutes and nothing happened.
    What might be the problem?

    Kind Regards
    Simon

    Reply
  • Hi, I experienced this error message:
    “The collection has not been initialized. It has not been requested or the request has
    not been executed. It may need to be explicitly requested.”

    Any idea please ?

    Reply
  • Great script, is it possible that instead of displaying the name of the Office 365 group, it would expand and display individual user names within the group?

    Reply
      • Thanks for the script, I’m very new to PowerShell, I have the same issue as Thomas. I had a look at the link but have no idea how to incorporate that into the original script, I guess it has to be added between lines 51 to 88 (from the second script)
        Please could you add it to the script I think it would be very helpful?

        Reply
  • Hi there – hope you can help.

    Like a number of people above, I’m also getting the error: Cannot process argument transformation on parameter ‘Web’. Cannot convert the “Microsoft.SharePoint.Client.Web” value…

    I’ve tried updating the module with
    Update-Module SharePointPnPPowerShellOnline

    Have also tried replacing Microsoft.SharePoint.Client.Web with $Web on the line:
    Function Get-PnPListPermission([Microsoft.SharePoint.Client.Web]$Web)
    to:
    Function Get-PnPListPermission($Web)

    Error remains.

    Salaudeen, are you able to offer a complete answer to resolve these issues? Thanks so much. Appreciate your work.

    Reply
      • Thank you so much for that. The one thing I didn’t think to do.

        Reply
  • Hi Salaudeen,
    Is it possible to run this report for a specific user UPN?
    Thanks

    Reply
    • We can change the script to filter the specific user access. Alternatively, Use the Excel “Filter” option from the generated report.

      Reply
      • How do we change the script to filter?

        Reply
  • Hey! im having some issues with the script, I keep getting this error: “Error Generating Site Permission Report! Cannot process argument transformation on parameter ‘Object’. Cannot convert the “Microsoft.SharePoint.Client.Web” value of type “Microsoft.SharePoint.Client.Web” to type “Microsoft.SharePoint.Client.SecurableObject”.

    I’ve already tried doing as mentionned above by removing [Microsoft.SharePoint.Client.Web]”. E.g. Instead of [Microsoft.SharePoint.Client.Web]$Web , Use just: $Web and also doing the update https://www.sharepointdiary.com/2019/02/import-module-could-not-load-type-microsoft-sharepoint-administration-designpackagetype-from-assembly.html. Both without any success.

    What type of arguments or object types does the command accept when running : Generate-PnPSitePermissionRpt -SiteURL $SiteURL -ReportFile $ReportFile -Recursive -IncludeInheritedPermissions

    im asking cause $SiteURL and $ReportFile are both strings in my case.

    Reply
  • Asking here as well:
    I’ll ask here because Microsoft support was unable to help me. I have a mixed bag here. My SharePoint Online has a combination of Site Owners in for of individuals (shown by email address), Site Groups and o365 Groups. So If I run one of your nice reports, I’ll get the details of the Site Owners and I think the Site Group owner too, but the row with groups and does not mention members, are o365 groups that are the Site Owner. Do you have a combo script that would allow me to get the Site name, Permissions, Site Owner, Site Group Ower and o365 Group Members (because they would be the site owner group)?

    Reply
  • Thanks your script helped me a lot. Thanks for sharing.

    I would like to ask a question, when the permissions report is generated it appears the name of the group (Libraries created by Microsoft 365) as Name Library Owners and Name Library Members. Is it possible in the script to inform that they are the members and owners of this group instead of showing the name of the group?

    Reply
  • Hi Salaudeen. Love this site and thanks for all your scripts. However with the script to check folder permissions (NOT files) I keep getting this error in PowerShell 5 and 7 on Windows 11:

    PS C:\Users\Moe\Desktop\PowerShell_Scripts> .\Site_Permissions_Report_FOLDERS.ps1
    Generate-PnPSitePermissionRpt: C:\Users\Moe\Desktop\PowerShell_Scripts\Site_Permissions_Report_FOLDERS.ps1:289
    Line |
    289 | … ePermissionRpt -SiteURL $SiteURL -ReportFile $ReportFile -ScanFolders
    | ~~~~~~~~~~~~
    | A parameter cannot be found that matches parameter name ‘ScanFolders’.

    PS C:\Users\Moe\Desktop\PowerShell_Scripts> Get-Module PnP.PowerShell -ListAvailable | Select-Object Name,Version

    Name Version
    —- ——-
    PnP.PowerShell 1.11.0

    Reply
    • Use the script under “PowerShell Script to Generate Permission Report for SharePoint Online Site”.

      Reply
      • Thank you so much Salaudeen, that worked this time. Much appreciated as always 🙂

        Reply
  • I needed to use “-UseWebLogin” instead of “-interactive”.

    Reply
  • Hi, works fine for me. Have a answer. How can i view like the capture of the excel report ?

    Reply
  • I keep getting a permission denied message when I try to use -interactive. What can I do to fix that?

    Thank you.

    Reply
  • Thank you for this wonderful script.

    I have one question, that I hope you can help with. In order for me to see the user’s part of a group, I need to run Get-UnifiedGroupLinks and add it to the script correct? Since I have a few users added directly and a few part of Office 365 groups.

    Reply
  • Hello, I get an error stating “Generate-PnPSitePermissionRpt” is not recognized as a cmdlet

    Reply
    • Your script must include the function Generate-PnPSitePermissionRpt on its top portion! So, copy-paste the first script in this article to yours.

      Reply
  • Hi Salaudeen,

    Thanks, for some odd reason the webpage did not display line 2
    “$TenantAdminURL = “https://crescent-admin.SharePoint.com””

    When I enter the domain address and enter my username/password I get the following error:

    Generate-PnPSitePermissionRpt : The term ‘Generate-PnPSitePermissionRpt’ 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:18 char:5
    + Generate-PnPSitePermissionRpt -SiteURL $Site.URL -ReportFile $Rep .

    I have the pnp version 1.9.0 installed

    PnP.PowerShell PSGallery Microsoft 365 Patterns and Practices PowerShell …

    Reply
    • Looks, your script did not include the Generate-PnPSitePermissionRpt function defined in the article!

      Reply
  • When I run the script for the all site report I get the following error below.

    It prompts me to enter my details and afterwards it errors out. Am I doing something incorrectly?

    Connect-PnPOnline : Cannot bind argument to parameter ‘Url’ because it is null.
    At line:3 char:24
    + Connect-PnPOnline -Url $TenantAdminURL -Credentials $Cred
    + ~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidData: (:) [Connect-PnPOnline], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,PnP.PowerShell.Commands.Base.ConnectOnlin
    e

    Get-PnPTenantSite : There is currently no connection yet. Use Connect-PnPOnline to connect.
    At line:6 char:21
    + $SitesCollections = Get-PnPTenantSite | Where -Property Template -Not …
    + ~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Get-PnPTenantSite], InvalidOperationException
    + FullyQualifiedErrorId : System.InvalidOperationException,PnP.PowerShell.Commands.GetTenantSite

    Reply
    • Make sure you replace the $TenantAdminURL variable with your SharePoint Admin Center URL! E.g., $TenantAdminURL = “https://YOURDOMAIN-admin.SharePoint.com”

      Reply
  • Thanks for the hard work on this, I’m getting the following timeout error, it’s delivering some output to the CSV file, but not making it through in a timely fashion I guess “Error Generating Site Permission Report! The cloud operation was not completed before the time-out period expired.”

    Any thoughts on extending the timeout period?

    Reply
  • in line 269 I added the -IncludeInheritedPermissions parameter and got the full report

    Reply
  • Initially this feels like a conflict between the various versions of the CSOM sdk. The PnP Cmdlets come with the CSOM SDK included, so you don’t have to install that SDK. The downloadable SDK is most likely also older than the one that comes with the PnP Cmdlets (the PnP cmdlets are always using the latest SDK, usually before the downloadable package is updated by MS). My initial try would be to uninstall the SDK (which installs the SDK into the GAC, which is generally a ‘bad’ thing).
    (citation: https://techcommunity.microsoft.com/t5/sharepoint-developer/error-when-using-pnp-powershell-and-csom/m-p/33781)

    Reply
  • Thank you for your response. I was able to run the scripts you posted. However, I noticed that the first script does not report permissions if a subsite inherits permissions even though the objects it contains (subsequent subsites, libraries, lists, etc.) have unique permissions. The report ignores the subsite and the objects it contains. Do you know how to fix it?

    Reply
  • I am receiving the following error:

    Error Generating Site Permission Report! A parameter cannot be found that matches parameter name ‘Interactive’.

    Reply
    • You are running legacy version of PnP PowerShell! either upgrade or use -useweblogin

      Reply
  • Thanks for the really useful script, much appreciated.

    Reply
  • Thanks for the script it’s been really useful and has worked really well. I just wanted to see if it it’s possible to have it so the script goes over all of the subsites in the site collection.

    Cheers,

    Jake

    Reply
    • Hi Jake, Sure – Just use the “-Recursive” switch! E.g.
      Generate-PnPSitePermissionRpt -SiteURL $SiteURL -ReportFile $ReportFile -Recursive

      Reply
  • Hi, I’m looking for a sharepoint 365 permission reporting tool. I’m a site collection administrator but unfortunately I don’t have tenant permission. Could you help me?

    Reply
    • Having a site collection admin rights for a particular site collection is enough for this script!

      Reply
  • Hats off to Salaudeen for this work!

    I had some issues expanding O365 group members and security group members, and a very, very long runtime when using itemlevel, so I made a few (major) modifications:https://www.lieben.nu/liebensraum/2021/09/sharepoint-permission-auditing/

    Reply
  • Yep, instead of -credential use -Interactive

    Reply
  • Hi Saludeen,
    any way to do the loop with MFA admin account?

    Reply
    • Did you find a solution to loop with MFA?
      else you need to click a lot of times 🙁

      Reply
      • Remove the “Disconnect-PnPOnline” from the script! That will cache the last connection’s credentials and suppress login prompts.

        Reply
  • How can i declare Multiple sites in there? i would like to generate Sites and Sub sites at one instance and not to run the script with separate URL?

    Reply
  • Hi, this script doesn’t work for me. Is there an update to it? I receive 401 unauthorized errors. thanks!

    Reply
  • I am pulling “Error Generating Site Permission Report! Could not find a part of the path ‘C:TempLegal-SitePermissionRpt.csv’.”

    Reply
    • Apparently, the output CSV file path is wrong! Create a Folder in C:\, E.g “Temp” and set the path variable $ReportFile in the script.

      Reply
  • This was answered previously. I follow these steps and it resolved the error:
    https://www.sharepointdiary.com/2019/02/import-module-could-not-load-type-microsoft-sharepoint-administration-designpackagetype-from-assembly.html

    Reply
  • Does it report on files/folders shared? Thanks!

    Reply
  • Is it possible to get report for “everyone” and and “everyone except external users” only? Can we add a switch or something?

    Reply
  • I am getting this error.
    Error Generating Site Permission Report! Cannot process argument transformation on parameter ‘Web’. Cannot convert the “Microsoft.SharePoint.Client.Web” value of type “Microsoft.SharePoint.Client.Web” to type “Microsoft.SharePoint.Client.Web”
    still getting the same error after update the PnP Module: Update-Module SharePointPnPPowerShellOnline
    Please advise
    Thanks,

    Reply
  • I am getting the same error
    Error Generating Site Permission Report! Cannot process argument transformation on parameter ‘Web’. Cannot convert the “Microsoft.SharePoint.Client.Web” value of type “Microsoft.Sh
    arePoint.Client.Web” to type “Microsoft.SharePoint.Client.Web”.
    still get the same error after update PnP Module: Update-Module SharePointPnPPowerShellOnline
    Please advise

    Thanks,

    Reply
  • Doesn’t work in VSCode I guess?

    Error Generating Site Permission Report! Cannot process argument transformation on parameter ‘Web’. Cannot convert the “Microsoft.SharePoint.Client.Web” value of type “Microsoft.SharePoint.Client.Web” to type “Microsoft.SharePoint.Client.Web”.

    Reply
  • Hello there,

    Thanks for this site, really helpful.
    I don’t know much about PS scripting or PS at all really.

    I need to know how to include a listing of all the actual files in the report but also importantly need to include unique sharing permissions, (the permissions that come from sharing links).

    Can anyone help me with this?

    Thanks
    Kyle

    Reply
  • Hello,

    I get issue after issue with the pnp commandlets… not found etc.. Anyone know why?

    Error Generating Site Permission Report! The term ‘Connect-PnPOnline’ 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.

    Error Generating Site Permission Report! The term ‘Get-PnPSiteCollectionAdmin’ is not recognized as the name of a cmdlet, function, script file, or oper
    able program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

    Reply
  • Is there a way to perform this script on a subsite, for instance: xxxxxx.sharepoint.com/Shared%20Documents/ ? Or even to specify further from there?

    Reply
  • Thank you greatly for this script! Is there a way to make use of the “All sites in tenant” part of the script, but add it all to one CSV instead of one-per site collection?

    Reply
    • You can use the last script under section “How to Generate Report for All Site Collections in the Tenant?”, Replace the $ReportFile variable with a path. E.g. “C:\ReportsPermissionRpt.csv”

      Reply
    • That seems to just overwrite the report with the most recent collections info. It doesn’t append.

      Reply
    • That did it. Thanks!

      Reply
  • Great script, thank you. I’ve been getting the following error when I run the first script with -Recursive -ScanItemLevel -IncludeInheritedPermissions enabled.

    The collection has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested.

    It doesn’t happen every time. I found restarting the PowerShell ISE or breaking it up to process a subset of results helped. There are approximately 9,000 rows in the full report for the site collection. It would appear to be an “out of memory” problem, but it’s failed with 4.7 GB of memory available. Any ideas? It I’m using version 3.25.2009.1 of the SharePointPnPPowerShellOnline module.

    Reply
    • Follow up: I had more success by putting a delay into the end of the loop in Get-PnPListItemsPermission. This may indicate that the problem is with throttling. However, it does mean the code is slower.

      # Pause for 10 seconds after every 20 items are processed
      if($ItemCounter % 20 -eq 0)
      {
      Start-Sleep -Seconds 10
      }

      Reply
  • Is there anyway to have this iterate through all site collections at once? Maybe by using Get-PnPTenantSite? I’m newer to using/modifying these large scripts.

    Reply
    • When using the last script that iterates through the entire tenant, I am getting the error that “Generate-PnPSitePermissionRpt : The term ‘Generate-PnPSitePermissionRpt’ is not recognized as the name of a cmdlet,”. I have the latest module 3.25.2009.1 (that works at least, supposedly) of SharePointPnPPowerShellOnline. I have done some research looking for this cmdlet or function. Any help is welcomed

      Reply
    • The “Generate-PnPSitePermissionRpt” function is defined just above the script! You got to copy that as well (Till Line#260)!

      Reply
    • Thank you so much for your help. I probably need to copy the top script given the -ScanItemLevel parameter to get granular permissions for the tenant, correct? Line #260 doesn’t make since to copy through in the top script, though. I will work my way through this but if you get a chance, I would not mind a second hint. 🙂

      Reply
  • Great work Salaudeen but I keep getting

    Get-PnPWeb : The remote server returned an error: (403) Forbidden.

    Despite using an account with Sharepoint Admin / Site Owner roles ?

    If I run

    Connect-PnPOnline -URL $SiteURL -Interactive

    I get no errors.

    But this line

    $Web = Get-PnPWeb

    Produces he 403 forbidden

    Reply
  • Nice script. thanks for sharing!

    Reply
  • Hello

    Is there any way to check who provided the permission using powershell? For example I have one admin group with full permission. This admin group having 4 users, who can add/remove/change users to different group like reader and writer group. I want to track the activities which owner provide add/remove/change the user and how many time user add/removed by particular owner.

    I want to do it using PowerShell for SP16 and SPO.

    Regards
    Avian

    Reply
  • Hi, great script. I was able to use the second script and not the first script. The first script would only evaluate sites, no subsites, no lists, or items. It returns 3 rows (1 Site Collection, and two sites of the site collection). The second script seems to work so much better and I was able to make a loop to iterate through the other sites of a Sharepoint.

    Is there a way to write a function Get-PnPItemsPermissions? The item (docx, excel, etc.) could be in a folder or a list. That would help so much and avoid using the -scanitemlevel switch. Thank you so much for your hard work, I’m here to learn!

    Reply
    • Use -ScanFolders switch in the 2nd script to limit searching permissions up to the Folders scope! Otherwise, It runs by scanning Item level permissions.

      Reply
  • The script worked fine for me.

    However, it’s still not displaying the permissions for a lot of documents having unique permissions.
    The library itself has unique permissions so it’s showing for library level but not for file level.
    Please let me know what am I missing.

    Reply
    • Use the First script in this article with -ScanItemLevel switch:
      Generate-PnPSitePermissionRpt -SiteURL $SiteURL -ReportFile $ReportFile -Recursive -ScanItemLevel

      Reply
  • Getting “The remote server returned an error: (400) Bad Request.” with both Get-PnPWeb and Get-PnpSiteCollectionAdmin commands in either script. I’ve got all the prerequisites installed and working and I’ve tried it on two different computers. 2FA is enabled. Both successfully login. One is also Azure joined. No dice.

    Reply
  • How do I capture the email address of the user with permissions too?

    Reply
  • Disregard my previous comment. I followed your instructions for fixing the CSOM assemblies in the GAC and it worked perfectly. This is a superb script and a great site overall. Many thanks!!

    Reply
  • Getting the following error: Error Generating Site Permission Report! Cannot process argument transformation on parameter ‘Object’. Cannot convert the “Microsoft.SharePoint.Client.Web” value of type “Microsoft.SharePoint.Client.Web” to type “Microsoft.SharePoint.Client.SecurableObject”.

    Have tried updating the module but the error remains

    Reply
    • Try restarting the PowerShell ISE or PowerShell console. Otherwise, Remove the [Microsoft.SharePoint.Client.Web] type from the parameter!

      Reply
  • Can I use this script to generate permission for sub-site?

    Reply
  • Excellent script, thanks very much.
    I had to make one small change in the second script: $folders = @( $listItems | where… )
    I think where there is one item, $folders does not have a “count” property and it fails.
    Using @() forces it to be an array

    Reply
  • This is an excellent work. Thank you very much

    Reply
  • Hi,

    I only seem to be able to report on site permissions. I can not report on unique file and folder permissions which are further down the document library.

    Do you know why this is?

    Thanks,

    Sean

    Reply
  • Do you have a similar script that work for SharePoint 2016 on-premise?

    Reply
  • Can you help Error Generating Site Permission Report! Cannot process argument transformation on parameter ‘Object’. Cannot convert the “Microsoft.SharePoint.Client.Web” value of type “Microsoft.SharePoint
    .Client.Web” to type “Microsoft.SharePoint.Client.SecurableObject”.

    Reply
  • Hi,I am getting this error.
    Error Generating Site Permission Report! Cannot process argument transformation on parameter ‘Web’. Cannot convert the
    “Microsoft.SharePoint.Client.Web” value of type “Microsoft.SharePoint.Client.Web” to type “Microsoft.SharePoint.Clien
    t.Web”

    Reply
  • This works perfectly for me needs, thank you so much for sharing your hard work!

    Reply
  • Thank you for great work and sharing the script! I’ve used your script and adjusted to my needs. The only thing is the speed. If I need to generate a report on a Site Collection with few libraries, which have over 35k files/folders. To run it takes forever. As it have to go through each item/file and check if HasUniqueRoleAssignments=TRUE. Is it any way to speed up this? Any way to get List Items with HasUniqueRoleAssignments True filtered from beginning? Was looking arround on Internet and didn’t find any solutions or suggestions. I’m ok to mix it with CSOM if needed, but didn’t find how yet.
    Thanks again for your help!

    Reply
    • I understand! But unfortunately, we don’t have any mechanism to handle this issue as of today!

      Reply
    • and SharePoint Server Object model offers: SPList.GetItemsWithUniquePermission() method which gets you all items with unique permissions from a List instantly! However, this method is not available in CSOM yet.

      Reply
  • Thanks for your excellent sharing! But I encounter a problem when I add my site-url value at the end of this method and run this code with the error of ‘Error generating site permission report! Cannot process argument transformation on parameter ‘Web’. Cannot convert the ‘Microsoft.Sharepoint.Client.Web’ value of type ‘Microsoft.Sharepoint.Client.Web’ to type ‘Microsoft.Sharepoint.Client.Web’. What I have done: connect-SPOService, connect-PnPOnline, and load SharePoint CSOM Assemblies. Can you please to tell me how to figure out this error and export the whole permission report successfully? Thanks a lot.

    Reply
    • Try removing explicit type “[Microsoft.SharePoint.Client.Web]”. E.g. Instead of [Microsoft.SharePoint.Client.Web]$Web , Use just: $Web

      Reply
    • I’ve tried removing [Microsoft.SharePoint.Client.Web] and it failed. Error Generating Site Permission Report! Cannot process argument transformation on parameter ‘Object’. Cannot convert the “Microsoft.SharePoint.Client.Web” value of type “Microsoft.SharePoint.Client.Web” to type “Microsoft.SharePoint.Client.SecurableObject”. Please help!.

      Reply
    • Looks to me this is due version conflict from CSOM assemblies deployed in GAC! Can you try this: https://www.sharepointdiary.com/2019/02/import-module-could-not-load-type-microsoft-sharepoint-administration-designpackagetype-from-assembly.html

      Reply
    • You’re a legend!

      Reply
  • Sorry that after I could not see any items/folder level permission on csv file, do you know what’s wrong with me?

    Reply
  • it’s really good,thank you!
    is it possible to dump all permissions report just for the lists/folders? The code is now exporting every file permissions,and making the csv too big to open

    Reply
  • I was not able to run script and was getting error saying Get-PnPPermissions not recognized. i added Get-PnPGroupPermissions and it worked. I would like to have email address of users too. is it possible for you to modify in such way ?

    Reply
    • Get-PnPPermissions is a custom function name defined in the top of this script! You can get user Email address with: $RoleAssignment.Member.LoginName .

      Reply
      • Can you tell me how to get the user’s email address with: $ RoleAssignment.Member.LoginName? When I changed to $ GroupMembers = Get-PnPGroupMember -Identity $ RoleAssignment.Member.Email I get “Error Generating Site Permission Report! Cannot bind argument to parameter ‘Group’ because it is null.”

        Reply

Leave a Reply

Your email address will not be published. Required fields are marked *