SharePoint Users and Groups Permission Analysis Report for Site Collection

Requirement: For security audit purpose, had to generate a comprehensive users and groups security report for entire site collection. Some of the sub-sites, some of the lists & libraries are with unique permission.

Solution: Lets use PowerShell with little HTML & CSS to generate a users and groups permission Report!

On executing the PowerShell script, will generate a HTML file as in the below screen with:

  • Table of content for all Sub-sites in the site collection
  • List of site collection administrators
  • Users who has access on the site
  • Site’s Groups granted access
  • Members of each group which has access to the site.
  • Lists with unique permissions and its users & groups.

To get all users and permissions (Roles granted directly and their groups), We can simply use this PowerShell code:

Get-SPWeb "https://sharepoint.crescent.com/sites/operations" | Get-SPUser | select LoginName, @{name="Exlicit Permissions";expression={$_.Roles}}, @{name="Permissions given via Groups";Expression={$_.Groups | %{$_.Roles}}} ,Groups  | format-Table -Wrap -AutoSize

But to fetch few more details and to generate report, I use the below code:

SharePoint Users and Permission Analysis Report:

Here goes our HTML template Code: In run time, The data being generated will be merged with the HTML code to get a formatted report in HTML format.  

Save the below content as “Table.htm” file in the same path where you save PowerShell script.

<html>
<head>
<!-- Javascript goes in the document HEAD -->
<script type="text/javascript">
function altRows(id){
 if(document.getElementsByTagName){  
  
  var table = document.getElementById(id);  
  var rows = table.getElementsByTagName("tr"); 
   
  for(i = 0; i < rows.length; i++){          
   if(i % 2 == 0){
    rows[i].className = "evenrowcolor";
   }else{
    rows[i].className = "oddrowcolor";
   }      
  }
 }
}
window.onload=function(){
 altRows('alternatecolor');
}
</script>

<!-- CSS goes in the document HEAD or added to your external stylesheet -->
<style type="text/css">
body{
font-family: Calibri; 
 height: 12pt; 
}
table.altrowstable {
 border-collapse: collapse; 
 font-family: verdana,arial,sans-serif;
 font-size:11px;
 color:#333333;
 border-width: 1px;
 border-color: #a9c6c9;
 border: b1a0c7 0.5pt solid; 
 
}
table.altrowstable th {
 border-width: 1px;
 padding: 5px;
 background-color:#8064a2;
 border: #b1a0c7 0.5pt solid;
 font-family: Calibri; 
 height: 15pt; 
 color: white; 
 font-size: 11pt; 
 font-weight: 700; 
 text-decoration: none;
}
table.altrowstable td {
 border: #b1a0c7 0.5pt solid; font-family: Calibri; height: 15pt; color: black; font-size: 11pt; font-weight: 400; text-decoration: none; 
}
.oddrowcolor{
 background-color: #e4dfec;
}
.evenrowcolor{
 background-color:#FFFFFF;
}
</style>
</head>
<body>

PowerShell Script to Generate Users and Groups Permission Report:

[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")

#Region MOSS 2007 Cmdlets 

 Function global:Get-SPSite($url)
 {
  if($url -ne $null)
     {
     return New-Object Microsoft.SharePoint.SPSite($url)
  }
 }
 
 Function global:Get-SPWeb($url)
 {
   $site= Get-SPSite($url)
         if($site -ne $null)
             {
                $web=$site.OpenWeb();
       
             }
     return $web
 }
 
#EndRegion

Function GenerateUserAccessReport()
{
 #Site Collection URL - Mandatory parameter
 param( [Parameter(Mandatory=$true)] [string]$SiteCollectionURL)

  #Get the site collection
  $Site= Get-SPSite $SiteCollectionURL
  #Append the HTML File with CSS into the Output report
  $Content=Get-Content -Path "table.htm" > PermissionReport.htm
  
 "<h2> $($Site.RootWeb.Title) - Users & Groups Permission Report</h2>" >> PermissionReport.htm 
  
  #Table of Contents
  "<h3> List of Sites</h3> <table class='altrowstable' id='alternatecolor' cellpadding='5px'><tr><th>Site Name </th><th> URL </th></tr>" >> PermissionReport.htm
    #Get Users of All Webs : Loop throuh all Sub Sites
       foreach($Web in $Site.AllWebs) 
       {
    "<tr> <td> <a href='#$($web.Title.ToLower())'>$($web.Title)</a> </td><td> $($web.URL)</td> </tr>" >> PermissionReport.htm
    }
  
   #Site Collection Administrators Heading 
  "</table><br/><b>Site Collection Administrators</b>" >> PermissionReport.htm

  "<table class='altrowstable' id='alternatecolor' cellpadding='5px'><tr>" >> PermissionReport.htm

  #Write CSV (TAB Separated File) Header
  "<th>User Account ID </th> <th>User Name </th></tr>" >> PermissionReport.htm 
  
    #Get All Site Collection Administrators
     $Site.RootWeb.SiteAdministrators | sort $_.Name | ForEach-Object   {  
   "<tr><td> $($_.LoginName) </td> <td> $($_.Name)</td></tr> " >> PermissionReport.htm
  }
       
    #Get Users of All Webs : Loop throuh all Sub Sites
       foreach($Web in $Site.AllWebs) 
       { 
      #Check if site is using Unique Permissions or Inheriting from its Parent Site!
      if($Web.HasUniqueRoleAssignments -eq $true)
   {
         "</table><br/><hr> <h3>Site: [ <a name='$($Web.Title.ToLower())'>$($Web.Title) </a> ] at <a href='$($web.URL)'>$($web.URL)</a> is using Unique Permissions. </h3>" >> PermissionReport.htm
   }
   else
   {
     "</table><br/><hr> <h3>Site: [ <a name='$($Web.Title.ToLower())'>$($Web.Title) </a> ] at <a href='$($web.URL)'>$($web.URL)</a> is Inheriting Permissions from its Parent Site.</h3>" >> PermissionReport.htm
   }
  
      #Get the Users & Groups from site which has unique permissions - TOP sites always with Unique Permissions
      if($Web.HasUniqueRoleAssignments -eq $True)
             {
           #*** Get all the users granted permissions DIRECTLY to the site ***
     "<b>Site Users and Groups</b><table class='altrowstable' id='alternatecolor' cellpadding='5px'><tr>" >> PermissionReport.htm
     "<th>Users/Groups </th> <th> Type </th><th> User Name </th> <th>Permissions</th></tr>" >> PermissionReport.htm
              foreach($WebRoleAssignment in $Web.RoleAssignments ) 
                  { 
         #*** Get  User/Group Name *****#
         $UserGroupName=$WebRoleAssignment.Member.Name
         
         #**** Get User/Group Type *****#
       #Is it a SharePoint Group?
        if($WebRoleAssignment.Member.GetType() -eq [Microsoft.SharePoint.SPGroup]) 
        {
         $Type="SharePoint Group"
         $UserName=$WebRoleAssignment.Member.Name
         #Set Flag value for "Group Exists"
         $GroupExistsFlag=$true
        }
                        #Is it a SharePoint User Account or Domain Group?
        else
        {
            $UserName=$WebRoleAssignment.Member.LoginName
         #Is it a Domain Group?
         if($WebRoleAssignment.Member.IsDomainGroup)
         {
           $Type="Domain Group"
         }
         else #if($WebRoleAssignment.Member.LoginName)    
         {
           $Type="SharePoint User"
         }
        }

        #Get the Permissions assigned to Group/User
         $WebUserPermissions=@()
           foreach ($RoleDefinition  in $WebRoleAssignment.RoleDefinitionBindings)
           {
                           $WebUserPermissions += $RoleDefinition.Name +";"
                          }
       
        #Send the Data to Log file
        " <tr> <td> $($UserGroupName) </td><td> $($Type) </td><td> $($UserName) </td><td>  $($WebUserPermissions)</td></tr>" >> PermissionReport.htm
      }
     
     #****** Get the Group members *********#
       "</table></br> " >>PermissionReport.htm
       if($GroupExistsFlag -eq $true)
       {
        "<b>Group Users</b><table class='altrowstable' id='alternatecolor' cellpadding='5px'><tr>" >>PermissionReport.htm
       foreach($WebRoleAssignment in $Web.RoleAssignments ) 
                   { 
        #Is it a SharePoint Group?
        if($WebRoleAssignment.Member.GetType() -eq [Microsoft.SharePoint.SPGroup])    
         {
            "<th>Group: $($WebRoleAssignment.Member.Name)</th></tr> " >> PermissionReport.htm 
                           foreach($user in $WebRoleAssignment.member.users)
                               {
           #Send the Data to Log file
           " <tr><td></td> <td> $($user.Name) </td><td> $($user.LoginName) </td><td> $($user.Email)</td><tr>" >> PermissionReport.htm
          }
         }
                     }
      }
      #Reset Group Exists Flag 
      $GroupExistsFlag=$false
     }
     
     #********  Check Lists with Unique Permissions ********/
              foreach($List in $Web.lists)
              {
      #Skip the Hidden Lists
                  if( ($List.HasUniqueRoleAssignments -eq $True) -and  ($List.Hidden -eq $false))
                  {
       "</table><br/><b>Users and Groups in List: [ $($List.Title) ] at <a href='$($List.ParentWeb.Url)/$($List.RootFolder.Url)'>$($List.ParentWeb.Url)/$($List.RootFolder.Url)</a> with Unique Permissions.</b><table class='altrowstable' id='alternatecolor' cellpadding='5px'><tr>" >> PermissionReport.htm
       "<th>Users/Groups </th><th>  Type </th><th> User Name </th><th> Permissions</th></tr>" >> PermissionReport.htm 
                  
         #Get all the users granted permissions to the list
               foreach($ListRoleAssignment in $List.RoleAssignments ) 
                   { 
          #*** Get  User/Group Name *****#
          $UserGroupName=$ListRoleAssignment.Member.Name
         
        #**** Get User/Group Type *****#
       #Is it a SharePoint Group?
        if($ListRoleAssignment.Member.GetType() -eq [Microsoft.SharePoint.SPGroup]) 
        {
         $Type="SharePoint Group"
         $UserName=$ListRoleAssignment.Member.Name
        }
                        #Is it a SharePoint User Account or Domain Group?
        else
        {
            $UserName=$ListRoleAssignment.Member.LoginName
         #Is it a Domain Group?
         if($ListRoleAssignment.Member.IsDomainGroup)
         {
           $Type="Domain Group"
         }
         else #if($ListRoleAssignment.Member.LoginName)    
         {
           $Type="SharePoint User"
         }
        }

        #Get the Permissions assigned to Group/User
         $ListUserPermissions=@()
           foreach ($RoleDefinition  in $ListRoleAssignment.RoleDefinitionBindings)
           {
                           $ListUserPermissions += $RoleDefinition.Name +";"
                          }
       
        #Send the Data to Log file
        "<tr><td>$($UserGroupName) </td><td> $($Type) </td><td> $($UserName) </td><td>  $($ListUserPermissions)</td></tr>" >> PermissionReport.htm
        }
        "</table>" >>PermissionReport.htm
                }
                }
    } 
                 "</body></html>" >>PermissionReport.htm
   }

#Call the function to Get Users & groups Report
GenerateUserAccessReport "https://sharepoint.crescent.com/sites/compliance"

Report output:

SharePoint Users and Groups Permission Report

For SharePoint Online Permission report, use: SharePoint-Online Site Collection Permission Report using PowerShell

Salaudeen Rajack

Salaudeen Rajack - SharePoint Expert with Two decades of SharePoint Experience. Love to Share my knowledge and experience with the SharePoint community, through real-time articles!

26 thoughts on “SharePoint Users and Groups Permission Analysis Report for Site Collection

  • Getting error PermissionReport.htm file is being used by another process.
    out-file : The process cannot access the file ‘XXXXScriptPermissionReport.htm’ because it is being used by another process.
    At XXXScriptGetUserAccessReportSP13.ps1:190 char:9

    + CategoryInfo : OpenError: (:) [Out-File], IOException
    + FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.OutFileCommand

    Reply
  • Team,
    can you please help me out with audiences home page content
    because we have different web parts associated and how will pull all audiencing report

    Thanks,
    Venkat

    Reply
  • Hello, I desire to subscribe for this weblog to get most up-to-date
    updates, thus where can i do it please assist.

    Reply
  • Will this script work with SP 2013? Thx!

    Reply
  • Hi Salaudeen,
    Is this script able to be easily modified to run on SharePoint Online / 365? I’ve tried to run this and I’m given the following error.

    New-Object : Cannot find type [Microsoft.SharePoint.SPSite]: verify that the assembly containing this type is loaded.
    At line:9 char:13
    + return New-Object Microsoft.SharePoint.SPSite($url)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
    + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

    Reply
  • This is an AMAZING piece of work. You saved me days of work.

    thanks!

    Reply
  • In the function, GenerateUserAccessReport, I’m getting an error when it tries to pull the Site Collection Administrators.

    The variable ‘$_’ cannot be retrieved because it has not been set.
    At line:55 char:46
    + $Site.RootWeb.SiteAdministrators | sort $_.Name | ForEach-Object {
    + ~~
    + CategoryInfo : InvalidOperation: (_:String) [], RuntimeException
    + FullyQualifiedErrorId : VariableIsUndefined

    The line referenced in the error is
    $Site.RootWeb.SiteAdministrators | sort $_.Name | ForEach-Object {

    The script continued and finished, but the Site Collection Administrator section of the report was empty. I changed

    sort $_.Name
    to
    sort $Site.RootWeb.SiteAdministrators.Name

    and it ran with no errors and the Site Collection Admin section was filled in.

    Any idea why it’s not taking the piped input for $_? I’m running it from ISE started with Run as Administrator, and I’m running SP 2013 & PS 5.0, if that makes any difference.

    Reply
  • For Folders which have unique permission its not able to get the URL and throwing error as below

    Method invocation failed because [System.String] doesn’t contain a method named ‘op_Division’.
    At E:ScriptsPermissionReport.ps1:220 char:69
    + $FolderURL=$folder.ParentList.ParentWeb.URL/ <<<< $folder.Url + CategoryInfo : InvalidOperation: (op_Division:String) [], RuntimeException + FullyQualifiedErrorId : MethodNotFound

    Reply
  • I’m sorry for double post but I found solution to the error message. Now, I’ve got a repport file, but it’s empty. I have only cells with color and title, but i’ve no entry, and no powershell error message…
    An idea?!

    Reply
    • Are you running this script as “Farm Administrator” with FULL control permissions? Make sure you choose “Run as Administrator” too.

      Reply
  • I get an error when I run your script :

    + GenerateUserAccessReport <<<< https://global + CategoryInfo : NotSpecified: (:) [GenerateUserAccessReport], Ta rgetInvocationException + FullyQualifiedErrorId : System.Reflection.TargetInvocationException,Gene rateUserAccessReport Have U got an idea? Thks.

    Reply
  • Nothing is generated for me, I saved the files into two parts one is Table.htm and another one is GenerateUserAccessReport.ps1 , I ran the command as GenerateUserAccessReport “https://xxxxxxxxx” but it is not generating any file there.

    Reply
    • Try Changing these lines: Instead of: >>PermissionReport.htm, change it to: >>c:PermissionReport.htm or something like that. Also, Make sure you are running this script from a SharePoint WFE as a Farm Admin!

      Reply
  • I get an error when running powershell, cannot find path “table.htm” because it does not exist.

    Reply
    • Create “Table.htm” with the content provided, on the same path where this script located.

      Reply
  • Saluadeen-
    This is a great report and it covers everything I am looking for. I’m very familiar with PowerShell however I’m have no experience with HTML. How do I get these scripts to work together?

    Thanks in advance

    Reply
    • Hi there,
      The html code applies formating to the report here. Just save the html code as “table.htm” on the same path where you save the script, and execute the script.

      Reply
    • Great…I did save the script in the path.

      I ran the GenerateUserAccessReport along with the site URL but nothing happened. The SP2010 Mgmt did not error out. I checked the directory where the PowerShell script and the table.htm are located and did not see any new files.

      Here are some more question..
      Where to I go to see the report? Am I suppose to go to a particular URL or local path?

      Does the PowerShell script need to be update with my site collection URL?
      I did not see anywhere in the script that required to be update.

      Thank you again

      Reply
    • mmjr,

      You will get the Output report PermissionReport.htm on the same path where you save this script.
      Only thing you need to change is the Site collectin URL passed to function:GenerateUserAccessReport

      Reply

Leave a Reply

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