Originally posted on: http://geekswithblogs.net/bjackett/archive/2013/07/01/powershell-script-to-enumerate-sharepoint-2010-or-2013-permissions-and.aspx In this post I will present a script to enumerate SharePoint 2010 or 2013 permissions across the entire farm down to the site (SPWeb) level. As a bonus this script also recursively expands the membership of any Active Directory (AD) group including nested groups which you wouldn’t be able to find through the SharePoint UI. History Back in 2009 (over 4 years ago now) I published one my most read blog posts about enumerating SharePoint 2007 permissions. I finally got around to updating that script to remove deprecated APIs, supporting the SharePoint 2010 commandlets, and fixing a few bugs. There are 2 things that script did that I had to remove due to major architectural or procedural changes in the script. Indenting the XML output Ability to search for a specific user I plan to add back the ability to search for a specific user but wanted to get this version published first. As for indenting the XML that could be added but would take some effort. If there is user demand for it (let me know in the comments or email me using the contact button at top of blog) I’ll move it up in priorities. As a side note you may also notice that I’m not using the Active Directory commandlets. This was a conscious decision since not all environments have them available. Instead I’m relying on the older [ADSI] type accelerator and APIs. It does add a significant amount of code to the script but it is necessary for compatibility. Hopefully in a few years if I need to update again I can remove that legacy code. Solution Below is the script to enumerate SharePoint 2010 and 2013 permissions down to site level. You can also download it from my SkyDrive account or my posting on the TechNet Script Center Repository. SkyDrive TechNet Script Center Repository http://gallery.technet.microsoft.com/scriptcenter/Enumerate-SharePoint-2010-35976bdb 001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 ########################################################### #DisplaySPWebApp8.ps1 # #Author: Brian T. Jackett #Last Modified Date: 2013-07-01 # #Traverse the entire web app site by site to display # hierarchy and users with permissions to site. ########################################################### function Expand-ADGroupMembership { Param ( [Parameter(Mandatory=$true, Position=0)] [string] $ADGroupName, [Parameter(Position=1)] [string] $RoleBinding ) Process { $roleBindingText = "" if(-not [string]::IsNullOrEmpty($RoleBinding)) { $roleBindingText = " RoleBindings=`"$roleBindings`"" } Write-Output "<ADGroup Name=`"$($ADGroupName)`"$roleBindingText>" $domain = $ADGroupName.substring(0, $ADGroupName.IndexOf("\") + 1) $groupName = $ADGroupName.Remove(0, $ADGroupName.IndexOf("\") + 1) #BEGIN - CODE ADAPTED FROM SCRIPT CENTER SAMPLE CODE REPOSITORY #http://www.microsoft.com/technet/scriptcenter/scripts/powershell/search/users/srch106.mspx #GET AD GROUP FROM DIRECTORY SERVICES SEARCH $strFilter = "(&(objectCategory=Group)(name="+($groupName)+"))" $objDomain = New-Object System.DirectoryServices.DirectoryEntry $objSearcher = New-Object System.DirectoryServices.DirectorySearcher $objSearcher.SearchRoot = $objDomain $objSearcher.Filter = $strFilter # specify properties to be returned $colProplist = ("name","member","objectclass") foreach ($i in $colPropList) { $catcher = $objSearcher.PropertiesToLoad.Add($i) } $colResults = $objSearcher.FindAll() #END - CODE ADAPTED FROM SCRIPT CENTER SAMPLE CODE REPOSITORY foreach ($objResult in $colResults) { if($objResult.Properties["Member"] -ne $null) { foreach ($member in $objResult.Properties["Member"]) { $indMember = [adsi] "LDAP://$member" $fullMemberName = $domain + ($indMember.Name) #if($indMember["objectclass"] # if child AD group continue down chain if(($indMember | Select-Object -ExpandProperty objectclass) -contains "group") { Expand-ADGroupMembership -ADGroupName $fullMemberName } elseif(($indMember | Select-Object -ExpandProperty objectclass) -contains "user") { Write-Output "<ADUser>$fullMemberName</ADUser>" } } } } Write-Output "</ADGroup>" } } #end Expand-ADGroupMembership # main portion of script if((Get-PSSnapin -Name microsoft.sharepoint.powershell) -eq $null) { Add-PSSnapin Microsoft.SharePoint.PowerShell } $farm = Get-SPFarm Write-Output "<Farm Guid=`"$($farm.Id)`">" $webApps = Get-SPWebApplication foreach($webApp in $webApps) { Write-Output "<WebApplication URL=`"$($webApp.URL)`" Name=`"$($webApp.Name)`">" foreach($site in $webApp.Sites) { Write-Output "<SiteCollection URL=`"$($site.URL)`">" foreach($web in $site.AllWebs) { Write-Output "<Site URL=`"$($web.URL)`">" # if site inherits permissions from parent then stop processing if($web.HasUniqueRoleAssignments -eq $false) { Write-Output "<!-- Inherits role assignments from parent -->" } # else site has unique permissions else { foreach($assignment in $web.RoleAssignments) { if(-not [string]::IsNullOrEmpty($assignment.Member.Xml)) { $roleBindings = ($assignment.RoleDefinitionBindings | Select-Object -ExpandProperty name) -join "," # check if assignment is SharePoint Group if($assignment.Member.XML.StartsWith('<Group') -eq "True") { Write-Output "<SPGroup Name=`"$($assignment.Member.Name)`" RoleBindings=`"$roleBindings`">" foreach($SPGroupMember in $assignment.Member.Users) { # if SharePoint group member is an AD Group if($SPGroupMember.IsDomainGroup) { Expand-ADGroupMembership -ADGroupName $SPGroupMember.Name } # else SharePoint group member is an AD User else { # remove claim portion of user login #Write-Output "<ADUser>$($SPGroupMember.UserLogin.Remove(0,$SPGroupMember.UserLogin.IndexOf("|") + 1))</ADUser>" Write-Output "<ADUser>$($SPGroupMember.UserLogin)</ADUser>" } } Write-Output "</SPGroup>" } # else an indivdually listed AD group or user else { if($assignment.Member.IsDomainGroup) { Expand-ADGroupMembership -ADGroupName $assignment.Member.Name -RoleBinding $roleBindings } else { # remove claim portion of user login #Write-Output "<ADUser>$($assignment.Member.UserLogin.Remove(0,$assignment.Member.UserLogin.IndexOf("|") + 1))</ADUser>" Write-Output "<ADUser RoleBindings=`"$roleBindings`">$($assignment.Member.UserLogin)</ADUser>" } } } } } Write-Output "</Site>" $web.Dispose() } Write-Output "</SiteCollection>" $site.Dispose() } Write-Output "</WebApplication>" } Write-Output "</Farm>" The output from the script can be sent to an XML which you can then explore using the [XML] type accelerator. This lets you explore the XML structure however you see fit. See the screenshot below for an example. If you do view the XML output through a text editor (Notepad++ for me) notice the format. Below we see a SharePoint site that has a SharePoint group Demo Members with Edit permissions assigned. Demo Members has an AD group corp\developers as a member. corp\developers has a child AD group called corp\DevelopersSub with 1 AD user in that sub group. As you can see the script recursively expands the AD hierarchy. Conclusion It took me 4 years to finally update this script but I‘m happy to get this published. I was able to fix a number of errors and smooth out some rough edges. I plan to develop this into a more full fledged tool over the next year with more features and flexibility (copy permissions, search for individual user or group, optional enumerate lists / items, etc.). If you have any feedback, feature requests, or issues running it please let me know. Enjoy the script! -Frog Out