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 permission report on all objects such as SharePoint Online Site collection and its subsites, lists and libraries, folders, and list items which has unique permissions on the given site collection. It scans through the following securables:
- 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.
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:
#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:
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 SharePoint Online site and subsite permissions using PowerShell.
How to Generate Report for All Site Collections in the Tenant?
We can just call the function for all sites in the tenant as:
#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, Mysite Host, App Catalog, Content Type Hub, eDiscovery and Bot Sites
$SitesCollections = Get-PnPTenantSite | Where -Property Template -NotIn ("SRCHCEN#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
Disconnect-PnPOnline -Connection $SiteConn
}
Replace the #Region *** Parameters *** with the above script to generate a report for all sites in your tenant.
I keep getting a permission denied message when I try to use -interactive. What can I do to fix that?
Thank you.
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.
Hello, I get an error stating “Generate-PnPSitePermissionRpt” is not recognized as a cmdlet
Your script must include the function Generate-PnPSitePermissionRpt on its top portion! So, copy-paste the first script in this article to yours.
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 …
Looks, your script did not include the Generate-PnPSitePermissionRpt function defined in the article!
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
Make sure you replace the $TenantAdminURL variable with your SharePoint Admin Center URL! E.g., $TenantAdminURL = “https://YOURDOMAIN-admin.SharePoint.com”
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?
in line 269 I added the -IncludeInheritedPermissions parameter and got the full report
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)
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?
I am receiving the following error:
Error Generating Site Permission Report! A parameter cannot be found that matches parameter name ‘Interactive’.
You are running legacy version of PnP PowerShell! either upgrade or use -useweblogin
Thanks for the really useful script, much appreciated.
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
Hi Jake, Sure – Just use the “-Recursive” switch! E.g.
Generate-PnPSitePermissionRpt -SiteURL $SiteURL -ReportFile $ReportFile -Recursive
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?
Having a site collection admin rights for a particular site collection is enough for this script!
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/
Yep, instead of -credential use -Interactive
Hi Saludeen,
any way to do the loop with MFA admin account?
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?
Hi, this script doesn’t work for me. Is there an update to it? I receive 401 unauthorized errors. thanks!
Refer here to fix 401 Unauthorized error: SharePoint Online: Fix “The remote server returned an error (401) Unauthorized” Error in PowerShell
I am pulling “Error Generating Site Permission Report! Could not find a part of the path ‘C:TempLegal-SitePermissionRpt.csv’.”
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.
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
Does it report on files/folders shared? Thanks!
Is it possible to get report for “everyone†and and “everyone except external users†only? Can we add a switch or something?
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,
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,
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”.
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
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.
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?
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?
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”
That seems to just overwrite the report with the most recent collections info. It doesn’t append.
Use -Append switch in Export-CSV cmdlets.
That did it. Thanks!
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.
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
}
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.
Article updated with the script to generate permissions report for all sites!
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
The “Generate-PnPSitePermissionRpt” function is defined just above the script! You got to copy that as well (Till Line#260)!
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. 🙂
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
Generally 403 Forbidden error tells us the current user doesn’t have permission to the site. SharePoint Admin/Global Admin doesn’t get access to the site automatically unless they are added to the site! The Connect-PnPOnline just authorizes your account and the actual authentication occurs when you try to access objects. So, Make sure the account has permissions to sites!
There could be other reasons as well: Fix for “The remote server returned an error: (403) Forbidden” in SharePoint Online PowerShell
Nice script. thanks for sharing!
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
Audit Logs/Office 365 Security Audit reports.
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!
Use -ScanFolders switch in the 2nd script to limit searching permissions up to the Folders scope! Otherwise, It runs by scanning Item level permissions.
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.
Use the First script in this article with -ScanItemLevel switch:
Generate-PnPSitePermissionRpt -SiteURL $SiteURL -ReportFile $ReportFile -Recursive -ScanItemLevel
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.
How do I capture the email address of the user with permissions too?
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!!
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
Try restarting the PowerShell ISE or PowerShell console. Otherwise, Remove the [Microsoft.SharePoint.Client.Web] type from the parameter!
Can I use this script to generate permission for sub-site?
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
This is an excellent work. Thank you very much
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
Do you have a similar script that work for SharePoint 2016 on-premise?
Here you go: SharePoint 2016 Permission Report using PowerShell
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”.
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”
Update your PnP Module: Update-Module SharePointPnPPowerShellOnline
Still getting same error
This works perfectly for me needs, thank you so much for sharing your hard work!
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!
I understand! But unfortunately, we don’t have any mechanism to handle this issue as of today!
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.
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.
Try removing explicit type “[Microsoft.SharePoint.Client.Web]”. E.g. Instead of [Microsoft.SharePoint.Client.Web]$Web , Use just: $Web
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!.
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
You’re a legend!
Sorry that after I could not see any items/folder level permission on csv file, do you know what’s wrong with me?
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
This should help you: SharePoint Online: Export Site, List, List Item Permissions to CSV using PowerShell
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 ?
Get-PnPPermissions is a custom function name defined in the top of this script! You can get user Email address with: $RoleAssignment.Member.LoginName .
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.”