This script generates a CSV report of all site permissions for a given sharepoint online site collection url. It includes permissions of subsites. Unless the permissions are broken subsite will have its parent web permissions. 


SharePoint Client DLLs - would be available with SharePoint Client SDK intallation or can be taken from SharePoint farm. The location path can be found in comment. 

Load-CSOMProperties.ps1 - a utility script taken from


Here is my another article that fetches permissions of a single site using PnP approach.

Edit: Now the script optionally includes group users and their principal type. Groups and Groups users principal type are shown in the same field.


$admin = "<your account>" 
$pass = ConvertTo-SecureString "<password>" -AsPlainText -Force 
$OutputFile = "C:\Temp\AllSitePermissions.csv" 
$header = "Site,HasUniquePerm?,Group Name,Group Owner,Login Name,Roles,Principal Type," 
$header +if($includeGroupUsers) { "Group User LoginName,Group User DisplayName" } else { "" } 
Set-Content $OutputFile $header 
Function Get-SPOAllSitePermissions ($url,$includeGroupUsers) 
    $ctx = New-Object Microsoft.SharePoint.Client.ClientContext($url) 
    $ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($admin$pass) 
    $web = $ctx.Web 
    Load-CSOMProperties -Object $web -PropertyNames @("HasUniqueRoleAssignments""Url""Title")     
    Write-Host $web.Url 
    $webUrl = $web.Url     
    $record = "`"$webUrl`",$($web.HasUniqueRoleAssignments),"      
    if($web.HasUniqueRoleAssignments -eq $true) { 
        $firstIteration = $true #helps when to append commas 
        foreach($roleAssignment in $ctx.Web.RoleAssignments) { 
            Load-CSOMProperties -Object $roleAssignment -PropertyNames @("Member","RoleDefinitionBindings") 
            $roles = ($roleAssignment.RoleDefinitionBindings | Select -ExpandProperty Name) -join ", "; 
            $loginName = if($roleAssignment.Member.PrincipalType -eq "User") { $($roleAssignment.Member.LoginName) } else { "" } 
            $record +if($firstIteration) { "" } else { ",," } 
            $record +"`"$($roleAssignment.Member.Title)`",`"$($roleAssignment.Member.OwnerTitle)`"," 
            $record +"`"$loginName`",`"$roles`","    
            $record +$($roleAssignment.Member.PrincipalType) 
            Add-Content $OutputFile $record 
            $firstIteration = $false 
            $record = "" 
            if($includeGroupUsers) { 
                if($roleAssignment.Member.PrincipalType -eq "SharePointGroup") { 
                    $group = $ctx.Web.SiteGroups.GetByName($roleAssignment.Member.Title)     
                    $users = $group.Users 
                    foreach($user in $users) { 
                        $record = ",,,,,," 
                        $record +"$($user.PrincipalType)," 
                        $record +"`"$($user.LoginName)`",`"$($user.Title)`"" 
                        Add-Content $OutputFile $record 
                        $record = ""                     
    else { 
        Add-Content $OutputFile $record #you can refer the permissions from its parent web. 
    if($web.Webs.Count -eq 0) 
    else { 
        foreach ($web in $web.Webs) { 
            Get-SPOAllSitePermissions -Url $web.Url 
# Paths to SDK. Please verify location on your computer. 
# On farm it would be available at c:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\ 
Add-Type -Path "Microsoft.SharePoint.Client.dll"  
Add-Type -Path "Microsoft.SharePoint.Client.Runtime.dll" 
#Pass "$true" for second parameter to get the group users 
Get-SPOAllSitePermissions "<your site collection url here>" $false