In this article I'll demonstrate how to set permissions on work item queries or query folders using the Team Foundation Server 2010 Client SDK.
The source code can be downloaded from the following link:
WitQueryPermissions.zip (35.73 kb)
I often run into situations where I need to set a bunch of permission on specific work item query folders and doing it from the Team Explorer UI too time consuming. I looked into scripting the task using the TFSSecurity.exe command line tool that comes with TFS 2010, but it needs the GUID of each query item, and getting that GUID isn't easy. See this item in the MSDN forums for details.
What I want is to specify the path to the query item, the name of the account, and the permission to be granted for the account on the query item.
The path to the query item looks something like:
"DemoAgile/Team Queries/Hello World"

Below I use the TFS SDK and write some code to do what I want.
Here's we needed to do:
- Connect to the TFS team project collection.
- Get the IdentityDescriptor for the named user or group.
- Get the QueryHierarchy (the root of the query folders)
- Get the QueryItem for the given work item query path.
- Set and save the permissions on the QueryItem.
Project References and Namespaces
Here's a list of the TFS SDK references added to the Visual Studio project.
Microsoft.TeamFoundation.Client
Microsoft.TeamFoundation.Common
Microsoft.TeamFoundation.WorkItemTracking.Client
Here's a list of the TFS SDK namespace used.
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Framework.Client;
using Microsoft.TeamFoundation.Framework.Common;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
Connect to the TFS team project collection.
Connecting to a TFS team project collection is easy.
string teamProjectCollectionUrl = "http://Win7Dev-8:8080/TFS/DefaultCollection";
var teamProjectCollection = new TfsTeamProjectCollection(new Uri(teamProjectCollectionUrl));
Get the IdentityDescriptor for the named user or group.
TFS uses IdentityDescriptor objects to reference security users and groups.
The code below shows how to get the IdentityDescriptor object associated with the identity with the given display name in the given project collection. The display name has the form::
[MyTeamProject]\SomeIdentityName
We use the display name rather than the account name because the same account name may be defined within multiple team projects.
IdentityDescriptor GetIdentityDescriptorForDisplayName(
TfsTeamProjectCollection teamProjectCollection,
string identityDisplayName)
{
IIdentityManagementService identityManagementService =
teamProjectCollection.GetService<IIdentityManagementService>();
TeamFoundationIdentity identity = identityManagementService.ReadIdentity(
IdentitySearchFactor.DisplayName,
identityDisplayName,
MembershipQuery.Direct,
ReadIdentityOptions.None);
IdentityDescriptor securityGroupDescriptor = identity.Descriptor;
return securityGroupDescriptor;
}
Get the QueryHierarchy (the root of the query folders)
The root of the work item query hierarchy is a QueryHierarchy object that has the same name as the containing team project. We need the QueryHierarchy object so that we can save the changes we make to the permissions.
The QueryHierarchy object is derived from QueryFolder and it typically contains only two items, both are QueryFolder objects. One named "My Queries" and one named "Team Queries".
The following code shows how to access the QueryHierarchy for a named team project within a team project collection.
QueryHierarchy GetQueryHierarchy(
TfsTeamProjectCollection teamProjectCollection,
string teamProjectName)
{
WorkItemStore workItemStore = teamProjectCollection.GetService<WorkItemStore>();
Project witProject = workItemStore.Projects[teamProjectName];
return witProject.QueryHierarchy;
}
Get the QueryItem for the given work item query path.
TFS does not provide a built-in way to get a QueryFolder object specified by a given query folder path string such as:
"MyTeamProject\Team Queries\Iteration 1"
The following code show how to get the QueryFolder object specified by a given query folder path string.
static QueryItem GetQueryItemAtPath(QueryHierarchy queryHierarchy, string queryItemPath)
{
string[] itemArray = queryItemPath.Split('/');
// We skip the root folder from the itemArray
// so that only sub items remain.
string[] relItemArray = itemArray.Skip(1).ToArray();
// The QueryHierarchy object is the root QueryFolder.
QueryItem queryItem = GetQueryItemRecursive(queryHierarchy, relItemArray);
return queryItem;
}
QueryItem GetQueryItemRecursive(QueryItem queryItem, string[] relItemArray)
{
QueryItem itemFound = null;
if (relItemArray.Length == 0)
{
itemFound = queryItem;
}
else
{
// We assume the queryItem is a folder
// and look inside it.
QueryFolder queryFolder = (QueryFolder)queryItem;
QueryItem subItem = queryFolder[relItemArray[0]];
// Recurse.
itemFound = GetQueryItemRecursive(subItem, relItemArray.Skip(1).ToArray());
}
return itemFound;
}
Set and save the permissions on the QueryItem.
Permissions are assigned to QueryItem objects via the item's access control list (ACL) accessible via the item's AccessControlList property.
AccessControlList acl = queryItem.AccessControlList;
You change permissions for a QueryItem by changing the properties of individual AccessControlEntry objects in the work item's ACL.
When the AccessControlList.SetPermissions(..) method is called a new AccessControlEntry object will be added to the ACL if there is not one for the given descriptor.
If you set both allow and deny to QueryItemPermissions.None then the AccessControlEntry will be automatically removed from the copy of the ACL on the server after it is saved.
To save the changed to the TFS server you need to invoke the Save() method on the containing QueryHierarchy object.
if (queryItem.IsDirty)
{
queryHierarchy.Save();
}
You can determine whether or not a QueryItem has changes that need to be save by inspecting its IsDirty property.
The IsDirty property is not propagated up to the root of the hierarchy so the QueryHierarchy object's IsDirty property will not be changed when IsDirty changes on a child item.
Here's the code that sets and saves the permissions.
void ChangeQueryItemPermissions(QueryHierarchy queryHierarchy,
QueryItem queryItem,
IdentityDescriptor identityDescriptor,
QueryItemPermissions allowPermissions,
QueryItemPermissions denyPermissions)
{
AccessControlList acl = queryItem.AccessControlList;
acl.SetPermissions(identityDescriptor,
(int)allowPermissions,
(int)denyPermissions,
merge: false);
if (queryItem.IsDirty)
{
queryHierarchy.Save();
}
}
Putting it all together
Here's code for the Main of a sample command line that puts all the pieces together.
static void Main(string[] args)
{
string teamProjectCollectionUrl = "http://MyTfsServer:8080/TFS/DefaultCollection";
string teamProjectName = "MyTeamProject";
string identityDisplayName = @"[MyTeamProject]\Test Group";
string queryItemPath = "MyTeamProject/Team Queries/Hello World";
QueryItemPermissions allowPermissions = QueryItemPermissions.Read
| QueryItemPermissions.Contribute;
QueryItemPermissions denyPermissions = QueryItemPermissions.None;
var teamProjectCollection = new TfsTeamProjectCollection(
new Uri(teamProjectCollectionUrl));
IdentityDescriptor identityDescriptor = GetIdentityDescriptorForDisplayName(
teamProjectCollection,
identityDisplayName);
QueryHierarchy queryHierarchy = GetQueryHierarchy(teamProjectCollection,
teamProjectName);
QueryItem queryItem = GetQueryItemAtPath(queryHierarchy, queryItemPath);
ChangeQueryItemPermissions(queryHierarchy,
queryItem,
identityDescriptor,
allowPermissions,
denyPermissions);
}
The source code can be downloaded from the following link:
WitQueryPermissions.zip (35.73 kb)