A simple way to programmatically create SharePoint security groups
Posted
Monday, March 8, 2010 11:12 AM
by
CoreyRoth
When it comes to SharePoint deployments, I try to automate everything I can. I don’t like manual steps especially when it comes to setting up security. A common task when deploying any sites is setting up security in some manner. Today I am going to cover how to easily store definitions your SharePoint security groups in an XML file. We’ll use LINQ to XML to make reading the file a breeze, and then we’ll use the SharePoint object model to create the groups and add users (or AD groups). I’ve blogged on how to create a group before, but we’re going to take this a step further by giving you code that you can easily add to a feature receiver or console application. First let’s take a look at the XML file we’re going to use.
<?xml version="1.0" encoding="utf-8" ?>
<Groups>
<Group Name="My Custom Read Group" Owner="SHAREPOINT\GroupOwner" Description="Readonly Permission Group" PermissionLevel="Read">
<User Name="SHAREPOINT\TestUser1" />
<User Name="SHAREPOINT\TestGroup1" />
<User Name="SHAREPOINT\TestGroup2" />
</Group>
<Group Name="My Custom Contributors Group" Owner="SHAREPOINT\GroupOwner" Description="Contributors Permission Group" PermissionLevel="Contribute">
<User Name="SHAREPOINT\TestGroup3" />
</Group>
</Groups>
In this file, I am defining two SharePoint groups. One that will have read access and one that will have contribute access. I store the required information needed by the Add method on the SPGroupCollection object. I then have one or more User elements with the name of my Active Directory user or group. I tried to keep my XML schema pretty simple. You can customize it obviously how you want, you would just have to alter your LINQ queries.
Let’s take a look at the code we need to make this happen. I won’t go into as much detail of the object model since I went into it pretty well on my last post. We’ll just focus on how we use LINQ to XML to read the information we need and then have it create our groups. My method is called CreateGroups and it takes an SPWeb object and a string with the filename of the XML document.
private void CreateGroups(SPWeb currentSite, string groupsFilename)
{
// get the xml document from the feature folder
XDocument groupsXml = XDocument.Load(groupsFilename);
// create a new anoynmous type with the group data
var groups = from sharePointGroup in groupsXml.Root.Elements("Group")
select new
{
Name = sharePointGroup.Attribute("Name").Value,
Owner = sharePointGroup.Attributes("Owner").Any() ? sharePointGroup.Attribute("Owner").Value : null,
Description = sharePointGroup.Attributes("Description").Any() ? sharePointGroup.Attribute("Description").Value : string.Empty,
PermissionLevel = sharePointGroup.Attributes("PermissionLevel").Any() ? sharePointGroup.Attribute("PermissionLevel").Value : null,
Users = sharePointGroup.Elements("User").Any() ? sharePointGroup.Elements("User") : null
};
// iterate through the groups and create the groups
foreach (var sharePointGroup in groups)
{
// only create the group if it does not exist
if (!ContainsGroup(currentSite.SiteGroups, sharePointGroup.Name))
{
// add the owner to the web site users
currentSite.EnsureUser(sharePointGroup.Owner);
// add the group
currentSite.SiteGroups.Add(sharePointGroup.Name, currentSite.SiteUsers[sharePointGroup.Owner],
currentSite.SiteUsers[sharePointGroup.Owner], sharePointGroup.Description);
}
// add the users to the group
AddUsersToGroup(sharePointGroup.Name, sharePointGroup.Users, currentSite, sharePointGroup.PermissionLevel);
}
}
This seems like kind of a big method at first, but it’s really not that bad. To keep things simple, I haven’t included any exception handling code. We are really just querying the XML document, iterating through each group element inside of it, creating the groups, and then adding the users to the group. The first line of code just creates an XDocument object. We then construct a LINQ to XML query. What we want is to return data from each Group element in the document. The Add method doesn’t like nulls, so we check for them and use string.Empty if the value does not exist in the file. The one case where I don’t do this is for the Name of the group. If that is not present, I would rather the process throw an exception. As for the Users assigned to the group, I grab all of them and add them to our anonymous type like this.
Users = sharePointGroup.Elements("User").Any() ? sharePointGroup.Elements("User") : null
This gives us an IEnumerable<XElement> that we can pass to a method later to add each Active Directory user (or group) to the SharePoint group. Once we execute the query, we iterate through each group element. The first thing we have to do is make sure that the group does not exist. Of course there is no way to do that other than using the try/catch technique. I will usually wrap this in an extension method, but for today’s purpose, we’ll just call a method to check.
private bool ContainsGroup(SPGroupCollection groupCollection, string index)
{
try
{
SPGroup testGroup = groupCollection[index];
return true;
}
catch (SPException e)
{
return false;
}
}
Lame I know. I’m so happy there are ways to get around this in SharePoint 2010. Then this starts to look like code from the previous post. We call .EnsureUser to make sure the domain account of the group owner is registered with the site. We then just call the Add method with the Name, Owner, default user, and description. Again there is more info on the previous post about that method call. Assuming the group is created, we can then add the users to the group. We call a new method AddUsersToGroup which takes the groupName, the users element, an SPWeb, and the permission level.
The first thing we do is query the names of the Active Directory users (or groups). Here we are just grabbing it from the Name attribute of the User element. I probably could have condensed this query, but at least it’s easy to read. We then add each user (or group) from the User elements to the group. If you are curious about the empty parameters, take a look at the previous post. If you are going to run into an exception, it’s going to be here. If the group failed to be created or if the user does not exist (i.e.: you typed it in the XML file wrong), this line will throw an exception.
private void AddUsersToGroup(string groupName, IEnumerable<XElement> users, SPWeb currentSite)
{
// select the username from the xml document
var userList = from user in users
select new
{
Name = user.Attribute("Name").Value
};
// add the users to the sharepoint group
foreach (var user in userList)
{
currentSite.SiteGroups[groupName].AddUser(user.Name, string.Empty, user.Name, string.Empty);
}
}
Now, we’re almost done. The last thing we need to do is set the permission level on the group. This is where we specify whether the group has readonly, contribute, full control, etc access to the site. Be sure and get the name on the permission level right otherwise you will get an exception. I’ve also blogged about how to assign permission levels before. Today’s post is really just a great practical use of putting together the things I have posted on before.
private void SetRoleDefinitionBinding(string groupName, SPWeb currentSite, string permissionLevel)
{
// add the read role definition to the site group
SPRoleAssignment roleAssignment = new SPRoleAssignment(currentSite.SiteGroups[groupName]);
roleAssignment.RoleDefinitionBindings.Add(currentSite.RoleDefinitions[permissionLevel]);
currentSite.RoleAssignments.Add(roleAssignment);
currentSite.Update();
}
Effectively you create a new SPRoleAssignment by passing it a SPGroup object. You then add a binding using the existing RoleDefinitions on the site. You then add the assignment to the site and of course call .Update() so things get saved.
That’s really all there is to it. This is a great use of combining information from my previous posts into something that you can use everyday to set security on your sites. How you execute this code is up to you. I’ve used it in a feature receiver and in a console application before. Setting up security through the UI is very slow and painful. Once you create it on one server, there is no way to move it to another server and that’s not a lot of fun. This should help you with that and eliminate those nasty manual steps in your deployment process.