TFS11 API: Query Teams and Team Members

The default team profile picture

When talking about application lifecycle management with Team Foundation Server, the introduction of teams is one of the biggest new features shipped with TFS 11 beta. If you’ve got a copy of TFS 11 beta installed somewhere (or perhaps you’re using it extensively), I recommend you fire up your browser and point it to the WebAccess site of your team project collection. In case you haven’t heard about the teams feature or haven’t had a chance to check them out, it’s time.

To put it simply, a TFS team is a group of people you can assign work items to. Each team has a product backlog to keep track of requirements and one or more iteration backlogs where tasks waiting to be implemented, or already implemented ones, are kept. You can have multiple teams working on a single team project, in which case you would generally use work areas (determined by the work item area path property) to divide workload between the teams.

Retrieving available teams

Alright, time to get the party started. To retrieve a list of all available teams on a specified team project, we are going to use the QueryTeams() method of the TfsTeamService class. I have already blogged about the TfsTeamService before, where I’ve used it to programmatically detect the server version. Here’s the code:

static void Main(string[] args)
{
	Uri _serverUri = new Uri("<server_uri>");
	string _teamProjectName = "<project_name>";
	
	TfsTeamProjectCollection collection = 
		TfsTeamProjectCollectionFactory.GetTeamProjectCollection(_serverUri);

	// Retrieve the project URI. Needed to enumerate teams.
	var css4 = collection.GetService<ICommonStructureService4>();
	ProjectInfo projectInfo = css4.GetProjectFromName(_teamProjectName);

	// Retrieve a list of all teams on the project.
	TfsTeamService teamService = collection.GetService<TfsTeamService>();
	var allTeams = teamService.QueryTeams(projectInfo.Uri);
}

Additionally, you will need to add a reference to two TFS 11 assemblies: Microsoft.TeamFoundation.Client.dll and Microsoft.TeamFoundation.Common.dll, in order for the project to build successfully.

We start off by connecting to the TFS server specified by it’s URI. Next, we use the GetService<T> method to retrieve an instance of the ICommonStructureService4 service. As you may know, much of the Team Foundation Server functionality available in the client object model is accessible by querying the server (ie. the TfsTeamProjectCollection object) for an instance of a specified service. Most commonly, we do this by querying for a specified interface, which the service implements. Additionally, every time Microsoft updates one of these services, they create a new interface which inherits from the previous service interface and suffix it’s name with a unique number. So, we can think of the ICommonStructureService4 service as the fourth revision of the common structure service (ICommonStructureService). This service can be used to retrieve information about the team projects available on the server, as well as the areas and iterations available on a specified project.

In the example above, we use the common structure service to retrieve a ProjectInfo object from a specified team project name, because we need it to retrieve the project URI. Then, using GetService<T> again, we retrieve an instance of the TfsTeamService class. The TfsTeamService can be used to create, update or query teams, as well as retrieve the default team for the project. Strangely enough, I couldn’t find a method that would delete a team, though. Did I miss it?

The team itself is represented using the TeamFoundationTeam class and is actually implemented around a TFS security group. The TeamFoundationTeam object itself doesn’t have much properties: it has a Name, a Description and an Identity, which is implemented as a TeamFoundationIdentity object. One of the most interesting properties of this object is the TeamFoundationId property (it’s just a GUID), which can be used to uniquely identify an identity (that is, a team or a team member) on the server. For example, this property is used to retrieve the teams or team members profile picture in WebAccess. More about this in an upcoming blog post, though. This is just a very simple snippet used to output some of the available information to the console:

foreach (TeamFoundationTeam team in allTeams)
{
	Console.WriteLine("Team name: {0}", team.Name);
	Console.WriteLine("Team ID: {0}", team.Identity.TeamFoundationId);
	Console.WriteLine("Description: {0}", team.Description);
}

The team object also supports a few methods that work with properties, like GetProperty(), GetProperties(), TryGetProperty() and SetProperty(), all of which seem to enable us to store custom data into a hashtable-like structure. I’ve tried to store my custom properties using these methods, but this functionality doesn’t seem to be implemented yet (or I’m using it wrong). There’s also an UpdateTeam() method on TfsTeamService, which does not seem to do anything (on my environment, at least).

Querying team members

What’s a team without team members, right? Well, we’re just getting there. To query a team for it’s members, use the GetMembers() method:

var members = team.GetMembers(collection, MembershipQuery.Direct);

The GetMembers() method returns an array of TeamFoundationIdentity objects. What’s a bit interesting with this method call is the final parameter, the MembershopQuery enum. This enumeration can have one of these possible values: None, Direct and Expanded.

Calling the method and passing in MembershipQuery.None always returns an empty array, as you may have imagined. Passing in the value of Direct you get, well, the direct members. What does this actually mean? Remember when I’ve said that the team is actually implemented as a wrapper around a TFS group? Well, groups can contain both other groups as well as users. For example, let’s say you have group1 which contains users user1 and user2. You also have group2, with users user3 and user4. If you add those two groups to the team and query the members using the code from the example, you’ll get two results, one for each of the contained groups. So, if you imagine the team as the root node of a tree structure, where the leaves are either other security groups or team members, you would only get the direct children of the root node.

And what do you get if you use the MembershipQuery.Expanded value? You get six results: two groups and four users. Basically, this flattens out the tree structure into a list of nodes, much like what I’ve done in my previous blog post. If you’re only interested in the users and not the groups, it’s simple to filter out the TFS groups:

var users = members.Where(m => !m.IsContainer);

Once you retrieve your users, you have access to their display names, account names, TeamFoundationIDs (tfsId) and a few other useful properties. A particularly interesting property is the Descriptor.Identifier, which is actually the security identifier (SID), which can be used when retrieving or setting up security programmatically. For example, the IGroupSecurityService interface uses SIDs extensively.

Conclusion

That’s it for now. There are still lots of things to discover about the new API. Some of the other things I am planning on writing about include reading the team configuration (retrieving the product and iteration backlogs, as well as the teams work areas), reading and setting the iteration dates, retrieving the team and team member profile pictures, using the project process template settings and so on.

Does any of this sound interesting to you? Have I forgotten something? What’s your take on the new API?

Be Sociable, Share!
  • robson

    Please, fix the section “Conclusion”. It’s written “ConSlusion”.

    • Ivan Popek

      Wow, can’t believe I missed that one. Thanks a lot for catching it and letting me know so I can fix it :)

  • virasana

    Hi Ivan, I find this a very helpful article. You mention the product and iteration backlogs and in particular the team’s Work Areas. How do I retrieve the Work Areas?

    • http://blog.johnsworkshop.net/ Ivan Popek

      Hi there, I’m glad to year you’ve found the article useful. You can find out more about reading the team Iterations and Areas in another one of my posts: http://bit.ly/GTLSwF