Following up from my last post "'Grep'ing Groups" and this question on the forums, here's a bit of code that will help you add a user to multiple groups across all projects on your server. Say you want to add someone with the domain account "CORPNET\JoeBlogs" to the "Readers" group for all your projects on your Team Foundation Server "atlantis". Simply download, build, and run the attached code with the arguments "atlantis CORPNET\JoeBlogs Readers" as a Team Foundation Adminstrator.

A few of the more salient points in the code are as follows:

NTAccount userAccount = new NTAccount(username);
...
SecurityIdentifier userSid = (SecurityIdentifier)userAccount.Translate(typeof(SecurityIdentifier));

Here, I'm using some of the System.Security.Principal .NET helpers to conver our user to a SID. While I could also use the IGroupSecurityService's "ReadIdentityFromSource" call, that includes a round-trip to the server and includes a bit more information than we need in this helper code.

ProjectInfo[] projects = css.ListProjects();

Here, I chose to use "ListProjects()" rather than "ListAllProjects()". The former only includes well formed projects while the latter will list all projects that are in the process of being created, being deleted, etc. as well. In this case, I think we only want to try to update projects that have been completely constructed.

Identity projectGroup = gss.ReadIdentity(SearchFactor.AccountName, String.Format("[{0}]\\{1}", project.Name, groupToUpdate), QueryMembership.Expanded);
...
List<string> members = new List<string>(projectGroup.Members);
if (!members.Contains(userSid.ToString())) {...}

Before attempting to add the user to the group, I want to make sure it's not already a member. I chose to query the extended membership rather than just direct as it wouldn't make a difference in terms of user permissions whether the user is a direct member or a member under a nested group. However, if you want to make sure that the user is a direct member of the specified groups, you could change the "QueryMemberhip" value to Direct. Changing the query to Direct will also speed up the ReadIdentity call, particularly if your groups have many members.

Happy coding!