SharePoint Group Management

Managing SharePoint Groups in PowerShell

SharePoint Groups are a great mechanism for managing user permissions, however they exist within a single site collection. What if you have hundreds of site collections? We can easily script a range of common operations.

I prefer to use a CSV fed approach to manage groups and users. I create a CSV with the name of the group, and the users, which I list in pipe separated format (commas are already being used for the CSV). To read in a CSV use:

Import-Csv "L:PowerShellAD and SP group mapping.csv"

Let’s get the Site, Root Web, as well as an SPUser for the group owner, and get the groups object:

$Site = New-Object Microsoft.SharePoint.SPSite($SiteName)
write-host $site.Url
$rootWeb = $site.RootWeb;
$Owner = $rootWeb.EnsureUser($OwnerName)
$Groups = $rootWeb.SiteGroups;

Here’s how to add a Group:

$Groups.Add($SPGroupName, $Owner, $web.Site.Owner, “SharePoint Group to hold AD group for Members")

Here’s how to give the group Read access, for example:

$GroupToAddRoleTo = $Groups[$SPGroupName]
if ($GroupToAddRoleTo) #if group exists
{
$MyAcctassignment = New-Object Microsoft.SharePoint.SPRoleAssignment($GroupToAddRoleTo)
$MyAcctrole = $RootWeb.RoleDefinitions["Read"]
$MyAcctassignment.RoleDefinitionBindings.Add($MyAcctrole)
$RootWeb.RoleAssignments.Add($MyAcctassignment)
}

Here’s how to add a Member to a Group:

$UserObj = $rootWeb.EnsureUser($userName);
if ($UserObj) #if it exists
{
$GroupToAddTo.addUser($UserObj)  
}

Note that a duplicate addition of a member is a null-op, throwing no errors.

Here’s how to remove a member:

$UserObj = $rootWeb.EnsureUser($userName);
if ($UserObj)
{
$GroupToAddTo.RemoveUser($UserObj)  
}

Here’s how to remove all the members from a given group. This wipes the users from the whole site collection, so use this approach with care and consideration:

$user1 = $RootWeb.EnsureUser($MyUser)
try
{
$RootWeb.SiteUsers.Remove($MyUser)
$RootWeb.update()
}

Here’s the full script, with flags to setting the specific actions described above:

Add-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue
# uses feedfile to load and create set of SharePoint Groups.
$mylogfile="L:PowerShellongoinglogfile.txt"
$ADMap= Import-Csv "L:PowerShellAD and SP group mapping.csv"
$OwnerName = "DOMAIN\sp2013farm"
$AddGroups = $false;
$AddMembers = $false;  # optionally populates those groups, Comma separated list
$GrantGroupsRead = $true; #grants read at top rootweb level
$RemoveMembers = $false; # optionally  removes Comma separated list of users from the associated group
$WipeMembers = $false;	# wipes the groups clean		
$WipeUsersOutOfSite = $false;  #The Nuclear option. Useful to eliminate AD groups used directly as groups
#we do not need a hashtable for this work, but let's load it for extensibility
$MyMap=@{}  #load CSV contents into HashTable
for ($i=0; $i -lt $AD.Count; $i++)
{
$MyMap[$ADMap[$i].SharePointGroup] = $ADMap[$i].ADGroup;
}
# Script changes the letter heading for each site collection
$envrun="Dev"			# selects environment to run in
if ($envrun -eq "Dev")
{
$siteUrl = "h ttp://DevServer/sites/"
$mylogfile="L:PowerShellongoinglogfile.txt"
$LoopString = "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z"
$LoopStringArr = $LoopString.Split(“,”)
}
elseif ($envrun -eq "Prod")
{
$siteUrl = "ht tp://sharepoint/sites/"
$mylogfile="L:PowerShellongoinglogfile.txt"
$LoopString = "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z"
$LoopStringArr = $LoopString.Split(“,”)
}
else
{
Write-Host "ENVIRONMENT SETTING NOT VALID: script terminating..."
$siteUrl =  $null;
return;
}
Write-Host "script starting" 
$myheader = "STARTING: $(get-date)"
foreach ($letter in $LoopStringArr)
{
$SiteName=$siteurl+$letter
$Site = New-Object Microsoft.SharePoint.SPSite($SiteName)
write-host $site.Url
$rootWeb = $site.RootWeb;
$Owner = $rootWeb.EnsureUser($OwnerName)
$Groups = $rootWeb.SiteGroups;
for ($ADi = 0; $ADi -lt $ADMap.count; $ADi++)
{
$SPGroupName = $ADMap[$ADi].SharePointGroup;
if ($AddGroups)
{
if (!$Groups[$SPGroupName]) #no exist, so create
{
try
{
$Groups.Add($SPGroupName, $Owner, $web.Site.Owner, “SharePoint Group to hold AD group members")
}
catch
{
Write-Host -ForegroundColor DarkRed "Ouch, could not create $($SPgroupName)"
}
}
else
{
Write-Host -ForegroundColor DarkGreen "Already exists: $($SPgroupName)"
}
} #endif Add Groups
if ($GrantGroupsRead)
{
$GroupToAddRoleTo = $Groups[$SPGroupName]
if ($GroupToAddRoleTo) #if group exists
{
$MyAcctassignment = New-Object Microsoft.SharePoint.SPRoleAssignment($GroupToAddRoleTo)
$MyAcctrole = $RootWeb.RoleDefinitions["Read"]
$MyAcctassignment.RoleDefinitionBindings.Add($MyAcctrole)
$RootWeb.RoleAssignments.Add($MyAcctassignment)
} #if the group exists in the first place
} #ActionFlagTrue
if ($AddMembers)
{
$GroupToAddTo = $Groups[$SPGroupName]
if ($GroupToAddTo) #if group exists
{
$usersToAdd = $ADMap[$ADi].ADGroup;
if ($usersToAdd.length -gt 0) #if no users to add, skip
{
$usersToAddArr = $usersToAdd.split("|")
foreach ($userName in $usersToAddArr)
{
try
{
$UserObj = $rootWeb.EnsureUser($userName);
if ($UserObj)
{
$GroupToAddTo.addUser($UserObj)  #dup adds are a null-op, throwing no errors
}
}
catch
{
Write-Host -ForegroundColor DarkRed "cannot add user ($($userName) to $($GroupToAddTo)"
}
}
} #users to add
} #if the group exists in the first place
} #ActionFlagTrue
if ($RemoveMembers)
{
$GroupToAddTo = $Groups[$SPGroupName]
if ($GroupToAddTo) #if group exists
{
$usersToAdd = $ADMap[$ADi].SharePointGroup;
if ($usersToAdd.length -gt 0) #if no users to add, skip
{
$usersToAddArr = $usersToAdd.split("|")
foreach ($userName in $usersToAddArr)
{
try
{
$UserObj = $rootWeb.EnsureUser($userName);
if ($UserObj)
{
$GroupToAddTo.RemoveUser($UserObj)  #dup adds are a null-op, throwing no errors
}
}
catch
{
Write-Host -ForegroundColor DarkRed "cannot add user ($($userName) to $($GroupToAddTo)"
}
}
} #users to add
} #if the group exists in the first place
} #ActionFlagTrue
if ($WipeMembers)  #Nukes all users in the group
{
$GroupToAddTo = $Groups[$SPGroupName]
if ($GroupToAddTo) #if group exists
{
foreach ($userName in $GroupToAddTo.Users)
{
try
{
$UserObj = $rootWeb.EnsureUser($userName);
if ($UserObj)
{
$GroupToAddTo.RemoveUser($UserObj)  #dup adds are a null-op, throwing no errors
}
}
catch
{
Write-Host -ForegroundColor DarkRed "cannot remove user ($($userName) to $($GroupToAddTo)"
}
}
} #if the group exists in the first place
} #ActionFlagTrue
if ($WipeUsersOutOfSite)  #Nukes all users in the group
{
$usersToNuke = $ADMap[$ADi].ADGroup;
if ($usersToNuke.length -gt 0) #if no users to add, skip
{
$usersToNukeArr = $usersToNuke.split("|")
foreach ($MyUser in $usersToNukeArr)
{
try
{
try
{
$user1 = $RootWeb.EnsureUser($MyUser)
}
catch
{
Write-Host "x1: Failed to ensure user $($MyUser) in $($Site.url)"
}
try
{
$RootWeb.SiteUsers.Remove($MyUser)
$RootWeb.update()
}
catch
{
Write-Host "x2: Failed to remove $($MyUser) from all users in $($Site.url)"
}
}
catch
{
Write-Host "x4: other failure for $($MyUser) in $($Site.url)"
}
} #if user is not null
} #foreach user to nuke
} #ActionFlagTrue
}
$rootWeb.dispose()
$site.dispose()
} #foreach site

Checking for a specific permission for a specific user or group in SharePoint

While the UI allows one to easily check permissions for a given user, how can one do that iteratively?

Here’s the heart of the magic:

# first grab the user principal:
$user = $TargetWeb.Groups[$GroupToAdd];
# Now let's get the Role Assignments for that user on the folder:
$RA = $folder.RoleAssignments.GetAssignmentByPrincipal($user);
#Role bindings are useful
$RoleDefBindings = $RA.get_RoleDefinitionBindings();
#Now let's grab the Role Definition for Contribute permission in this SPWeb:
$roledef = $TargetWeb.RoleDefinitions["Contribute"];
Lastly we can check whether the role bindings for this user on this folder contains the Contribute Role Definition:
if ($RoleDefBindings.Contains($roledef)) {...}

Some useful routines first. Note I like to predefine a “Write” permission that allows creation and editing but not deletion:

function PermRole([string] $RoleChar)
{
switch ($RoleChar)
{
"R" {$res="Read"}
"C" {$res="Contribute"}
"W" {$res="Contribute wo delete"}
"D" {$res="Manage Hierarchy"}  #aka design, for setting permissions
default {$res=$null}
}
return $res;
}
# Routine for adding permission based on passing in a character for the role definition to be granted:
function AddPerm ([string] $RoleChar, [string] $RoleGroup)
{ #JPItem/f and TargetWeb are implied and not passed as parms for efficiency!
if ((!$RoleChar) -or (!$RoleGroup))
{
return; #race to be efficient on NullOp
}
$RoleValue=PermRole($RoleChar);
if (!$RoleValue) 
{
Write-Host -ForegroundColor -darkred "ok, expected Role, but got none, for $($RoleChar)"
return; 
}
try
{
#CONTROVERSIAL!
if ($RoleChar -eq "W")  #wipes out reads etc.
{
RemovePerm $RoleGroup
}
try
{
$user = $TargetWeb.ensureuser($RoleGroup)
}
catch  #if the above fails, user is likely not a user, but in fact a group, let's retry as group
{
$user = $TargetWeb.Groups[$RoleGroup]
}
$roledef = $TargetWeb.RoleDefinitions[$RoleValue]
$roleass = New-Object Microsoft.SharePoint.SPRoleAssignment($user)
$roleass.RoleDefinitionBindings.Add($roledef)
$f1.RoleAssignments.Add($roleass)  #This is SPFolder specific in this routine
}
catch
{
Write-Host -ForegroundColor DarkRed "ERR: Can't Assign $($RoleGroup)"
}
}

Let’s first establish the libraries to look at across all webs and site collections:

$libsArrStr="Library name 1|Library name 2"
$LibsArr=$libsArrStr.split("|")
$GroupToAdd = "Department Contributors"
$Site = "ht tp://SharePoint/sites/SiteOfInterest"
$TargetWeb=$web=get-spweb $Site;
Write-Host "==>working in $($web.url)"
for ($j=0; $j -lt $LibsArr.count; $j++)
{
$libStr=$LibsArr[$j];
$list=$web.Lists.TryGetList($libStr)
if ($list -eq $null)
{
Write-Host -ForegroundColor DarkRed "List not found"
}
else
{
for ($fi=0; $fi -lt $list.Folders.Count; $fi++)
{
$f1 = $list.Folders.get_Item($fi)
$f = $f1.folder;
write-host -f green "The Library $($listName) exists in the site $($web.url), about to set folder Perms"  
try
{
#the rule is if this field has data, make the user a Contributor
$f1.ResetRoleInheritance(); #badda-bing, security is inherited
$isWritable = ($f.item["TargetMetadata"] -ne $null);
if (!$isWritable)
{
# nul op, already inherited
}
else  #let's see whether to break perms, based on whether the group already has Contribute
{
#let's see if the user has Contributor rights already; if so, no need to break inheritence
$user = $TargetWeb.Groups[$GroupToAdd]
$RA = $f1.RoleAssignments.GetAssignmentByPrincipal($user)
$RoleDefBindings = $RA.get_RoleDefinitionBindings()
$roledef = $TargetWeb.RoleDefinitions["Contribute"]
if ($RoleDefBindings.Contains($roledef))  # user is already a Contributor, let's do nothing
{
}
else
{
$f1.BreakRoleInheritance($true);  #minimalist approach
addPerm	"C" 	$GroupToAdd								
}
}
}
catch
{
Write-Host problems setting perms
}
} #Folder processing for loop $fi
} # list found
} #for loop $j

Resetting a SharePoint Farm Passphrase

Resetting a SharePoint Farm Passphrase

If you don’t have the SharePoint farm passphrase, you can’t join a server to the farm. And there’s no way to get it back once you lose it. However it’s quite easy to reset it. First, let’s be aware fo the stiff requirements for a passphrase. You should ensure that the passphrase meets the following criteria:
Contains at least eight characters
Contains at least three of the following four character groups: ◦English uppercase characters (from A through Z)
+ English lowercase characters (from a through z)
+ Numerals (from 0 through 9)
+ Nonalphabetic characters (such as !, $, #, %)

$passphrase = ConvertTo-SecureString -String "P1ckAg00dPa$$w0rd" -asPlainText -Force
Set-SPPassPhrase -PassPhrase $passphrase -Confirm

The purpose of the passphrase is to prevent unauthorized servers from joining to a farm, and using their newfound access for malicious purposes.

What is less well known is that there is a job that runs that propagates the Passphrase amongst the SharePoint servers in the farm. It is only run when the passphrase is changed or when a server is added to the farm.
SPMasterPassphraseDeploymentJobDefinition. This Timer Job can run into trouble, so it is good to be aware of it for diagnosis and manual retry.

Granting SharePoint Shell Administration access

Granting SharePoint Shell Administration access via PowerShell

Enabling a PowerShell user can be as simple as granting local admin rights and the following command:

add-spshelladmin "DOMAIN\USER"

Sometimes though there remain Content DBs for which the user doesn’t gain PowerShell access. In this case, the following command pipes in the content DBs and forces the PowerShell access granted:

get-spcontentdatabase | add-spshelladmin "DOMAIN\USER"

There are service application databases as well, and these could be all handled with this single command:

get-spdatabase | add-spshelladmin "DOMAIN\USER"

In the end what is required is the user having Securityadmin server role access on the SQL instance and the db_owner role in a database.

The securityAdmin role is required so that the underlying service accounts get granted the appropriate permissions, say when mounting a content DB.

Also a user must be a member of the SharePoint_Shell_Access role on the configuration database and a member of the WSS_ADMIN_WPG local group on the server (actually best to do on each server in the farm).

SharePoint Group Management

Managing SharePoint Groups in PowerShell

SharePoint Groups are a great mechanism for managing user permissions, however they exist within a single site collection. What if you have hundreds of site collections? We can easily script a range of common operations.

I prefer to use a CSV fed approach to manage groups and users. I create a CSV with the name of the group, and the users, which I list in pipe separated format (commas are already being used for the CSV). To read in a CSV use:

Import-Csv "L:PowerShellAD and SP group mapping.csv"

Let’s get the Site, Root Web, as well as an SPUser for the group owner, and get the groups object:

$Site = New-Object Microsoft.SharePoint.SPSite($SiteName)
write-host $site.Url
$rootWeb = $site.RootWeb;
$Owner = $rootWeb.EnsureUser($OwnerName)
$Groups = $rootWeb.SiteGroups;

Here’s how to add a Group:

$Groups.Add($SPGroupName, $Owner, $web.Site.Owner, “SharePoint Group to hold AD group for Members")

Here’s how to give the group Read access, for example:

$GroupToAddRoleTo = $Groups[$SPGroupName]
if ($GroupToAddRoleTo) #if group exists
{
$MyAcctassignment = New-Object Microsoft.SharePoint.SPRoleAssignment($GroupToAddRoleTo)
$MyAcctrole = $RootWeb.RoleDefinitions["Read"]
$MyAcctassignment.RoleDefinitionBindings.Add($MyAcctrole)
$RootWeb.RoleAssignments.Add($MyAcctassignment)
}

Here’s how to add a Member to a Group:

$UserObj = $rootWeb.EnsureUser($userName);
if ($UserObj) #if it exists
{
$GroupToAddTo.addUser($UserObj)  
}

Note that a duplicate addition of a member is a null-op, throwing no errors.

Here’s how to remove a member:

$UserObj = $rootWeb.EnsureUser($userName);
if ($UserObj)
{
$GroupToAddTo.RemoveUser($UserObj)  
}

Here’s how to remove all the members from a given group. This wipes the users from the whole site collection, so use this approach with care and consideration:

$user1 = $RootWeb.EnsureUser($MyUser)
try
{
$RootWeb.SiteUsers.Remove($MyUser)
$RootWeb.update()
}

Here’s the full script, with flags to setting the specific actions described above:

Add-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue
# uses feedfile to load and create set of SharePoint Groups.
$mylogfile="L:PowerShellongoinglogfile.txt"
$ADMap= Import-Csv "L:PowerShellAD and SP group mapping.csv"
$OwnerName = "DOMAIN/sp2013farm"
$AddGroups = $false;
$AddMembers = $false;  # optionally populates those groups, Comma separated list
$GrantGroupsRead = $true; #grants read at top rootweb level
$RemoveMembers = $false; # optionally  removes Comma separated list of users from the associated group
$WipeMembers = $false;	# wipes the groups clean		
$WipeUsersOutOfSite = $false;  #The Nuclear option. Useful to eliminate AD groups used directly as groups
#we do not need a hashtable for this work, but let's load it for extensibility
$MyMap=@{}  #load CSV contents into HashTable
for ($i=0; $i -lt $AD.Count; $i++)
{
$MyMap[$ADMap[$i].SharePointGroup] = $ADMap[$i].ADGroup;
}
# Script changes the letter heading for each site collection
$envrun="Dev"			# selects environment to run in
if ($envrun -eq "Dev")
{
$siteUrl = "h ttp://DevServer/sites/"
$mylogfile="L:PowerShellongoinglogfile.txt"
$LoopString = "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z"
$LoopStringArr = $LoopString.Split(“,”)
}
elseif ($envrun -eq "Prod")
{
$siteUrl = "ht tp://sharepoint/sites/"
$mylogfile="L:PowerShellongoinglogfile.txt"
$LoopString = "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z"
$LoopStringArr = $LoopString.Split(“,”)
}
else
{
Write-Host "ENVIRONMENT SETTING NOT VALID: script terminating..."
$siteUrl =  $null;
return;
}
Write-Host "script starting" 
$myheader = "STARTING: $(get-date)"
foreach ($letter in $LoopStringArr)
{
$SiteName=$siteurl+$letter
$Site = New-Object Microsoft.SharePoint.SPSite($SiteName)
write-host $site.Url
$rootWeb = $site.RootWeb;
$Owner = $rootWeb.EnsureUser($OwnerName)
$Groups = $rootWeb.SiteGroups;
for ($ADi = 0; $ADi -lt $ADMap.count; $ADi++)
{
$SPGroupName = $ADMap[$ADi].SharePointGroup;
if ($AddGroups)
{
if (!$Groups[$SPGroupName]) #no exist, so create
{
try
{
$Groups.Add($SPGroupName, $Owner, $web.Site.Owner, “SharePoint Group to hold AD group members")
}
catch
{
Write-Host -ForegroundColor DarkRed "Ouch, could not create $($SPgroupName)"
}
}
else
{
Write-Host -ForegroundColor DarkGreen "Already exists: $($SPgroupName)"
}
} #endif Add Groups
if ($GrantGroupsRead)
{
$GroupToAddRoleTo = $Groups[$SPGroupName]
if ($GroupToAddRoleTo) #if group exists
{
$MyAcctassignment = New-Object Microsoft.SharePoint.SPRoleAssignment($GroupToAddRoleTo)
$MyAcctrole = $RootWeb.RoleDefinitions["Read"]
$MyAcctassignment.RoleDefinitionBindings.Add($MyAcctrole)
$RootWeb.RoleAssignments.Add($MyAcctassignment)
} #if the group exists in the first place
} #ActionFlagTrue
if ($AddMembers)
{
$GroupToAddTo = $Groups[$SPGroupName]
if ($GroupToAddTo) #if group exists
{
$usersToAdd = $ADMap[$ADi].ADGroup;
if ($usersToAdd.length -gt 0) #if no users to add, skip
{
$usersToAddArr = $usersToAdd.split("|")
foreach ($userName in $usersToAddArr)
{
try
{
$UserObj = $rootWeb.EnsureUser($userName);
if ($UserObj)
{
$GroupToAddTo.addUser($UserObj)  #dup adds are a null-op, throwing no errors
}
}
catch
{
Write-Host -ForegroundColor DarkRed "cannot add user ($($userName) to $($GroupToAddTo)"
}
}
} #users to add
} #if the group exists in the first place
} #ActionFlagTrue
if ($RemoveMembers)
{
$GroupToAddTo = $Groups[$SPGroupName]
if ($GroupToAddTo) #if group exists
{
$usersToAdd = $ADMap[$ADi].SharePointGroup;
if ($usersToAdd.length -gt 0) #if no users to add, skip
{
$usersToAddArr = $usersToAdd.split("|")
foreach ($userName in $usersToAddArr)
{
try
{
$UserObj = $rootWeb.EnsureUser($userName);
if ($UserObj)
{
$GroupToAddTo.RemoveUser($UserObj)  #dup adds are a null-op, throwing no errors
}
}
catch
{
Write-Host -ForegroundColor DarkRed "cannot add user ($($userName) to $($GroupToAddTo)"
}
}
} #users to add
} #if the group exists in the first place
} #ActionFlagTrue
if ($WipeMembers)  #Nukes all users in the group
{
$GroupToAddTo = $Groups[$SPGroupName]
if ($GroupToAddTo) #if group exists
{
foreach ($userName in $GroupToAddTo.Users)
{
try
{
$UserObj = $rootWeb.EnsureUser($userName);
if ($UserObj)
{
$GroupToAddTo.RemoveUser($UserObj)  #dup adds are a null-op, throwing no errors
}
}
catch
{
Write-Host -ForegroundColor DarkRed "cannot remove user ($($userName) to $($GroupToAddTo)"
}
}
} #if the group exists in the first place
} #ActionFlagTrue
if ($WipeUsersOutOfSite)  #Nukes all users in the group
{
$usersToNuke = $ADMap[$ADi].ADGroup;
if ($usersToNuke.length -gt 0) #if no users to add, skip
{
$usersToNukeArr = $usersToNuke.split("|")
foreach ($MyUser in $usersToNukeArr)
{
try
{
try
{
$user1 = $RootWeb.EnsureUser($MyUser)
}
catch
{
Write-Host "x1: Failed to ensure user $($MyUser) in $($Site.url)"
}
try
{
$RootWeb.SiteUsers.Remove($MyUser)
$RootWeb.update()
}
catch
{
Write-Host "x2: Failed to remove $($MyUser) from all users in $($Site.url)"
}
}
catch
{
Write-Host "x4: other failure for $($MyUser) in $($Site.url)"
}
} #if user is not null
} #foreach user to nuke
} #ActionFlagTrue
}
$rootWeb.dispose()
$site.dispose()
} #foreach site

SharePoint Full Farm Site Collection Admin report

Reporting on SharePoint Full Farm Site Collection Admins

Site Collection Administrators retain a great deal of power and capability within a SharePoint Site Collection. Wouldn’t it be nice to know are the Site Collection admins across an entire farm? This script will generate a report that is easily loaded and pivoted in Excel. First, let’s pick a column separator that’s not in use by any of the fields. This is a bit of a challenge, because with Claims based authentication, there’s quite a few characters in use that precludes them being a separator. Here’s the script:

$Sep="~"
Write-Host "WebApp$($sep)SiteCollection$($sep)UserName$($sep)UserLogin"
$spWebApps = get-spwebapplication 
foreach ($spWebApp in $spWebApps)
{
foreach($site in $spWebApp.Sites)
{
foreach($siteAdmin in $site.RootWeb.SiteAdministrators)
{
Write-Host "$($spWebApp.Name)$($sep)$($siteAdmin.ParentWeb.Url)$($sep)$($siteAdmin.DisplayName)$($sep),$($siteAdmin.UserLogin)"
}
$site.Dispose()
}
$spWebApp=$null; #this is not an IDisposable object
}

AD User group membership not propagating into site collections

AD User group membership propagation issue

In some rare instances, users may exist within a Site Collection that don’t receive their AD group membership updates.

I’ve traced this down to recreated AD users that have the same account name, yet a new SID. The solution is to wipe the user references from the site collection.

Be forewarned, any user permissions will be wiped as well. One more excellent reason to only use AD groups for assigning permissions in SharePoint!

You can see this internal list and even delete the user by adapting this URL:
http ://WebApp/ManagedPath/namedSiteCollection/_layouts/people.aspx?MembershipGroupId=0

Better to do it in PowerShell for speed, extensibility, consistency, and across many site collections. The trick comes down to a specific way to eliminate the user from the site collection:

$RootWeb.SiteUsers.Remove($MyUser)

Note trying $RootWeb.Users.Remove($MyUser) or $RootWeb.AllUsers.Remove($MyUser) will not work.

To finish it off, I prefer to re-add the user:

$RootWeb.EnsureUser($MyUser)

Here’s the full script, where I traverse through site collections in a Web App, filter them based on criteria (in this case the managed path), then carefully take the action on a list of users (one or more, comma separated), and output any failures along the way:

Start-SPAssignment –Global
$UsersToWipe = "DOMAINPoorBloke"
$UsersToWipeArray = $UsersToWipe.Split(“,”)
$siteUrl = "http ://sharepoint"  
Write-Host "script starting $(get-date)" 
$rootSite = New-Object Microsoft.SharePoint.SPSite($siteUrl)
$spWebApp = $rootSite.WebApplication 
foreach($site in $spWebApp.Sites)
{
if ($site.Url -notlike "$siteurl/SpecificPath/*") 
{
Write-Host "Fast Skipping $($site.Url)"
}
else
{ 
$rootWeb = $site.RootWeb;
foreach ($MyUser in $UsersToWipeArray)
{
try
{
try
{
$user1 = $RootWeb.EnsureUser($MyUser)
}
catch
{
Write-Host "x1: Failed to ensure user $($MyUser) in $($Site.url)"
}
try
{
$RootWeb.SiteUsers.Remove($MyUser)
$RootWeb.update()
}
catch
{
Write-Host "x2: Failed to remove $($MyUser) from all users in $($Site.url)"
}
try
{
$user1 = $RootWeb.EnsureUser($MyUser)
}
catch
{
Write-Host "x3: Failed to ensure user $($MyUser) in $($Site.url)"
}
}
catch
{
Write-Host "x4: other failure for $($MyUser) in $($Site.url)"
}
}
} #Site to process 
$site.Dispose();  
} #foreach Site
Write-Host "script finishing $(get-date)" 
Stop-SPAssignment –Global

Refining People-Picker

Refining The SharePoint People-Picker

In SharePoint there are several locations where a set of users is presented in what is known as the “People-Picker”. Examples include a “people” field in lists, and in assigning security.

One can manage the set of users and groups presented, however THe underlying mechanism is not well known in the SharePoint community. In short, it is the set of all users returned from AD (ActiveDirectory) plus the set of local users in the Site Collection being used.

In this article, I’ll provide guidance on how to adjust both the users returned from AD, as well as the users in the site collection.

AD Filtering

To return a subset of AD results, the following stsadm command is used:

stsadm -o setproperty -url http ://SharePoint/  -pn peoplepicker-searchadcustomfilter -pv ""

In this example, http ://SharePoint is your web application, and the “” is the LDAP query. This clears the AD filter, as the LDAP query is empty. Note there is no PowerShell equivalent in SP2010, and this applies to a Web Application and not individual site collections. To test the change, try editing the set of Site Collection Administrators or User Policy for the Web Application in Central Administration.

In the example below, an LDAP query is specified to select AD entries where users have a manager (which filters out all kinds of non-standard user accounts), and groups starting with “SharePoint_”.

stsadm -o setproperty -url http ://SharePoint  -pn peoplepicker-searchadcustomfilter -pv "(|(&(objectcategory=group)( sAMAccountName=domainsharepoint_*))(&(&(objectcategory=person)(objectclass=user))(manager=*)))"

Let’s check out the LDAP Query string above, with this bit of PowerShell:

$strFilter = "(|(&(objectcategory=group)( sAMAccountName=yourdomainsharepoint_*))(&(&(objectcategory=person)(objectclass=user))(manager=*)))"
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.PageSize = 1000
$objSearcher.Filter = $strFilter
$objSearcher.SearchScope = "Subtree"
$colProplist = "name"
foreach ($i in $colPropList){$objSearcher.PropertiesToLoad.Add($i)}
$colResults = $objSearcher.FindAll()
foreach ($objResult in $colResults)
{$objItem = $objResult.Properties; $objItem.name}

ColResults now has a nice array of results to work with.

While the above is all straightforward, the results in PeoplePicker could be less than perfect. If you test, the users returned may contain additional users, including those not returned by the LDAP Query.

It turns out, People Picker returns the superset of the LDAP Query results AND a local user list that is cached in a site collection. You can see this list by going to the Site Collection URL, plus this: “_catalogs/users/”. Note the default view does not allow you to delete items, but if you add a new view, you can add “Edit” as a column, and delete individual users one at a time.

The script below will delete all cached users in a Site Collection (except for Site Collection Administrators); but I don’t think you want to run it, as it will remove all user permissions as well!

$url = "http://YourSiteCollection"
$web = get-spweb $url
$list = $web.Lists["User Information List"]
$listItems = $list.Items
$listItemsTotal = $listItems.Count
for ($x=$listItemsTotal-1;$x -ge 0; $x–-)
{
Write-Host(“DELETED: ” + $listItems[$x].name)
remove-spuser $listItems[$x]["Account"] -web $url -confirm:$false
}
$web.dispose()

To get this right, we need to extract the LDAP query results into a hashtable, and loop through the cached user list and only remove entries that are not in the LDAP query.

Item Level permissions

Item Level permissions

SharePoint has a robust object model supporting security at each level of the farm.  Let’s take a quick tour of some relevant methods and properties around item level reporting.

All securable objects have a method named GetUserEffectivePermissionInfo which is defined in the base class SPSecurableObject. This method returns back an SPPermissionInfo object which we can use to inspect the role definition bindings and corresponding permission levels. SPSecurableObject is imple,eented at the SPWeb, SPList, and SPLIstItem class level, hence how we assign permissions if needed at the site level.

 We can loop through the SPRoleAssignments objects via the RoleAssignments property. This will give us information about how the user is given access to the resource. This returns the Member (the account or group), the RoleDefinitionBindings (permission level). This is an excellent place to start if you are looping through each item.

 Next can look at the RoleDefinitionBindings property which returns back a collection of SPRoleDefinition objects that tell us about the type of access granted.

 Other important properties for reporting security include:

  • HasUniqueRoleAssignments, or the method returing the same thing: get_HasUniqueRoleAssignments()
  • RoleDefinitionBindings: collection of SPRole Definition objects returned.
  • IsSiteAdmin : a property of the user, indicates if a user is a Site Collection Admin ,which includes explicit permissions to everything
  • SPListItem.FirstUniqueAncestorSecurableObject: Retrieves the first unique ancestor if it has unique role assignments otherwise returns the first parent object (folder, list, or Web site) that has unique role assignments.
  • SPItem.AllRolesForCurrentUser

For a more general view of Security permissions in SharePoint, please see this TechNet article.