SharePoint Online: Permission Report for Specific User in a Site Collection using PowerShell

Requirement:  Generate a permission report to audit a specific user's permissions in given SharePoint Online site collection, sub-sites, all its lists, libraries and list items.

How to Check User Permissions in SharePoint Online?
To get user permissions in SharePoint Online,
  • Navigate to the SharePoint Online site on which you want to check permissions.
  • Click on Settings Gear >> Site Settings >> Click on "Site Permissions" link.
  • On the Site permissions page, under Permissions tab, click "Check Permissions" icon in the top ribbon.
  • In the User/Group field, Enter the User Name which you want to verify permissions, and click on "Check Now" button
  • In a moment, You will see list of permissions the user has on a site.
    sharepoint online check user permissions powershell
But the problem is: This method of checking user permissions shows access to one site at a time. You should repeat these steps in each site, if you want to check user access for a site collection. So lets use PowerShell to generate user permissions report for SharePoint Online.

SharePoint Online PowerShell permissions report
This PowerShell script checks user permissions and exports the findings into a CSV file. How to run this script? Just change the Parameters from Line#6 to Line#8 according to your environment and hit run.
sharepoint online powershell check user permissions

SharePoint Online: PowerShell to get user permissions on a given site collection 
Here is how to check user permissions using PowerShell in SharePoint Online. Please note, there is a limitation in the script: This PowerShell script doesn't scan Active Directory security groups!
#Load SharePoint CSOM Assemblies
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"
  
#Set parameter values
$SiteURL="https://crescent.sharepoint.com"
$UserAccount="i:0#.f|membership|Salaudeen@crescent.com"
$ReportFile="C:\Temp\PermissionRpt.csv"
$BatchSize = 500
 
#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))
}
 
#sharepoint online powershell to get user permissions Applied on a particular Object, such as: Web, List, Folder 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/Folder"
 
            #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()
 
                #Check direct permissions
                if($RoleAssignment.Member.PrincipalType -eq "User")
                {
                    #Is the current user is the user we search for?
                    if($RoleAssignment.Member.LoginName -eq $SearchUser.LoginName)
                    {
                        Write-Host  -f Cyan "Found the User under direct permissions of the $($ObjectType) at $($ObjectURL)"
                         
                        #Get the Permissions assigned to user
                        $UserPermissions=@()
                        $Ctx.Load($RoleAssignment.RoleDefinitionBindings)
                        $Ctx.ExecuteQuery()
                        foreach ($RoleDefinition in $RoleAssignment.RoleDefinitionBindings)
                        {
                            $UserPermissions += $RoleDefinition.Name +";"
                        }
                        #Send the Data to Report file
                        "$($ObjectURL) `t $($ObjectType) `t $($Object.Title)`t Direct Permission `t $($UserPermissions)" | Out-File $ReportFile -Append
                    }
                }
                 
                Elseif($RoleAssignment.Member.PrincipalType -eq "SharePointGroup")
                {
                        #Search inside SharePoint Groups and check if the user is member of that group
                        $Group= $Web.SiteGroups.GetByName($RoleAssignment.Member.LoginName)
                        $GroupUsers=$Group.Users
                        $Ctx.Load($GroupUsers)
                        $Ctx.ExecuteQuery()
 
                        #Check if user is member of the group
                        Foreach($User in $GroupUsers)
                        {
                            #Check if the search users is member of the group
                            if($user.LoginName -eq $SearchUser.LoginName)
                            {
                                Write-Host -f Cyan "Found the User under Member of the Group '$($RoleAssignment.Member.LoginName)' on $($ObjectType) at $($ObjectURL)"
 
                                #Get the Group's Permissions on site
                                $GroupPermissions=@()
                                $Ctx.Load($RoleAssignment.RoleDefinitionBindings)
                                $Ctx.ExecuteQuery()
                                Foreach ($RoleDefinition  in $RoleAssignment.RoleDefinitionBindings)
                                {
                                    $GroupPermissions += $RoleDefinition.Name +";"
                                }          
                                #Send the Data to Report file
                                "$($ObjectURL) `t $($ObjectType) `t $($Object.Title)`t Member of '$($RoleAssignment.Member.LoginName)' Group `t $($GroupPermissions)" | Out-File $ReportFile -Append
                            }
                        }
                }
            }
}

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()
 
    #Get the User object
    $SearchUser = $Web.EnsureUser($UserAccount)
    $Ctx.Load($SearchUser)
    $Ctx.ExecuteQuery()
 
    #Write CSV- TAB Separated File) Header
    "URL `t Object `t Title `t PermissionType `t Permissions" | out-file $ReportFile
 
    Write-host -f Yellow "Searching in the Site Collection Administrators Group..."
    #Check if Site Collection Admin
    If($SearchUser.IsSiteAdmin -eq $True)
    {
        Write-host -f Cyan "Found the User under Site Collection Administrators Group!"
        #Send the Data to report file
        "$($Web.URL) `t Site Collection `t $($Web.Title)`t Site Collection Administrator `t Site Collection Administrator" | Out-File $ReportFile -Append
    }
 
 
    #Function to Check Permissions of All List Items of a given List
    Function Check-SPOListItemsPermission([Microsoft.SharePoint.Client.List]$List)
    {
        Write-host -f Yellow "Searching in List Items of 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 threashold issue on larger lists
        Do {  
            #Get items from the list in Batch
            $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 Check Permissions of all lists from the web
    Function Check-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
                Check-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 Check Webs's Permissions from given URL
    Function Check-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 "Searching in the 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 "Searching in the Lists and Libraries of "$Web.URL"..."
        Check-SPOListPermission($Web)
  
        #Iterate through each subsite in the current web
        Foreach ($Subweb in $web.Webs)
        {
                #Call the function recursively                            
                Check-SPOWebPermission($SubWeb)
        }
    }
 
    #Call the function with RootWeb to get site collection permissions
    Check-SPOWebPermission $Web
 
    Write-host -f Green "User Permission Report Generated Successfully!"
    }
Catch {
    write-host -f Red "Error Generating User Permission Report!" $_.Exception.Message
}
and the result of SharePoint Online user permissions report using PowerShell: Script generates a CSV file in below format.
sharepoint online powershell get user permissions
If you are looking for permission report on all users of the site collection, use my another script: SharePoint Online: PowerShell Permissions Report
SharePoint Online: Permission Report for Specific User in a Site Collection using PowerShell SharePoint Online: Permission Report for Specific User in a Site Collection using PowerShell Reviewed by Salaudeen Rajack on September 04, 2018 Rating: 5

14 comments:

  1. awesome script !!! thank you.

    ReplyDelete
  2. wonderfull.. you saved my day..

    Adding one more If condition to full Security groups is working absolutely fine.
    $RoleAssignment.Member.PrincipalType -eq "SecurityGroup"

    ReplyDelete
  3. Hi Salaudeen,

    Thanks for your script.

    As the result, it only shows the site permission information not including library and folder. Is it possible you can help to list the library permission with users and groups?

    Appreciate it if you can help.

    ReplyDelete
  4. Script working for small lists. Having problems with large lists. I am new to PowerShell. Can you please update the script to use linq to resolve performance issues?

    ReplyDelete
  5. Absolutamente bueno. Solo tengo un problema y es la conexion con el office 365

    ReplyDelete
    Replies
    1. Reducing the Row Limit from 2000 to 500 resolves the problem: Exception calling "ExecuteQuery" with "0" argument(s): "The underlying connection was closed: An unexpected error occurred on a receive."

      Delete
    2. Hi Salaudeen, I'm getting the below error
      "Error Generating User Permission Report! Exception calling "ExecuteQuery" with "0" argument(s): "The underlying connection was closed:
      An unexpected error occurred on a send."

      Please provide your comments and also how to change the row limit as mentioned above

      Delete
    3. In Line#9, set the $BatchSize to 100 from 500. Also, run the PowerShell console as "Administrator".

      Delete
  6. Hi..

    $RoleAssignment.Member.Users is available???. Seems to be not working..

    ReplyDelete
  7. Hi Salaudeen, I'm getting the error as mentioned above.. can you please let me know where to change the row limit?

    ReplyDelete
    Replies
    1. Thanks for your reply. But it's not working. I guess csom related scripts we can't run in our environment. So do you have any other scripts which can pull the permission of a user in online sharepoint?

      Delete
    2. Can you elaborate the problem please? CSOM-PowerShell scripts can be run on any desktops with CSOM SDK installed.

      Delete
  8. I would replace the below with something better which will create different file names for each user.

    Original

    [code]
    #Set parameter values
    $SiteURL="https://crescent.sharepoint.com/sites/Ops"
    $UserAccount="i:0#.f|membership|Salaudeen@Crescent.com"
    $ReportFile="C:\Temp\PermissionRpt.csv"[/code]

    My code

    [code]
    $SiteURL="https://crescent.sharepoint.com/"
    $UserEmail="Salaudeen@Crescent.com"
    $UserAccount="i:0#.f|membership|$UserEmail"
    $UserNameSplit=$UserEmail -replace '(.+?)@.+','$1'
    $ReportFile="C:\$UserNameSplit - SharePoint Permission Report.csv"
    [/code]


    Enjoy

    ReplyDelete

Please Login and comment to get your questions answered!

Powered by Blogger.