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.
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.
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):
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.
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()