<Update 2013-07-01> This script has been updated for SharePoint 2010 / 2013. Please see my updated script and blog post at PowerShell
Script to Enumerate SharePoint 2010 or 2013 Permissions and Active Directory
Group Membership.
</Update 2013-07-01>
<Update>
Posting code didn’t format as well as hoped. Download the below script here.
</Update>
For those of you who are SharePoint admins or developers but have never dug into the SharePoint API or PowerShell, I would recommend first checking out some tutorials on both and referencing the SharePoint Developer Center. At a later date I hope to be able to provide some quick demo scripts that highlight the power, time savings, and overall usefulness that can be gained by combining PowerShell and the SharePoint API. For now though I wish to post a script I developed almost a year ago as a side project to combine a number of powerful features into one script. To start, let me overview what the below script is capable of.
- Recursively crawl a site or entire web application within SharePoint
- Enumerate permissions assigned to a SharePoint site
- Detail the SharePoint users assigned to a SharePoint group
- Determine if an Active Directory group is a member of a SharePoint group
- Detail the Active Directory users who are members of an Active Directory group
- Search for a specific user’s permissions on a SharePoint site
Before anyone says anything, yes I realize that combining so many utilities into one script is probably a bad design and I should’ve broken out functionality. Yes this is probably true, but I want to state that this script was never intended for Production release. Instead I was prototyping what was possible with PowerShell and I even surprised myself with what I ended up with. Here is an attempt to visualize what the above hierarchy would look like.
–Site
——SharePoint User A
——SharePoint Group A
————SharePoint User B
————Active Directory Group A
——————Active Directory User A
——————Active Directory User B
As you can see, this allows you to dig much further than what you might normally surface from the SharePoint API. The true purpose of this script was to determine if a user was assigned permissions anywhere within a web application, even if indirectly by membership in a SharePoint group or Active Directory group. This was only ever intended for a test environment, so you may still find some bugs when running against your own environment.
Before running this, ensure that you have loaded the SharePoint assembly with the following call (typically placed into your PowerShell profile for ease of use):
[void][System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.SharePoint”)
Please leave me feedback if you end up trying out this script or have any questions on how/why I wrote things the way I did. I always enjoy constructive criticism and dialog. If you do re-post this anywhere, be sure to include the reference to the source material for the Active Directory call portion as I borrowed it from the PowerShell Script Center.
Example call:
.DisplaySPWebApp6.ps1 http://server WebApp userA
Note: The below script does not format properly through WordPress after I migrated my blog. Please refer to the source script for better view.
########################################################### #DisplaySPWebApp6.ps1 -URL -searchScope -userToFind # #Author: Brian Jackett #Last Modified Date: Jan. 12, 2009 # #Supply Traverse the entire web app site by site to display # hierarchy and users with permissions to site. ########################################################### #DECLARE VARIABLES [string]$siteUrl = $args[0] [string]$searchScope = $args[1] [string]$userToFind = $args[2] #DECLARE CONSTANTS $BUFFER_CHARS = " " function DetermineSpaceBuffer #-iterations { [string]$spaceBuffer = "" for($i = 0; $i -lt $args[0]; $i++) {$spaceBuffer += $BUFFER_CHARS} return $spaceBuffer } #DECLARE FUNCTIONS function DrillDownADGroup #-group -depth { [string]$spaceBuffer = DetermineSpaceBuffer $args[1] $domain = $args[0].Name.substring(0, $args[0].Name.IndexOf("\") + 1) $groupName = $args[0].Name.Remove(0, $args[0].Name.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 # $colProplist = ("name","member") 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) { foreach ($member in $objResult.Properties.member) { $indMember = [adsi] "LDAP://$member" #ATTEMPT TO GET AD OBJECT TYPE FOR USER, NOT WORKING RIGHT NOW #$user = $indMember.PSBase #$user.Properties $fullUserName = $domain + ($indMember.Name) DisplayADEntry $fullUserName ($args[1]) } } } function DisplaySPGroupMembers #-group -depth { [string]$spaceBuffer = DetermineSpaceBuffer $args[1] if($args[0].Users -ne $Null) { #START SHAREPOINT USERS ENTITY Write-Output $spaceBuffer"" foreach($user in $args[0].Users) { DisplayADEntry $user ($args[1] + 1) } #END SHAREPOINT USERS ENTITY Write-Output $spaceBuffer"" } } function DisplayADEntry #-user/group -depth { #FILTER RESULTS IF LOOKING FOR SPECIFIC USER if($args[0].IsDomainGroup -eq "True") { $outputText = "$spaceBuffer$BUFFER_CHARS" + ($args[0]) Write-Output $outputText DrillDownADGroup $args[0] ($args[1]) $outputText = "$spaceBuffer$BUFFER_CHARS" Write-Output $outputText } else { #USER FOUND AS A CHILD OF AN EMBEDDED AD GROUP if(($userToFind -ne "" -and ($userToFind.ToUpper() -eq $args[0].LoginName.ToUpper() -or $userToFind.ToUpper() -eq $args[0].ToUpper())) -or $userToFind -eq "") { $outputText = "$spaceBuffer$BUFFER_CHARS" + ($args[0]) + "" Write-Output $outputText } } } function DetermineUserAccess #-web -depth { [string]$spaceBuffer = DetermineSpaceBuffer $args[1] #START SHAREPOINT GROUPS ENTITY Write-Output "$spaceBuffer" foreach($perm in $args[0].Permissions) { #CHECK IF MEMBER IS AN ACTIVE DIRECTORY ENTRY OR SHAREPOINT GROUP if($perm.XML.Contains('MemberIsUser="True"') -eq "True") { DisplayADEntry $perm.Member ($args[1] + 1) } #IS A SHAREPOINT GROUP else { $outputText = "$spaceBuffer$BUFFER_CHARS" + ($perm.Member) Write-Output $outputText DisplaySPGroupMembers $perm.Member ($args[1] + 2) Write-Output "$spaceBuffer$BUFFER_CHARS" } } #END SHAREPOINT GROUPS ENTITY Write-Output "$spaceBuffer" } function DisplayWebApplication #-webApp { [string]$spaceBuffer = DetermineSpaceBuffer $args[1] #START WEB APPLICATION ENTITY $outputText = "$spaceBuffer" + ($args[0].Name) Write-Output $outputText if($args[0].Sites -ne $Null) { #START CONTAINED SITE COLLECTIONS ENTITY Write-Output "$spaceBuffer$BUFFER_CHARS" foreach($spSiteColl in $args[0].Sites) { DisplaySiteCollection $spSiteColl ($args[1] + 2) $spSiteColl.Dispose() } #END CONTAINED SITE COLLECTIONS ENTITY Write-Output "$spaceBuffer$BUFFER_CHARS" } #END WEB APPLICATION ENTITY "$spaceBuffer" } function DisplaySiteCollection #-siteColl -depth { [string]$spaceBuffer = DetermineSpaceBuffer $args[1] $sc = $args[0].OpenWeb() #START SITE COLLECTION ENTITY $outputText = "$spaceBuffer" + ($sc.URL) Write-Output $outputText if($sc -ne $Null) { #START CONTAINED SITES ENTITY Write-Output "$spaceBuffer$BUFFER_CHARS" foreach ($spWeb in $sc) { DisplayWeb $spWeb ($args[1] + 2) $spWeb.Dispose() } #END CONTAINED SITES ENTITY Write-Output "$spaceBuffer$BUFFER_CHARS" } #END SITE COLLECTION ENTITY Write-Output "$spaceBuffer" #CLEANUP SITE COLLECTION VARIABLE $sc.Dispose() } function DisplayWeb #-web -depth -parentWeb { [string]$spaceBuffer = DetermineSpaceBuffer $args[1] #START SITE ENTITY $outputText = "$spaceBuffer" + ($args[0].URL) Write-Output $outputText if($args[0].HasUniquePerm -eq "True") { DetermineUserAccess $args[0] ($args[1] + 1) } else { Write-Output "$spaceBuffer<!--Inherits from parent>-->" } if($args[0].Webs -ne $Null) { #START CONTAINED SUBSITES ENTITY Write-Output "$spaceBuffer$BUFFER_CHARS" #RECURSIVELY SEARCH SUBWEBS foreach ($spSubWeb in $args[0].Webs) { DisplayWeb $spSubWeb ($args[1] + 2) $spSubWeb.Dispose() } #END CONTAINED SUBSITES ENTITY Write-Output "$spaceBuffer$BUFFER_CHARS" } #END SITE ENTITY Write-Output "$spaceBuffer" } function DisplayMissingParametersMessage { #Write-Output "You are missing a parameter for 'Site URL'" $script:siteURL = Read-Host "Enter Site URL" } ############ # MAIN ############ #IF MISSING PARM FOR SITE URL, ASK FOR INPUT TO FILL if($args.length -eq 0) { DisplayMissingParametersMessage } $rootSite = New-Object Microsoft.SharePoint.SPSite($siteUrl) $spWebApp = $rootSite.WebApplication Write-Output "" #IF SEARCH SCOPE SPECIFIED FOR SITE, ONLY SEARCH SITE if($searchScope -eq "-site") { DisplaySiteCollection $rootSite 1 } #ELSE SEARCH ENTIRE WEB APP else { DisplayWebApplication $spWebApp 1 } Write-Output "" #CLEANUP $rootSite.Dispose()
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#478527Brian,Thanks for this excellent piece of work. I had to make very minor changes to the script to make a well formed XML structure. Changes- Removed spaces from all XML tags like <WebApplications>, <WebApplication>, <SiteCollections>, <SiteCollection>- Line # 214 : Write-Output “$spaceBuffer<!– Inherits from parent –>”Sameer Dhoothttp://sharemypoint.in
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#478549Sameer, Thanks for your additions to make well formed XML, glad you found the post helpful. Brian
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#518306I am fresher about MOSS 2007.I want to know -the users contained in all SP groups on Sharepoint site and Which users have elevated access.so is this script helpfull for me?and of yes then how i can execute this script in my MOSS env and check output.Please let me know.
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#518841Amey, For looping through all SP groups on a SharePoint site you can enumerate through the SPWeb.SiteGroups property. You can then list out all members of the group (whether user or AD group) using snippets from my above code. This code is also compatible with MOSS. To check the output run your script against a small site with just a few users in a group and compare against the listings on the actual SharePoint site settings UI. Let me know if you have further questions.
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#519484Brian,Thanks Brian.I can run this script, and get the users and there elevated access. really good script.
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#519531Amey, Glad you’re able to run it and found it useful. I usually demo this script for many of my PowerShell presentations to show people the power of combining PowerShell with other components like Active Directory and SharePoint all in one script.
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#533673Hi Brian,Thanks for the post. I have an issue which i thought your script may help to resolve.We have a Web App that contains many site collections & Sites none of which are anonymous access. The ‘NT AuthorityAuthorised Users’ group is added to all of the site collections somehow. This has the unfortunate effect of enabling the search to bring back results from other site collections. This is a really bad thing for us!I was hoping to adapt your script to pick out the ‘NT AuthorityAuthorised Users’ and remove it from every site collection but it doesn’t show up in the results..Any ideas?I know this probably isn’t fixing the root cause but I need a quick fix to buy me some timeThanks in Advance,Michael
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#534082Michael, I tested out your scenario by adding “NT AuthorityAuthenticated Users” to a site. I then ran the script and I got a result for that group. It appears as “<Group>NT AUTHORITYauthenticated users</Group>”. Are you sure that isn’t in your result set?
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#535603Great script! I removed the ToUpper calls to get the specific user parmeter to work for me, but once I figured that out, it was very helpful. Thanks!
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#539325Hi Brain / All,we are in middle to fade-out one legacy NT4.0 domain and during decommission we found that many permissions inside sharepoint are tied with this legacy domain.Since i am not a programing guy, i would like to check with you that is your script can translate the permissions from domain-A to domain-B?Thanks in Advance,
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#539720Kushneev,As far as I know, when you run this script you will need to be logged in as a user who has access to the target domain. If you have configured a trust between the two domains it may be possible for to enumerate accounts from both domains.In terms of translating permissions, do you mean transfering permissions from domainAuser to domainBuser? I believe the “STSADM -o migrateuser” command may be of use to you. Try the below link for more information. Let me know if you have additional questions relating to that.STSADM –o Migrateuser command http://technet.microsoft.com/en-us/library/cc288467(office.12).aspx
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#562755A truly amazing scriptI had built a less comprehensive one for a client to allow them to scan sites and remove full control permissions from each of the owner groups. One thing I wanted to mention is with the aid of the PowerGui object tree. I picked out my web groups with”SPWeb.get_Groups().Web.Groups”Thereafter I can pick out members or owning group. I think your approach is better tho.
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#562802Daniel, Thanks for the tip. From the looks of your snippet that looks redundant getting the web, then groups, then web and groups again. I’m not familiar enough with PowerGUI though. Been meaning to take a look at it more. Glad you found my script useful.
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#579168Hi Great script – thanks!When i run it using .DisplaySPWebApp6.ps1 http://server >userpermissions.txti receive an output file and following errors:The ‘-ne’ operator failed: Exception has been thrown by the target of an invocation..At C:TempSP_Display-WebApp6.ps1:218 char:23+ if($args[0].Webs -ne <<<< $Null) + CategoryInfo : InvalidOperation: (:) [], RuntimeException + FullyQualifiedErrorId : OperatorFailedThe following exception was thrown when trying to enumerate the collection: “<nativehr>0x80070005</nativehr><nativestack></nativestack>”.At C:TempSP_Display-WebApp6.ps1:77 char:24+ if($args[0].Users -ne <<<< $Null) + CategoryInfo : NotSpecified: (:) [], ExtendedTypeSystemExceptio n + FullyQualifiedErrorId : ExceptionInGetEnumeratorPlease can you advise on how to overcome this?
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#579231Tomas, I’m not sure why you are receiving an exception on the “-ne” operator. Do you receive an exception when using that operator by itself in a simple command? Also just to make sure, you are running the command against a valid URL in your farm, correct? And the account you are running the script with has access to that URL?
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#581399I have the same error (in French 🙂 )L’opérateur « -ne » a échoué : Une exception a été levée par la cible d’un appel..Au niveau de C:UsersSP_FarmDesktopDisplaySPWebApp.ps1 : 216 Caractère : 25+ if($args[0].Webs -ne <<<< $Null) + CategoryInfo : InvalidOperation: (:) [], RuntimeException + FullyQualifiedErrorId : OperatorFailedLine 216 corresponds to:if($args[0].Webs -ne $Null) Interestingly that error does not appear for all sites.Must be site-specific then..
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#582778Hi BrianGreat script!A question: Would it be possible to disable unnecessary functions. We just wanna run function 6: “Search for a specific user’s permissions on a SharePoint site”, where we provide WebApp URL and a user name as parameters.Thanks!
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#583470Manuel, Thanks, glad you like the script. Yes it is possible to comment out calls to unnecessary functions. Feel free to modify the script as you see fit. I don’t currently have time to help you out, but let me know if you have any questions or issues in modifying it.
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#615779this script is awesome just what i was looking forThanks
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#621199Didnt know it existed as Id given up (a few weeks ago) actually lookin for the script. Its just what the doc ordered Thanks for the effort (& results in this case :)Best regards, Sheila KFounderWork At Home Moms Network
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#623102Hello,I am running .SP_Display-SiteCollectionAdmins1.ps1 http://teams.ad/desktop >> desktop.txt but the file remains empty and the display on the screen is showing all sites and sub-sites but “site – ” no account, no group are listed ???What did I mis?Thnaks,DOm
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#623104Hello,By comparison the command .SP_Display-WebApp6.ps1 http://teams.ad >> desktop.txt is filling the file with data with the hierarchy which is really excellentThnaks,Dom
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#623107Hello,the enumeration of the sites and users associated with is excellent, anyway to add the rights, privileges they have on the site..?Thanks,Dom
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#623462Great script! By any chance, is there a corresponding script to create the groups and members enumerated by this script? We are in a situation of progressing through system integration testing, user acceptance testing, etc, and we are regularly recreating site collections to have a fresh test bed. It would be great to have a script to reapply the permissions each time we create a new site collection.
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#623684Dominique,This script is a couple years old and I haven’t updated since I wrote it. You could add additional code to check which permissions they have, but I would likely rewrite this at this point to be more concise and use better scripting practices. For now I don’t have the spare time to undertake that effort. You may want to check CodePlex for a solution or look to a 3rd party application.Dean,No I do not have any script to create the corresponding groups enumerated. You may want to look into SP Copy Group (http://spcopygroup.codeplex.com/) or doing a site collection backup (after configuring the groups and permissions you want) and restore. Hope that helps.
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#624828Nice job on the script. I get a complete listing of sites with who has access. I’d like to also see what permission is assigned to each group or user. Can this detail be pulled too? How? Thanks!
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#625105HarleyRider,Yes it is possible to get which permissions the user / group has been assigned. Unfortunately this script was targetted to SharePoint 2007. Since that time the SPWeb.Permissions property has been deprecated. You would be better to grab the SPWeb.RoleDefinitions instead to identify permissions assigned. I have not updated this script in years but if I do I’ll post a link on here.
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#630341Great script! I’m curious if there is a way to get the AD groups that show with a SID only to do a translate into the Security Principal Name instead?We have a resource forest and it shows the sid for groups in the alternate forest.
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#630463Chris, I’ve never tried this script with a different AD forest, only a single domain. It might be that the lookup that I’m doing via ADSI is limited to a single forest since. On a semi-related note I am preparing to publish the 2010 / 2013 version of this script on my blog with some improvements and bug fixes. If you’d like to test that out send me an email (contact link up top) and see if we can work out the SID only issue.
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#631274It worked the first time that I tried. It is great code.Thanks,Alan
LikeLike
Originally posted on: https://briantjackett.com/archive/2009/04/24/the-power-of-powershell-and-sharepoint-enumerating-sharepoint-permissions-and.aspx#637671How would I get the AD Group names if Claims Authentication is in use. All I get is a GUID which is impossible for our users to decipher.
LikeLike