Home Contact

The Frog Pond of Technology

Ripples of Knowledge for SharePoint and Other .Net Technologies

News

 Subscribe to this blog


Upcoming speaker at:

About Me

Name:
Brian T. Jackett
Location:
Columbus, OH
Company:
Sogeti USA

Find me on...

Speaker Rate

Twitter












Archives

Post Categories

Syndication:

The Power of PowerShell and SharePoint: Enumerating SharePoint Permissions and Active Directory

<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.

  1. Recursively crawl a site or entire web application within SharePoint
  2. Enumerate permissions assigned to a SharePoint site
  3. Detail the SharePoint users assigned to a SharePoint group
  4. Determine if an Active Directory group is a member of a SharePoint group
  5. Detail the Active Directory users who are members of an Active Directory group
  6. 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

   1:  ###########################################################
   2:  #DisplaySPWebApp6.ps1 -URL <string> -searchScope <string> -userToFind <string>
   3:  #
   4:  #Author: Brian Jackett
   5:  #Last Modified Date: Jan. 12, 2009
   6:  #
   7:  #Supply Traverse the entire web app site by site to display
   8:  # hierarchy and users with permissions to site.
   9:  ###########################################################
  10:   
  11:   
  12:  #DECLARE VARIABLES
  13:  [string]$siteUrl = $args[0]
  14:  [string]$searchScope = $args[1]
  15:  [string]$userToFind = $args[2]
  16:   
  17:  #DECLARE CONSTANTS
  18:  $BUFFER_CHARS = "   "
  19:   
  20:  function DetermineSpaceBuffer #-iterations <int>
  21:  {
  22:    [string]$spaceBuffer = ""
  23:    for($i = 0; $i -lt $args[0]; $i++)
  24:    {$spaceBuffer += $BUFFER_CHARS}
  25:   
  26:    return $spaceBuffer
  27:  }
  28:   
  29:  #DECLARE FUNCTIONS
  30:  function DrillDownADGroup #-group <[AD]DirectoryEntry> -depth <int>
  31:  {
  32:    [string]$spaceBuffer = DetermineSpaceBuffer $args[1]
  33:    $domain = $args[0].Name.substring(0, $args[0].Name.IndexOf("\") + 1)
  34:    $groupName = $args[0].Name.Remove(0, $args[0].Name.IndexOf("\") + 1)
  35:   
  36:    #BEGIN - CODE ADAPTED FROM SCRIPT CENTER SAMPLE CODE REPOSITORY
  37:    #http://www.microsoft.com/technet/scriptcenter/scripts/powershell/search/users/srch106.mspx
  38:   
  39:    #GET AD GROUP FROM DIRECTORY SERVICES SEARCH
  40:    $strFilter = "(&(objectCategory=Group)(name="+($groupName)+"))"
  41:    $objDomain = New-Object System.DirectoryServices.DirectoryEntry
  42:    $objSearcher = New-Object System.DirectoryServices.DirectorySearcher
  43:    $objSearcher.SearchRoot = $objDomain
  44:    $objSearcher.Filter = $strFilter
  45:   
  46:    #
  47:    $colProplist = ("name","member")
  48:    foreach ($i in $colPropList)
  49:    {
  50:      $catcher = $objSearcher.PropertiesToLoad.Add($i)
  51:    }
  52:    $colResults = $objSearcher.FindAll()
  53:   
  54:    #END - CODE ADAPTED FROM SCRIPT CENTER SAMPLE CODE REPOSITORY
  55:   
  56:   
  57:    foreach ($objResult in $colResults)
  58:    {
  59:      foreach ($member in $objResult.Properties.member)
  60:      {
  61:        $indMember = [adsi] "LDAP://$member"
  62:        
  63:        #ATTEMPT TO GET AD OBJECT TYPE FOR USER, NOT WORKING RIGHT NOW
  64:        #$user = $indMember.PSBase
  65:        #$user.Properties
  66:   
  67:        $fullUserName = $domain + ($indMember.Name)
  68:        DisplayADEntry $fullUserName ($args[1])
  69:      }
  70:    }
  71:  }
  72:   
  73:  function DisplaySPGroupMembers #-group <SPGroup> -depth <int>
  74:  {
  75:    [string]$spaceBuffer = DetermineSpaceBuffer $args[1]
  76:   
  77:    if($args[0].Users -ne $Null)
  78:    {
  79:      #START SHAREPOINT USERS ENTITY
  80:      Write-Output $spaceBuffer"<SPUsers>"
  81:    
  82:      foreach($user in $args[0].Users)
  83:      {
  84:        DisplayADEntry $user ($args[1] + 1)
  85:      }
  86:   
  87:      #END SHAREPOINT USERS ENTITY
  88:      Write-Output $spaceBuffer"</SPUsers>"
  89:    }
  90:  }
  91:   
  92:  function DisplayADEntry #-user/group <SPUser> -depth <int>
  93:  {
  94:    #FILTER RESULTS IF LOOKING FOR SPECIFIC USER
  95:    if($args[0].IsDomainGroup -eq "True")
  96:    {
  97:      $outputText = "$spaceBuffer$BUFFER_CHARS<Group>" + ($args[0])
  98:      Write-Output $outputText
  99:      DrillDownADGroup $args[0] ($args[1])
 100:      $outputText = "$spaceBuffer$BUFFER_CHARS</Group>" 
 101:      Write-Output $outputText
 102:    }
 103:    else
 104:    {
 105:      #USER FOUND AS A CHILD OF AN EMBEDDED AD GROUP
 106:      if(($userToFind -ne "" -and ($userToFind.ToUpper() -eq $args[0].LoginName.ToUpper() -or $userToFind.ToUpper() -eq $args[0].ToUpper())) -or $userToFind -eq "")
 107:      {
 108:        $outputText = "$spaceBuffer$BUFFER_CHARS<User>" + ($args[0]) + "</User>"
 109:        Write-Output $outputText
 110:      }
 111:    }
 112:  }
 113:   
 114:  function DetermineUserAccess #-web <SPWeb> -depth <int>
 115:  {
 116:    [string]$spaceBuffer = DetermineSpaceBuffer $args[1]
 117:   
 118:    #START SHAREPOINT GROUPS ENTITY
 119:    Write-Output "$spaceBuffer<SPGroups>"
 120:   
 121:    foreach($perm in $args[0].Permissions)
 122:    {
 123:      #CHECK IF MEMBER IS AN ACTIVE DIRECTORY ENTRY OR SHAREPOINT GROUP
 124:      if($perm.XML.Contains('MemberIsUser="True"') -eq "True")
 125:      {
 126:        DisplayADEntry $perm.Member ($args[1] + 1)
 127:      }
 128:      #IS A SHAREPOINT GROUP
 129:      else
 130:      {
 131:        $outputText = "$spaceBuffer$BUFFER_CHARS<SPGroup>" + ($perm.Member)
 132:        Write-Output $outputText
 133:        DisplaySPGroupMembers $perm.Member ($args[1] + 2)
 134:        Write-Output "$spaceBuffer$BUFFER_CHARS</SPGroup>"
 135:      }
 136:    }
 137:   
 138:    #END SHAREPOINT GROUPS ENTITY
 139:    Write-Output "$spaceBuffer</SPGroups>"
 140:  }
 141:   
 142:  function DisplayWebApplication #-webApp <SPWebApplication>
 143:  {
 144:    [string]$spaceBuffer = DetermineSpaceBuffer $args[1]
 145:   
 146:    #START WEB APPLICATION ENTITY
 147:    $outputText = "$spaceBuffer<Web Application>" + ($args[0].Name)
 148:    Write-Output $outputText
 149:   
 150:    if($args[0].Sites -ne $Null)
 151:    {
 152:      #START CONTAINED SITE COLLECTIONS ENTITY
 153:      Write-Output "$spaceBuffer$BUFFER_CHARS<Site Collections>"
 154:   
 155:      foreach($spSiteColl in $args[0].Sites)
 156:      {
 157:        DisplaySiteCollection $spSiteColl ($args[1] + 2)
 158:        $spSiteColl.Dispose()
 159:      }
 160:   
 161:      #END CONTAINED SITE COLLECTIONS ENTITY
 162:      Write-Output "$spaceBuffer$BUFFER_CHARS</SiteCollections>"
 163:    }
 164:   
 165:    #END WEB APPLICATION ENTITY
 166:    "$spaceBuffer</Web Application>"
 167:  }
 168:   
 169:  function DisplaySiteCollection #-siteColl <SPSiteCollection> -depth <int>
 170:  {
 171:    [string]$spaceBuffer = DetermineSpaceBuffer $args[1]
 172:    $sc = $args[0].OpenWeb()
 173:   
 174:    #START SITE COLLECTION ENTITY
 175:    $outputText = "$spaceBuffer<Site Collection>" + ($sc.URL)
 176:    Write-Output $outputText
 177:   
 178:    if($sc -ne $Null)
 179:    {
 180:      #START CONTAINED SITES ENTITY
 181:      Write-Output "$spaceBuffer$BUFFER_CHARS<Sites>"
 182:   
 183:      foreach ($spWeb in $sc)
 184:      {
 185:        DisplayWeb $spWeb ($args[1] + 2)
 186:        $spWeb.Dispose()
 187:      }
 188:   
 189:      #END CONTAINED SITES ENTITY
 190:      Write-Output "$spaceBuffer$BUFFER_CHARS</Sites>"
 191:    }
 192:   
 193:    #END SITE COLLECTION ENTITY
 194:    Write-Output "$spaceBuffer</Site Collection>"
 195:   
 196:    #CLEANUP SITE COLLECTION VARIABLE
 197:    $sc.Dispose()
 198:  }
 199:   
 200:  function DisplayWeb #-web <SPWeb> -depth <int> -parentWeb <SPWeb>
 201:  {
 202:    [string]$spaceBuffer = DetermineSpaceBuffer $args[1]
 203:   
 204:    #START SITE ENTITY
 205:    $outputText = "$spaceBuffer<Site>" + ($args[0].URL)
 206:    Write-Output $outputText
 207:    
 208:    if($args[0].HasUniquePerm -eq "True")
 209:    {
 210:      DetermineUserAccess $args[0] ($args[1] + 1)
 211:    }
 212:    else
 213:    {
 214:      Write-Output "$spaceBuffer<!--Inherits from parent>"
 215:    }
 216:   
 217:   
 218:    if($args[0].Webs -ne $Null)
 219:    {
 220:      #START CONTAINED SUBSITES ENTITY
 221:      Write-Output "$spaceBuffer$BUFFER_CHARS<Subsites>"
 222:   
 223:      #RECURSIVELY SEARCH SUBWEBS
 224:      foreach ($spSubWeb in $args[0].Webs)
 225:      {
 226:        DisplayWeb $spSubWeb ($args[1] + 2)
 227:        $spSubWeb.Dispose()
 228:      }
 229:      #END CONTAINED SUBSITES ENTITY
 230:      Write-Output "$spaceBuffer$BUFFER_CHARS</Subsites>"
 231:    }
 232:   
 233:    #END SITE ENTITY
 234:    Write-Output "$spaceBuffer</Site>"
 235:  }
 236:   
 237:  function DisplayMissingParametersMessage
 238:  {
 239:    #Write-Output "You are missing a parameter for 'Site URL'"
 240:    $script:siteURL = Read-Host "Enter Site URL"
 241:  }
 242:   
 243:  ############
 244:  # MAIN
 245:  ############
 246:   
 247:  #IF MISSING PARM FOR SITE URL, ASK FOR INPUT TO FILL
 248:  if($args.length -eq 0)
 249:  {
 250:    DisplayMissingParametersMessage
 251:  }
 252:   
 253:  $rootSite = New-Object Microsoft.SharePoint.SPSite($siteUrl)
 254:  $spWebApp = $rootSite.WebApplication
 255:   
 256:   
 257:  Write-Output "<Web Applications>"
 258:   
 259:  #IF SEARCH SCOPE SPECIFIED FOR SITE, ONLY SEARCH SITE
 260:  if($searchScope -eq "-site")
 261:  {
 262:    DisplaySiteCollection $rootSite 1
 263:  }
 264:  #ELSE SEARCH ENTIRE WEB APP
 265:  else
 266:  {
 267:    DisplayWebApplication $spWebApp 1
 268:  }
 269:  Write-Output "</Web Applications>"
 270:   
 271:   
 272:  #CLEANUP
 273:  $rootSite.Dispose()

Feedback

# re: The Power of PowerShell and SharePoint: Enumerating SharePoint Permissions and Active Directory

Brian,
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 Dhoot
http://sharemypoint.in
6/28/2009 4:03 AM | Sameer Dhoot

# re: The Power of PowerShell and SharePoint: Enumerating SharePoint Permissions and Active Directory

Sameer,
Thanks for your additions to make well formed XML, glad you found the post helpful.

Brian 6/28/2009 1:29 PM | Brian

# re: The Power of PowerShell and SharePoint: Enumerating SharePoint Permissions and Active Directory

I 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. 5/7/2010 1:25 AM | Amey

# re: The Power of PowerShell and SharePoint: Enumerating SharePoint Permissions and Active Directory

Amey,

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. 5/10/2010 7:58 PM | Brian Jackett

# re: The Power of PowerShell and SharePoint: Enumerating SharePoint Permissions and Active Directory

Brian,

Thanks Brian.
I can run this script, and get the users and there elevated access. really good script. 5/14/2010 1:55 AM | Amey

# re: The Power of PowerShell and SharePoint: Enumerating SharePoint Permissions and Active Directory

Amey,

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. 5/14/2010 7:09 AM | Brian Jackett

Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification: