SharePoint Online: Site Collection Permissions Report using PowerShell

Requirement: Generate permissions report for a SharePoint Online site collection.

SharePoint Online: Site Collection Permissions Report using PowerShell
Have you ever wanted to get SharePoint Online Site and subsites permissions using PowerShell? Well, This PowerShell script generates permission report on all objects which has unique permissions on the given site collection. It scans through 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.
Here is the SharePoint Online PowerShell to get site permissions report:
#sharepoint online powershell permissions report
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
  
#To call a non-generic method Load
Function Invoke-LoadMethod() {
    param(
            [Microsoft.SharePoint.Client.ClientObject]$Object = $(throw "Please provide a Client Object"),
            [string]$PropertyName
        ) 
   $ctx = $Object.Context
   $load = [Microsoft.SharePoint.Client.ClientContext].GetMethod("Load") 
   $type = $Object.GetType()
   $clientLoad = $load.MakeGenericMethod($type)
  
   $Parameter = [System.Linq.Expressions.Expression]::Parameter(($type), $type.Name)
   $Expression = [System.Linq.Expressions.Expression]::Lambda([System.Linq.Expressions.Expression]::Convert([System.Linq.Expressions.Expression]::PropertyOrField($Parameter,$PropertyName),[System.Object] ), $($Parameter))
   $ExpressionArray = [System.Array]::CreateInstance($Expression.GetType(), 1)
   $ExpressionArray.SetValue($Expression, 0)
   $clientLoad.Invoke($ctx,@($Object,$ExpressionArray))
}
 
#Function to Get Permissions Applied on a particular Object, such as: Web, List or Item
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 }
        "Microsoft.SharePoint.Client.ListItem"
        { 
            $ObjectType = "List Item"
            #Get the URL of the List Item
            Invoke-LoadMethod -Object $Object.ParentList -PropertyName "DefaultDisplayFormUrl"
            $Ctx.ExecuteQuery()
            $DefaultDisplayFormUrl = $Object.ParentList.DefaultDisplayFormUrl
            $ObjectURL = $("{0}{1}?ID={2}" -f $Ctx.Web.Url.Replace($Ctx.Web.ServerRelativeUrl,''), $DefaultDisplayFormUrl,$Object.ID)
        }
        Default 
        { 
            $ObjectType = "List/Library"
            #Get the URL of the List or Library
            $Ctx.Load($Object.RootFolder)
            $Ctx.ExecuteQuery()            
            $ObjectURL = $("{0}{1}" -f $Ctx.Web.Url.Replace($Ctx.Web.ServerRelativeUrl,''), $Object.RootFolder.ServerRelativeUrl)
        }
    }
 
    #Get permissions assigned to the object
    $Ctx.Load($Object.RoleAssignments)
    $Ctx.ExecuteQuery()
 
    Foreach($RoleAssignment in $Object.RoleAssignments)
    { 
                $Ctx.Load($RoleAssignment.Member)
                $Ctx.executeQuery()
                 
                #Get the Permissions on the given object
                $Permissions=@()
                $Ctx.Load($RoleAssignment.RoleDefinitionBindings)
                $Ctx.ExecuteQuery()
                Foreach ($RoleDefinition in $RoleAssignment.RoleDefinitionBindings)
                {
                    $Permissions += $RoleDefinition.Name +";"
                }
 
                #Check direct permissions
                if($RoleAssignment.Member.PrincipalType -eq "User")
                {
                        #Send the Data to Report file
                        "$($ObjectURL) `t $($ObjectType) `t $($Object.Title)`t $($RoleAssignment.Member.LoginName) `t User `t $($Permissions)" | Out-File $ReportFile -Append
                }
                 
                ElseIf($RoleAssignment.Member.PrincipalType -eq "SharePointGroup")
                {        
                        #Send the Data to Report file
                        "$($ObjectURL) `t $($ObjectType) `t $($Object.Title)`t $($RoleAssignment.Member.LoginName) `t SharePoint Group `t $($Permissions)" | Out-File $ReportFile -Append
                }
                ElseIf($RoleAssignment.Member.PrincipalType -eq "SecurityGroup")
                {
                    #Send the Data to Report file
                    "$($ObjectURL) `t $($ObjectType) `t $($Object.Title)`t $($RoleAssignment.Member.Title)`t $($Permissions) `t Security Group" | Out-File $ReportFile -Append
                }
    }
}
 
#powershell to get sharepoint online site permissions
Function Generate-SPOSitePermissionRpt($SiteURL,$ReportFile)
{
    Try {
        #Get Credentials to connect
        $Cred= Get-Credential
        $Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Cred.Username, $Cred.Password)
  
        #Setup the context
        $Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
        $Ctx.Credentials = $Credentials
 
        #Get the Web
        $Web = $Ctx.Web
        $Ctx.Load($Web)
        $Ctx.ExecuteQuery()
 
        #Write CSV- TAB Separated File) Header
        "URL `t Object `t Title `t Account `t PermissionType `t Permissions" | out-file $ReportFile
 
        Write-host -f Yellow "Getting Site Collection Administrators..."
        #Get Site Collection Administrators
        $SiteUsers= $Ctx.Web.SiteUsers 
        $Ctx.Load($SiteUsers)
        $Ctx.ExecuteQuery()
        $SiteAdmins = $SiteUsers | Where { $_.IsSiteAdmin -eq $true}
 
        ForEach($Admin in $SiteAdmins)
        {
            #Send the Data to report file
            "$($Web.URL) `t Site Collection `t $($Web.Title)`t $($Admin.Title) `t Site Collection Administrator `t  Site Collection Administrator" | Out-File $ReportFile -Append
        }
 
        #Function to Get Permissions of All List Items of a given List
        Function Get-SPOListItemsPermission([Microsoft.SharePoint.Client.List]$List)
        {
            Write-host -f Yellow "`t `t Getting Permissions of List Items in the List:"$List.Title

            $Query = New-Object Microsoft.SharePoint.Client.CamlQuery
            $Query.ViewXml = "<View Scope='RecursiveAll'><Query><OrderBy><FieldRef Name='ID' Ascending='TRUE'/></OrderBy></Query><RowLimit Paged='TRUE'>$BatchSize</RowLimit></View>"

            $Counter = 0
            #Batch process list items - to mitigate list threshold issue on larger lists
            Do {  
                #Get items from the list
                $ListItems = $List.GetItems($Query)
                $Ctx.Load($ListItems)
                $Ctx.ExecuteQuery()
          
                $Query.ListItemCollectionPosition = $ListItems.ListItemCollectionPosition
 
                #Loop through each List item
                ForEach($ListItem in $ListItems)
                {
                    Invoke-LoadMethod -Object $ListItem -PropertyName "HasUniqueRoleAssignments"
                    $Ctx.ExecuteQuery()
                    If($ListItem.HasUniqueRoleAssignments -eq $True)
                    {
                        #Call the function to generate Permission report
                        Get-Permissions -Object $ListItem
                    }
                    $Counter++
                    Write-Progress -PercentComplete ($Counter / ($List.ItemCount) * 100) -Activity "Processing Items $Counter of $($List.ItemCount)" -Status "Searching Unique Permissions in List Items of '$($List.Title)'" 
                }
            } While ($Query.ListItemCollectionPosition -ne $null)
        }
 
        #Function to Get Permissions of all lists from the web
        Function Get-SPOListPermission([Microsoft.SharePoint.Client.Web]$Web)
        {
            #Get All Lists from the web
            $Lists = $Web.Lists
            $Ctx.Load($Lists)
            $Ctx.ExecuteQuery()
 
            #Get all lists from the web   
            ForEach($List in $Lists)
            {
                #Exclude System Lists
                If($List.Hidden -eq $False)
                {
                    #Get List Items Permissions
                    Get-SPOListItemsPermission $List
 
                    #Get the Lists with Unique permission
                    Invoke-LoadMethod -Object $List -PropertyName "HasUniqueRoleAssignments"
                    $Ctx.ExecuteQuery()
 
                    If( $List.HasUniqueRoleAssignments -eq $True)
                    {
                        #Call the function to check permissions
                        Get-Permissions -Object $List
                    }
                }
            }
        }
 
        #Function to Get Webs's Permissions from given URL
        Function Get-SPOWebPermission([Microsoft.SharePoint.Client.Web]$Web) 
        {
            #Get all immediate subsites of the site
            $Ctx.Load($web.Webs)  
            $Ctx.executeQuery()
  
            #Call the function to Get Lists of the web
            Write-host -f Yellow "Getting the Permissions of Web "$Web.URL"..."
 
            #Check if the Web has unique permissions
            Invoke-LoadMethod -Object $Web -PropertyName "HasUniqueRoleAssignments"
            $Ctx.ExecuteQuery()
 
            #Get the Web's Permissions
            If($web.HasUniqueRoleAssignments -eq $true) 
            { 
                Get-Permissions -Object $Web
            }
 
            #Scan Lists with Unique Permissions
            Write-host -f Yellow "`t Getting the Permissions of Lists and Libraries in "$Web.URL"..."
            Get-SPOListPermission($Web)
  
            #Iterate through each subsite in the current web
            Foreach ($Subweb in $web.Webs)
            {
                 #Call the function recursively                            
                 Get-SPOWebPermission($SubWeb)
            }
        }
 
        #Call the function with RootWeb to get site collection permissions
        Get-SPOWebPermission $Web
 
        Write-host -f Green "Site Permission Report Generated Successfully!"
     }
    Catch {
        write-host -f Red "Error Generating Site Permission Report!" $_.Exception.Message
   }
}
 
#Set parameter values
$SiteURL="https://crescent.sharepoint.com"
$ReportFile="C:\Temp\SitePermissionRpt.csv"
$BatchSize = 500
 
#Call the function
Generate-SPOSitePermissionRpt -SiteURL $SiteURL -ReportFile $ReportFile

Output Report of the script:
The above script generates a CSV file in the provided ReportFile parameter. Here is a sample report generated.
sharepoint online site collection permission report using powershell
If you are looking for permission report for a specific user, use my another script: SharePoint Online: User Permissions Report using PowerShell

Update: SharePoint Online Site Permission Report V2
How about extending the script to expand SharePoint Groups (instead of just group name, have all members of the group) and introduce switches for Recursively process all sub-sites, Scan up to Item level permissions and export all permissions including the one with inheriting permissions from its parent? Here is the SharePoint Online PowerShell to get all user permissions:
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
   
#Function to call a non-generic method Load
Function Invoke-LoadMethod([Microsoft.SharePoint.Client.ClientObject]$Object = $(throw "Please provide a Client Object"),[string]$PropertyName) 
{
   $Ctx = $Object.Context
   $Load = [Microsoft.SharePoint.Client.ClientContext].GetMethod("Load") 
   $Type = $Object.GetType()
   $ClientLoad = $load.MakeGenericMethod($type)
   
   $Parameter = [System.Linq.Expressions.Expression]::Parameter(($type), $type.Name)
   $Expression = [System.Linq.Expressions.Expression]::Lambda([System.Linq.Expressions.Expression]::Convert([System.Linq.Expressions.Expression]::PropertyOrField($Parameter,$PropertyName),[System.Object] ), $($Parameter))
   $ExpressionArray = [System.Array]::CreateInstance($Expression.GetType(), 1)
   $ExpressionArray.SetValue($Expression, 0)
   $ClientLoad.Invoke($ctx,@($Object,$ExpressionArray))
}
  
#Function to Get Permissions Applied on a particular Object, such as: Web or List
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"
        { 
            If($Object.FileSystemObjectType -eq "Folder")
            {
                $ObjectType = "Folder"
                #Get the URL of the Folder
                Invoke-LoadMethod -Object $Object -PropertyName "Folder"
                $Ctx.ExecuteQuery()
                $ObjectTitle = $Object.Folder.Name
                $ObjectURL = $("{0}{1}" -f $Ctx.Web.Url.Replace($Ctx.Web.ServerRelativeUrl,''),$Object.Folder.ServerRelativeUrl)
            }
            Else #File or List Item
            {
                #Get the URL of the Object
                Invoke-LoadMethod -Object $Object -PropertyName "File"
                $Ctx.ExecuteQuery()
                If($Object.File.Name -ne $Null)
                {
                    $ObjectType = "File"
                    $ObjectTitle = $Object.File.Name
                    $ObjectURL = $("{0}{1}" -f $Ctx.Web.Url.Replace($Ctx.Web.ServerRelativeUrl,''),$Object.File.ServerRelativeUrl)
                }
                else
                {
                    $ObjectType = "List Item"
                    $ObjectTitle = $Object["Title"]
                    #Get the URL of the List Item
                    Invoke-LoadMethod -Object $Object.ParentList -PropertyName "DefaultDisplayFormUrl"
                    $Ctx.ExecuteQuery()
                    $DefaultDisplayFormUrl = $Object.ParentList.DefaultDisplayFormUrl
                    $ObjectURL = $("{0}{1}?ID={2}" -f $Ctx.Web.Url.Replace($Ctx.Web.ServerRelativeUrl,''), $DefaultDisplayFormUrl,$Object.ID)
                }
            }
        }
        Default 
        { 
            $ObjectType = "List or Library"
            $ObjectTitle = $Object.Title
            #Get the URL of the List or Library
            $Ctx.Load($Object.RootFolder)
            $Ctx.ExecuteQuery()            
            $ObjectURL = $("{0}{1}" -f $Ctx.Web.Url.Replace($Ctx.Web.ServerRelativeUrl,''), $Object.RootFolder.ServerRelativeUrl)
        }
    }
  
    #Check if Object has unique permissions
    Invoke-LoadMethod -Object $Object -PropertyName "HasUniqueRoleAssignments"
    $Ctx.ExecuteQuery()
    $HasUniquePermissions = $Object.HasUniqueRoleAssignments
  
    #Get permissions assigned to the object
    $RoleAssignments = $Object.RoleAssignments
    $Ctx.Load($RoleAssignments)
    $Ctx.ExecuteQuery()
   
    #Loop through each permission assigned and extract details
    $PermissionCollection = @()
    Foreach($RoleAssignment in $RoleAssignments)
    { 
        $Ctx.Load($RoleAssignment.Member)
        $Ctx.executeQuery()
   
        #Get the Principal Type: User, SP Group, AD Group
        $PermissionType = $RoleAssignment.Member.PrincipalType
   
        #Get the Permission Levels assigned
        $Ctx.Load($RoleAssignment.RoleDefinitionBindings)
        $Ctx.ExecuteQuery()
        $PermissionLevels = $RoleAssignment.RoleDefinitionBindings | Select -ExpandProperty Name

        #Remove Limited Access
        $PermissionLevels = ($PermissionLevels | Where { $_ –ne "Limited Access"}) -join ","
        If($PermissionLevels.Length -eq 0) {Continue}

        #Get SharePoint group members
        If($PermissionType -eq "SharePointGroup")
        {
            #Get Group Members
            $Group = $Ctx.web.SiteGroups.GetByName($RoleAssignment.Member.LoginName)
            $Ctx.Load($Group)
            $GroupMembers= $Group.Users
            $Ctx.Load($GroupMembers)
            $Ctx.ExecuteQuery()
            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-SPOSitePermissionRpt()
{
    [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 {
        #Get Credentials to connect
        $Cred= Get-Credential
   
        #Setup the context
        $Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
        $Ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Cred.Username, $Cred.Password)
  
        #Get the Web & Root Web
        $Web = $Ctx.Web
        $RootWeb = $Ctx.Site.RootWeb
        $Ctx.Load($Web)
        $Ctx.Load($RootWeb)
        $Ctx.ExecuteQuery()
  
        Write-host -f Yellow "Getting Site Collection Administrators..."
        #Get Site Collection Administrators
        $SiteUsers= $RootWeb.SiteUsers 
        $Ctx.Load($SiteUsers)
        $Ctx.ExecuteQuery()
        $SiteAdmins = $SiteUsers | Where { $_.IsSiteAdmin -eq $true}
        
        $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($RootWeb.Title)
        $Permissions | Add-Member NoteProperty URL($RootWeb.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-SPOListItemsPermission([Microsoft.SharePoint.Client.List]$List)
        {
            Write-host -f Yellow "`t `t Getting Permissions of List Items in the List:"$List.Title
 
            $Query = New-Object Microsoft.SharePoint.Client.CamlQuery
            $Query.ViewXml = "<View Scope='RecursiveAll'><Query><OrderBy><FieldRef Name='ID' Ascending='TRUE'/></OrderBy></Query><RowLimit Paged='TRUE'>$BatchSize</RowLimit></View>"
 
            $ItemCounter = 0
            #Batch process list items - to mitigate list threshold issue on larger lists
            Do {  
                #Get items from the list
                $ListItems = $List.GetItems($Query)
                $Ctx.Load($ListItems)
                $Ctx.ExecuteQuery()
           
                $Query.ListItemCollectionPosition = $ListItems.ListItemCollectionPosition
  
                #Loop through each List item
                ForEach($ListItem in $ListItems)
                {
                    #Get Objects with Unique Permissions or Inherited Permissions based on 'IncludeInheritedPermissions' switch
                    If($IncludeInheritedPermissions)
                    {
                        Get-Permissions -Object $ListItem
                    }
                    Else
                    {
                        Invoke-LoadMethod -Object $ListItem -PropertyName "HasUniqueRoleAssignments"
                        $Ctx.ExecuteQuery()
                        If($ListItem.HasUniqueRoleAssignments -eq $True)
                        {
                            #Call the function to generate Permission report
                            Get-Permissions -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)'"
                }
            } While ($Query.ListItemCollectionPosition -ne $null)
        }

        #Function to Get Permissions of all lists from the web
        Function Get-SPOListPermission([Microsoft.SharePoint.Client.Web]$Web)
        {
            #Get All Lists from the web
            $Lists = $Web.Lists
            $Ctx.Load($Lists)
            $Ctx.ExecuteQuery()
  
            #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 "Processing Lists $Counter of $($Lists.Count) in $($Web.URL)" -Status "Exporting Permissions from List '$($List.Title)'"

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

                    #Get Lists with Unique Permissions or Inherited Permissions based on 'IncludeInheritedPermissions' switch
                    If($IncludeInheritedPermissions)
                    {
                        Get-Permissions -Object $List
                    }
                    Else
                    {
                        #Check if List has unique permissions
                        Invoke-LoadMethod -Object $List -PropertyName "HasUniqueRoleAssignments"
                        $Ctx.ExecuteQuery()
                        If($List.HasUniqueRoleAssignments -eq $True)
                        {
                            #Call the function to check permissions
                            Get-Permissions -Object $List
                        }
                    }
                }
            }
        }
  
        #Function to Get Webs's Permissions from given URL
        Function Get-SPOWebPermission([Microsoft.SharePoint.Client.Web]$Web) 
        {
            #Get all immediate subsites of the site
            $Ctx.Load($web.Webs)  
            $Ctx.executeQuery()
   
            #Call the function to Get permissions of the web
            Write-host -f Yellow "Getting Permissions of the Web: $($Web.URL)..."  
            Get-Permissions -Object $Web
  
            #Get List Permissions
            Write-host -f Yellow "`t Getting Permissions of Lists and Libraries..."
            Get-SPOListPermission($Web)

            #Recursively get permissions from all sub-webs based on the "Recursive" Switch
            If($Recursive)
            {
                #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-SPOWebPermission($Subweb)
                    }
                    Else
                    {
                        #Check if the Web has unique permissions
                        Invoke-LoadMethod -Object $Subweb -PropertyName "HasUniqueRoleAssignments"
                        $Ctx.ExecuteQuery()
  
                        #Get the Web's Permissions
                        If($Subweb.HasUniqueRoleAssignments -eq $true) 
                        { 
                            #Call the function recursively                            
                            Get-SPOWebPermission($Subweb)
                        }
                    }
                }
            }
        }
  
        #Call the function with RootWeb to get site collection permissions
        Get-SPOWebPermission $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"
$BatchSize = 500
#endregion

#Call the function to generate permission report
Generate-SPOSitePermissionRpt -SiteURL $SiteURL -ReportFile $ReportFile
#Generate-SPOSitePermissionRpt -SiteURL $SiteURL -ReportFile $ReportFile -Recursive -ScanItemLevel -IncludeInheritedPermissions
This script produces output as below! It gets SharePoint Online site and subsites along with its child object permission using PowerShell
SharePoint Online Site Permission Report
SharePoint Online: Site Collection Permissions Report using PowerShell SharePoint Online: Site Collection Permissions Report using PowerShell Reviewed by Salaudeen Rajack on September 06, 2018 Rating: 5

47 comments:

  1. I tried your script and it worked like a charm!! Thanks a Million Salaudeen!

    ReplyDelete
  2. Getting below error: Error Generating Site Permission Report! Exception calling "ExecuteQuery" with "0" argument(s): "The specified user could not be found."

    Any thought?

    ReplyDelete
    Replies
    1. Guess it happens when the user doesn't exist (Orphan Users!).. Let me check if this can be avoided.

      Delete
  3. hi i have the same error
    Getting below error: Error Generating Site Permission Report! Exception calling "ExecuteQuery" with "0" argument(s): "The specified user could not be found."


    #Read more: http://www.sharepointdiary.com/2018/09/sharepoint-online-site-collection-permission-report-using-powershell.html#ixzz5SlTVyKNr

    ReplyDelete
  4. This comment has been removed by the author.

    ReplyDelete
  5. The script does not show a title for the list item

    ReplyDelete
  6. Hi I am getting below list view threshold error, any ideas please because we have more list in our sites.
    Error Generating Site Permission Report! Exception calling "ExecuteQuery" with "0" argument(s): "The attempted operation is prohibited because it exceeds the list view threshold enforced by the administrator."

    ReplyDelete
    Replies
    1. This is because: You have larger lists with > 5000 List items. The script has been updated to handle larger lists. Try now!

      Delete
  7. Hi Salaudeen,

    Did you modify this script?

    I'm getting error like,

    "Error Generating Site Permission Report! Exception calling "ExecuteQuery" with "0" argument(s): "The sign-in name or password does not match one in the Microsoft account system."

    I have entered the correct credentials and I'm a tenant admin for SharePoint online.

    Thanks.

    ReplyDelete
    Replies
    1. I am having the same error and I am using MFA. Where do you "Leave the -Credentials parameter" at?

      Delete
    2. You will need to use your app password for MFA instead of the regular password you use. When you enable the MFA it displays a special app password string you will use for static apps or scripts to use.

      Delete
    3. Yes, When you have MFA enabled, You have to use App Passwords to authenticate through scripts! Here is how to create an App Password: How to Create App Password in Office 365?

      Delete
  8. Thank you! This is a great script.

    The output for folders and subfolders are written as ../Forms/DispForm.aspx?ID=nnn. Can you include the actual name of the folders and subfolders?

    Thank you!

    ReplyDelete
  9. CAn i use this to get permissions for a specifi library?

    ReplyDelete
  10. Salaudeen, this is great! But I'm still getting issues with lists over 5000 items -- actually, lists over 2000 items. The script seems to stall. Are you sure the list threshold issue is resolved? Do you need to paginate the query?

    ReplyDelete
  11. Seeing this error on running. Is there a problem with the function?

    PS C:\users\Victor\Desktop> .\SPO-SiteCollectionPermissionsReport.ps1

    At C:\users\Victor\Desktop\SPO-SiteCollectionPermissionsReport.ps1:127 char:48
    + ... ewXml = "2000"
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Unexpected token 'RecursiveAll">2000"' in expression or statement.
    + CategoryInfo : ParserError: (:) [], ParseException
    + FullyQualifiedErrorId : UnexpectedToken

    ReplyDelete
    Replies
    1. Wrap "RecursiveAll" with 'RecursiveAll' (Replace "" with ')

      Delete
    2. Thanks for the update. Any idea how to update this further? We have some pretty beefy lists/document library upwards of 5000 items. It seems to stall on bigger ones still.

      Delete
  12. Thanks but I noticed that this isn't iterating through the sharepoint groups and exporting out the members... can you add that?

    ReplyDelete
  13. Hey Salaudeen, it seems like this is the only script for that purpose, so thanks for writing it!

    I tried to run it and was able to successfully output some info like SC admins and permissions of the web, but when it gets to the function for the lists items, it fails with the following error:

    Error Generating Site Permission Report! Exception calling "ExecuteQuery" with "0" argument(s): "Cannot complete this action.

    Please try again."

    Any ideas where to look?

    ReplyDelete
    Replies
    1. Could happen in slow connections. Try setting RowLimit from 500 to 100

      Delete
  14. thanks works really well,
    i seem to get the below error on my larger document libraries, any idea why?
    "ExecuteQuery" with "0" argument(s): "The remote server returned an error: (429)

    thanks!

    ReplyDelete
  15. Same error as just above for me too.

    ReplyDelete
    Replies
    1. i think it has something to do with the list view setting in SP Online, I've ran successfully on a doc library with 4000 files but it failed on one with 9000

      Delete
    2. Script has been updated with batch processing and progress bars. Try now!

      Delete
  16. This has helped and working well as is. Thank you!

    ReplyDelete
  17. thanks for the script! I am running it over one document library where sub-folders have different permissions. is there a way to change column A so that it includes the true path for example it's currently showing
    https://hereismy.sharepoint.com/sites/hereismysitename/Documents/Forms/DispForm.aspx?ID=8280
    but I would like to show the folder structure instead of the ID:
    https://hereismy.sharepoint.com/sites/hereismysitename/Documents/name_of_folder/name_of_subfolder

    Also can you please confirm (I think I saw it in your screen shot) that if a user is added direct to the Document Library (not to a sharepoint permission group) that there name will appear on the output. Thank you :)

    ReplyDelete
  18. Thank you for this great script. I'm getting this error if you could help :

    Error Generating User Permission Report! Exception calling "ExecuteQuery" with "0" argument(s): "The remote server returned an error: (500) Internal Server Error."

    I ran a search and found many "ExecuteQuery()". Do i need to put a value in everyone? and what value should they be?

    Thank you

    ReplyDelete
  19. Hi, I'm having this error while running the script. Any clues of what might be wrong or where I could look for the problem? Thanks in advance!

    Error Generating Site Permission Report! Exception calling "ExecuteQuery" with "0" argument(s): "Entity (External Content Type) cannot be found with Namespace = 'namespace', Name = 'Role'."

    ReplyDelete
  20. Hi,

    I have followed the script where we can find SharePoint Groups and their members along with the permissions, but it doesn't give unique users list and their permissions and second , it gives GUIDs instead of names . Can you look into this ?

    Thanks

    ReplyDelete
    Replies
    1. And this doesn't include AD groups too. We are looking for a script where we can find permissions of all groups ( AD and SharePoint both ) and users ( permissions given directly ) for a SharePoint Online site collection . How can this be achieved ?

      Delete
  21. Error Generating Site Permission Report! Exception calling "ExecuteQuery" with "0" argument(s): "The remote server retur
    ned an error: (401) Unauthorized."

    ReplyDelete
  22. Thanks for a great script!
    Question - is it possible to run this on a specific folder/subfolder in the document library?

    ReplyDelete
  23. Hi, Is it possible to have it show the AzureAD displayName instead of the ObjectID on the Users?

    ReplyDelete
  24. Hi, thanks for the script. I got below, can you give me some suggestions please?
    ---------------------
    Error Generating Site Permission Report! Exception calling "ExecuteQuery" with "0" argument(s): "The remote server returned an error: (500) Internal Server Error."

    ReplyDelete
  25. Version 1 Script is taking an exceptionally long time to run. Is this typical? I am analyzing a fairly large site collection. It has been running for over 2 hours now and not sure how close it is to completion. Can PowerShell handle two Progress Meters, The first to show progress over the entire collection and the second show progress as it currently does?

    ReplyDelete
  26. This is excellent stuff. I am concerned however about the execution time. I am analyzing a fairly large site collection and the script has been running for over 2 hours now. Is this typical?

    Also, can PowerShell handle 2 Progress Meters, where the first can show progress over the entire collection and the second as you currently have it?

    ReplyDelete
  27. I've tried running this script (the v2 version) on an office 365 site, but it doesn't seem to be working recursively. It lists the permissions of the objects on the site, but not for any folders or files in a document library. Any ideas on what I'm doing wrong?

    ReplyDelete
    Replies
    1. I've tried to do some debugging on this and when i put a break at "foreach ($Subweb in $Web.Webs)" and then look for at $web.webs.count it comes up as 0. The site I'm looking at has a few lists and a document library with some folders with unique permissions, but these are not being interrogated. Also the "If($recursive)" line is only being reached after the permissions of the immediate sites contents are noted. Is this normal?

      Delete
  28. Awesome script, thanks a bunch! Saved me sooo much time.

    ReplyDelete
  29. will the same script work for 2010?I need to compare source and target

    ReplyDelete
  30. Hi Salaudeen, are we able to iterate through subsites and subsites of subsites?

    ReplyDelete
    Replies
    1. Hi Tony, Yes! This PowerShell script iterates through all subsites of all levels recursively.

      Delete

Please Login and comment to get your questions answered!

Powered by Blogger.