Querying TFS for groups and memberships
Customers find it necessary at times to extract information from TFS about groups and their membership. This can arise in the context of auditing, custom report generation, or building custom extensions to TFS. The following sample shows how this can be accomplished quite easily using the Identity Management Service API's. The sample can extract all groups and members in the Team Foundation server, in a Team Project Collection, or just scope it to a Team Project.
To compile and run, add references to Microsoft.TeamFoundation.Client and Microsoft.TeamFoundation.Common assemblies.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Framework.Client;
using Microsoft.TeamFoundation.Framework.Common;
namespace TFSGroupMembersFast
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Enter TFS server or project collection Url");
Console.Write("e.g. https://localhost:8080/tfs/defaultcollection: ");
string address = Console.ReadLine();
Console.WriteLine();
Console.WriteLine("Enter Team Project Uri for project scope, else empty string");
Console.Write("e.g. vstfs:///Classification/TeamProject/****: ");
string projectUri = Console.ReadLine();
Console.WriteLine();
DateTime startTime = DateTime.Now;
TfsTeamProjectCollection tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(TfsTeamProjectCollection.GetFullyQualifiedUriForName(address));
_ims = tfs.GetService<IIdentityManagementService>();
if (string.IsNullOrEmpty(projectUri))
{
TeamFoundationIdentity group = _ims.ReadIdentity(GroupWellKnownDescriptors.EveryoneGroup, MembershipQuery.Direct, ReadIdentityOptions.None);
m_identities[group.Descriptor] = group;
m_groups.Add(group);
// Get expanded membership of the Valid Users group, which is all identities in this host
group = _ims.ReadIdentity(GroupWellKnownDescriptors.EveryoneGroup, MembershipQuery.Expanded, ReadIdentityOptions.None);
FetchIdentities(group.Members);
}
else
{
// Get all groups in this project
TeamFoundationIdentity[] projectGroups = _ims.ListApplicationGroups(projectUri, ReadIdentityOptions.None);
Dictionary<IdentityDescriptor, object> descSet = new Dictionary<IdentityDescriptor, object>(IdentityDescriptorComparer.Instance);
foreach (TeamFoundationIdentity projectGroup in projectGroups)
{
descSet[projectGroup.Descriptor] = projectGroup.Descriptor;
}
// Expanded membership of project groups
projectGroups = _ims.ReadIdentities(descSet.Keys.ToArray(), MembershipQuery.Expanded, ReadIdentityOptions.None);
// Collect all descriptors
foreach (TeamFoundationIdentity projectGroup in projectGroups)
{
foreach (IdentityDescriptor mem in projectGroup.Members)
{
descSet[mem] = mem;
}
}
FetchIdentities(descSet.Keys.ToArray());
}
// Now output groups and their members.
foreach (TeamFoundationIdentity identity in m_groups)
{
Write(identity);
}
Console.WriteLine(String.Format("======= Finished reading {0} identities in {1} minutes",
m_identities.Count, (DateTime.Now - startTime).TotalMinutes));
Console.ReadLine();
}
static void FetchIdentities(IdentityDescriptor[] descriptors)
{
TeamFoundationIdentity[] identities;
// If total membership exceeds batch size limit for Read, break it up
int batchSizeLimit = 100000;
if (descriptors.Length > batchSizeLimit)
{
int batchNum = 0;
int remainder = descriptors.Length;
IdentityDescriptor[] batchDescriptors = new IdentityDescriptor[batchSizeLimit];
while (remainder > 0)
{
int startAt = batchNum * batchSizeLimit;
int length = batchSizeLimit;
if (length > remainder)
{
length = remainder;
batchDescriptors = new IdentityDescriptor[length];
}
Array.Copy(descriptors, startAt, batchDescriptors, 0, length);
identities = _ims.ReadIdentities(batchDescriptors, MembershipQuery.Direct, ReadIdentityOptions.None);
SortIdentities(identities);
remainder -= length;
}
}
else
{
identities = _ims.ReadIdentities(descriptors, MembershipQuery.Direct, ReadIdentityOptions.None);
SortIdentities(identities);
}
}
static void SortIdentities(TeamFoundationIdentity[] identities)
{
foreach (TeamFoundationIdentity identity in identities)
{
m_identities[identity.Descriptor] = identity;
if (identity.IsContainer)
{
m_groups.Add(identity);
}
}
}
static void Write(TeamFoundationIdentity group)
{
// Output this group's membership
Console.WriteLine("Members of group: {0}", group.DisplayName);
Console.WriteLine("=================");
foreach (IdentityDescriptor memDesc in group.Members)
{
Console.WriteLine(m_identities[memDesc].DisplayName);
}
Console.WriteLine();
}
static Dictionary<IdentityDescriptor, TeamFoundationIdentity> m_identities =
new Dictionary<IdentityDescriptor, TeamFoundationIdentity>(IdentityDescriptorComparer.Instance);
static List<TeamFoundationIdentity> m_groups = new List<TeamFoundationIdentity>();
static IIdentityManagementService _ims;
}
}
Comments
- Anonymous
February 06, 2013
I would like to create a dashboard (query results web part) that displays all work items where assigned_to = @Me or assigned_to is in the collection of groups in which @Me is a member. Can you provide guidance on how I may do this?