<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Vasu Sankaran</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/vasu_sankaran/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/vasu_sankaran/feed.xml</link>
    <description>Senior Software Engineer - Visual Studio Team Foundation Server</description>
    <lastBuildDate>Wed, 17 Apr 2019 03:51:52 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Querying TFS for groups and memberships</title>
      <link>https://docs.microsoft.com/archive/blogs/vasu_sankaran/querying-tfs-for-groups-and-memberships</link>
      <pubDate>Mon, 11 Oct 2010 11:21:00 GMT</pubDate>
      <dc:creator><![CDATA[Vasu Sankaran]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/vasu_sankaran/2010/10/11/querying-tfs-for-groups-and-memberships/</guid>
      <description><![CDATA[Customers find it necessary at times to extract information from TFS about groups and their...]]></description>
      <content:encoded><![CDATA[<p>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&nbsp;to a Team Project.</p>
<p>To compile and run,&nbsp;add references to Microsoft.TeamFoundation.Client and Microsoft.TeamFoundation.Common assemblies.</p>
<p>using System;<br />using System.Collections.Generic;<br />using System.Linq;<br />using System.Text;<br />using Microsoft.TeamFoundation.Client;<br />using Microsoft.TeamFoundation.Framework.Client;<br />using Microsoft.TeamFoundation.Framework.Common;</p>
<p>namespace TFSGroupMembersFast<br />{<br />&nbsp;&nbsp;&nbsp; class Program<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static void Main(string[] args)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("Enter TFS server or project collection Url");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.Write("e.g. <a href="https://localhost:8080/tfs/defaultcollection">https://localhost:8080/tfs/defaultcollection</a>: ");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string address = Console.ReadLine();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("Enter Team Project Uri for project scope, else empty string");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.Write("e.g. vstfs:///Classification/TeamProject/****: ");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string projectUri = Console.ReadLine();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DateTime startTime = DateTime.Now;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TfsTeamProjectCollection tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(TfsTeamProjectCollection.GetFullyQualifiedUriForName(address));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _ims = tfs.GetService&lt;IIdentityManagementService&gt;();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (string.IsNullOrEmpty(projectUri))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TeamFoundationIdentity group = _ims.ReadIdentity(GroupWellKnownDescriptors.EveryoneGroup, MembershipQuery.Direct, ReadIdentityOptions.None);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_identities[group.Descriptor] = group;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_groups.Add(group);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Get expanded membership of the Valid Users group, which is all identities in this host&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; group = _ims.ReadIdentity(GroupWellKnownDescriptors.EveryoneGroup, MembershipQuery.Expanded, ReadIdentityOptions.None);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FetchIdentities(group.Members);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Get all groups in this project<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TeamFoundationIdentity[] projectGroups = _ims.ListApplicationGroups(projectUri, ReadIdentityOptions.None);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dictionary&lt;IdentityDescriptor, object&gt; descSet = new Dictionary&lt;IdentityDescriptor, object&gt;(IdentityDescriptorComparer.Instance);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (TeamFoundationIdentity projectGroup in projectGroups)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; descSet[projectGroup.Descriptor] = projectGroup.Descriptor;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Expanded membership of project groups<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; projectGroups = _ims.ReadIdentities(descSet.Keys.ToArray(), MembershipQuery.Expanded, ReadIdentityOptions.None);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Collect all descriptors<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (TeamFoundationIdentity projectGroup in projectGroups)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (IdentityDescriptor mem in projectGroup.Members)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; descSet[mem] = mem;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FetchIdentities(descSet.Keys.ToArray());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Now output groups and their members.<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (TeamFoundationIdentity identity in m_groups)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write(identity);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine(String.Format("======= Finished reading {0} identities in {1} minutes", <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_identities.Count, (DateTime.Now - startTime).TotalMinutes));</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.ReadLine();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static void FetchIdentities(IdentityDescriptor[] descriptors)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TeamFoundationIdentity[] identities;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // If total membership exceeds batch size limit for Read, break it up<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int batchSizeLimit = 100000;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (descriptors.Length &gt; batchSizeLimit)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int batchNum = 0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int remainder = descriptors.Length;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IdentityDescriptor[] batchDescriptors = new IdentityDescriptor[batchSizeLimit];</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (remainder &gt; 0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int startAt = batchNum * batchSizeLimit;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int length = batchSizeLimit;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (length &gt; remainder)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; length = remainder;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; batchDescriptors = new IdentityDescriptor[length];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Array.Copy(descriptors, startAt, batchDescriptors, 0, length);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; identities = _ims.ReadIdentities(batchDescriptors, MembershipQuery.Direct, ReadIdentityOptions.None);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SortIdentities(identities);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; remainder -= length;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; identities = _ims.ReadIdentities(descriptors, MembershipQuery.Direct, ReadIdentityOptions.None);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SortIdentities(identities);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static void SortIdentities(TeamFoundationIdentity[] identities)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (TeamFoundationIdentity identity in identities)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_identities[identity.Descriptor] = identity;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (identity.IsContainer)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_groups.Add(identity);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static void Write(TeamFoundationIdentity group)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Output this group's membership<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("Members of group: {0}", group.DisplayName);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("=================");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (IdentityDescriptor memDesc in group.Members)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine(m_identities[memDesc].DisplayName);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static Dictionary&lt;IdentityDescriptor, TeamFoundationIdentity&gt; m_identities =<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new Dictionary&lt;IdentityDescriptor, TeamFoundationIdentity&gt;(IdentityDescriptorComparer.Instance);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static List&lt;TeamFoundationIdentity&gt; m_groups = new List&lt;TeamFoundationIdentity&gt;();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static IIdentityManagementService _ims;<br />&nbsp;&nbsp;&nbsp; }<br />}</p>]]></content:encoded>
    </item>
    <item>
      <title>Identity Change in TFS 2010</title>
      <link>https://docs.microsoft.com/archive/blogs/vasu_sankaran/identity-change-in-tfs-2010</link>
      <pubDate>Mon, 07 Jun 2010 11:48:00 GMT</pubDate>
      <dc:creator><![CDATA[Vasu Sankaran]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/vasu_sankaran/2010/06/07/identity-change-in-tfs-2010/</guid>
      <description><![CDATA[In this post I will discuss the nuances of changing identities in TFS 2010. The notion of changing...]]></description>
      <content:encoded><![CDATA[<p>In this post I will discuss the nuances of changing identities in TFS 2010. The notion of changing an identity or mapping a user in TFS arises in scenarios where the identity's external identifier has changed, and we wish to make TFS aware of that change. Moving the server from workgroup to domain, moving from one hardware configuration to another, are examples of such scenarios that may entail changing identities - see <a href="https://msdn.microsoft.com/en-US/library/ms404883.aspx">https://msdn.microsoft.com/en-US/library/ms404883.aspx</a>. In TFS 2008, identity change was accomplished by running the <a href="https://msdn.microsoft.com/en-us/library/ms253054(VS.80).aspx">TFSAdminUtil Sid </a>command. In TFS 2010 we provide the new and improved <a href="https://msdn.microsoft.com/en-US/library/ms253054.aspx">TFSConfig Identities </a>command, aspects&nbsp;of which we discuss now. </p>
<ol>
<li><strong>Target account restriction</strong> - the target account of the mapping should not already exist in TFS. This is a key restriction of <a href="https://msdn.microsoft.com/en-US/library/ms253054.aspx">TFSConfig Identities </a>, as well as the older <a href="https://msdn.microsoft.com/en-us/library/ms253054(VS.80).aspx">TFSAdminUtil Sid</a>. If the target account is somehow introduced, either by being granted permissions or by group membership sync, it cannot be undone. Simply "removing" the user from group(s) will not work. Removing identities in TFS simply marks the record as logically deleted, and does not physically delete because the identity may own TFS artifacts such as changesets or work items.</li>
<li><strong>Batch&nbsp;versus individual mode</strong> - if account names are the same in both domains, the command can be run in batch mode. Otherwise identities can changed individually, specifying a different target account name. </li>
<li><strong>Identity source</strong> - if the identity was directly added to a TFS group, running the Identity Change command is sufficient. For accounts which were picked up by TFS by syncing AD groups, the source of the data should also reflect the change.</li>
<li><strong>Sync update</strong> - after running the identity change command, the next hourly sync will update all properties.</li>
</ol>
<p>One scenario to which identity change is applicable is <strong>domain migration</strong> of account(s) in Active Directory. For this scenario, we have made an improvement&nbsp;in 2010 which makes it unnecessary to run identity change when the migration is to another domain <strong><em>within the same forest</em></strong>. Every identity in AD has a Guid in addition to the SID. In TFS 2010, we sync this AD Guid, and use it as the internal identity reference in TFS (TeamFoundationId). When an identity is domain migrated within the same AD forest, its SID changes but&nbsp;Guid remains the same.&nbsp;Hence TFS can automatically detect the SID change. This improves user experience because users will no longer get into the irredeemable state in which identity change does not work because the target account is already in TFS. Instead when the domain migrated account is added to TFS, its SID will automatically be updated.</p>
<p><strong>Revert Behavior</strong></p>
<p>There is a flip side to this to be aware. If identity change command is used to map unrelated accounts in the same AD environment, and if the old account is ever picked up again by TFS, then the identity change will revert back! It is easiest to explain this revert behavior by getting a bit technical. As stated above, every domain account in AD has a Guid, which is preserved during intra-forest AD migration. So let us say the following account is first added to TFS:</p>
<p>DomainA\foo, G1</p>
<p>with G1 being the Guid. This account is then &ldquo;migrated&rdquo; to "DomainB", and TFSConfig Identities is run, so this identity record in TFS becomes:</p>
<p>DomainB\foo, G1</p>
<p>If this was a true domain migration in AD within the same forest, this change will persist, because DomainA\foo is gone. But let us say that all that was done was to create a new account in DomainB, and DomainA\foo still exists in AD. Then if TFS ever encounters this account (for example, by group membership sync), then it will revert the identity row back to:</p>
<p>DomainA\foo, G1</p>
<p>This is the auto-Sid migration logic, which keys off of the identity Guid.&nbsp;Note that legitimate&nbsp;identity change, such as workgroup to domain account, or intra-forest AD migration, will never get&nbsp;reverted.</p>
<p>Some aspects of this topic have been discussed in the context of TFS Upgrade in <a href="https://blogs.msdn.com/b/vasu_sankaran/archive/2010/05/11/guidance-for-upgrading-to-tfs-2010-along-with-domain-move.aspx">Guidance for upgrading to TFS 2010 along with domain move</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Identity Synchronization in Team Foundation Server 2010</title>
      <link>https://docs.microsoft.com/archive/blogs/vasu_sankaran/identity-synchronization-in-team-foundation-server-2010</link>
      <pubDate>Mon, 07 Jun 2010 09:17:42 GMT</pubDate>
      <dc:creator><![CDATA[Vasu Sankaran]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/vasu_sankaran/2010/06/07/identity-synchronization-in-team-foundation-server-2010/</guid>
      <description><![CDATA[There are many improvements in TFS 2010 when it comes to synchronization of groups and users with...]]></description>
      <content:encoded><![CDATA[<p>There are many improvements in TFS 2010 when it comes to synchronization of groups and users with identity providers. They fall into 2 categories -</p>
<p>(i) implementing an extensible identity provider model which will be exploited in furture releases to support identity types such as Passport, and<br />(ii) robustness and scalability.</p>
<p>Identity sync runs as a core plug-in of the TFS Job Agent. The frequency of identity sync is determined by the&nbsp;schedule of this job. By default this is once an hour, on the hour. This is the recommended value and should be adequate in most cases. During every periodic sync run:</p>
<p>(i) membership of groups is brought up to date, and<br />(ii) detailed properties of a fraction of identities is refreshed.</p>
<p>With regard to the second aspect, the refresh cycle duration is 24 hours. That is, detailed properties of all identities will be refreshed once during that interval. </p>
<p>The default frequency can be changed either by modifying the job schedule in the TFS Configuration database (Interval column of tbl_JobSchedule), or by using the Job Service object model. The job id of the sync job is 544DD581-F72A-45A9-8DE0-8CD3A5F29DFE. It is not recommended that this frequency be increased drastically, as it will increase the burden on the server as well as on Active Directory. The refresh cycle duration is set in the TFS Registry in the Configuration database with the registry path \Service\Integration\Settings\IdentitySyncCycleDuration\. A related registry setting is \Service\Integration\Settings\IdentitySyncFull\. Setting this to the value True will cause the next sync to be a "full" sync, i.e. refresh detailed properties of all identities.</p>
<p>If an immediate refresh of a specific group or user is required, Identity Management Service provides the RefreshIdentity method. For a group, executing this method will force synchronization of the group and its direct members. For a user, properties will be refreshed. </p>
<p><strong>Troubleshooting Identity synchronization issues</strong></p>
<p>"User has been added to a group, but they do not have access to TFS". </p>
<p>In TFS there is an external identity sync process with the provider such as Active Directory, and an internal sync to Workitem Tracking (WIT). When troubleshooting user access problems which appear to be caused by identity synchronization, the first step is to&nbsp;determine which of the two sync processes seems broken. First of all, note that when a user is added directly to a TFS group, there is no further external sync required. It is only when the user is picked up by nested membership in a Windows group that external sync&nbsp;comes into play. </p>
<p>1. Verify that the Team Foundation job service is running.<br />2. Confirm the hourly event log entry for identity sync, and check for any errors logged.<br />3. Using the Group Membership UI, check drilling down to the affected user.</p>
<p>If the user is not found in the expected group, the following command can be used to directly query AD for that group's membership. It should be run from the TFS application tier from a command prompt that is running as the TFS service account.</p>
<p>dsquery group -samid &lt;group&gt; -d &lt;domain&gt; | dsget group -members</p>
<p>If dsquery returns the user, but TFS does not, then the external sync must be investigated. If the user appears in the Group Membership dialog, but not in the WIT Assigned To list, that would point to the internal sync.</p>]]></content:encoded>
    </item>
    <item>
      <title>Guidance for upgrading to TFS 2010 along with domain move</title>
      <link>https://docs.microsoft.com/archive/blogs/vasu_sankaran/guidance-for-upgrading-to-tfs-2010-along-with-domain-move</link>
      <pubDate>Tue, 11 May 2010 13:34:00 GMT</pubDate>
      <dc:creator><![CDATA[Vasu Sankaran]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/vasu_sankaran/2010/05/11/guidance-for-upgrading-to-tfs-2010-along-with-domain-move/</guid>
      <description><![CDATA[Recently one of our customers wished to upgrade their TFS 2008 server in one domain to TFS 2010 in a...]]></description>
      <content:encoded><![CDATA[<P>Recently one of our customers wished to upgrade their TFS 2008 server in one domain to TFS 2010 in a different domain with no trust between the domains. This procedure seems to be of general interest and is hence the subject of this blog post. </P>
<P>The tricky aspect of the procedure is the correct sequence for changing identities to mapped users in the new domain. The <A title="Identities Change" href="https://msdn.microsoft.com/en-us/library/ms253054.aspx" mce_href="https://msdn.microsoft.com/en-us/library/ms253054.aspx"><STRONG>Identities Change</STRONG></A> command in TFS 2010, as well as its equivalent in TFS 2008, have a key restriction that the target account&nbsp;cannot already exist in TFS. That is if you wish to change domainA\foo to domainB\foo, then the latter account cannot already exist in TFS when running the command. If the target account is somehow introduced, either by being granted permissions or by group membership sync, it cannot be undone. Simply "removing" the user from group(s) will not work. Removing identities in TFS simply marks the record as logically deleted, and&nbsp;does not physically delete because the&nbsp;identity may own TFS artifacts such as changesets or work items.</P>
<P>There a few different options to accomplish the goal of upgrading to TFS 2010 along with domain move. Our general recommendation is to perform one operation at a time. Thus you can:</P>
<OL>
<LI>Upgrade TFS 2008 to TFS 2010 in the original domain, followed by migration to new domain. You may follow the instructions at <A href="https://msdn.microsoft.com/en-us/library/ms404883(v=VS.100).aspx"><STRONG>https://msdn.microsoft.com/en-us/library/ms404883(v=VS.100).aspx</STRONG></A> for moving the TFS 2010 server.</LI>
<LI>Migrate TFS 2008 to the new domain first, then upgrade to TFS 2010. The instructions for this can be found at <A href="https://msdn.microsoft.com/en-us/library/ms404883(v=VS.80).aspx"><STRONG>https://msdn.microsoft.com/en-us/library/ms404883(v=VS.80).aspx</STRONG></A>.</LI></OL>
<P>Option 1 above is to be preferred because the identity change command in 2010 is more robust and flexible.</P>
<P>If neither of these procedures will work, however, you can follow this combined procedure.</P>
<P>i. Upgrade TFS 2008 to TFS 2010 in the new domain, but <STRONG>do not</STRONG> add any new domain accounts or groups to TFS yet.<BR>ii. Change identities - refer <A title="Identities Change" href="https://msdn.microsoft.com/en-us/library/ms253054.aspx" mce_href="https://msdn.microsoft.com/en-us/library/ms253054.aspx"><STRONG>Identities Change</STRONG></A>&nbsp;command.<BR>iii. Add any AD groups from the new domain.</P>
<P>This will ensure that the new domain accounts are not introduced before running identities change for mapped accounts. The only exception is that a service account from the new domain has to be added to TFS during the upgrade, namely step (i) above. If you wish to domain migrate the old service account also, you will have to use a temporary service account for this step to circumvent this.</P>
<P>When using the identities change command, there are a few points to keep in mind.</P>
<UL>
<LI>If account names are the same in both domains, the command can be run in batch mode. Otherwise they can be changed individually, specifying a different target account name.</LI>
<LI>For accounts which were added to TFS by syncing AD groups, the source of the data should also reflect the change.</LI>
<LI>After running the identities change command, you have to wait for the next hourly sync to update all properties.</LI></UL>
<P mce_keep="true">&nbsp;</P>]]></content:encoded>
    </item>
    <item>
      <title>TF50374 error when upgrading to TFS 2010</title>
      <link>https://docs.microsoft.com/archive/blogs/vasu_sankaran/tf50374-error-when-upgrading-to-tfs-2010</link>
      <pubDate>Tue, 11 May 2010 13:01:00 GMT</pubDate>
      <dc:creator><![CDATA[Vasu Sankaran]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/vasu_sankaran/2010/05/11/tf50374-error-when-upgrading-to-tfs-2010/</guid>
      <description><![CDATA[When upgrading an older version Team Foundation server to TFS 2010, you may encounter the following...]]></description>
      <content:encoded><![CDATA[<p>When upgrading an older version Team Foundation server to TFS 2010, you may encounter the following error:</p>
<p><strong>Error TF50374 "Project group XXXX cannot be added to global group YYYY."</strong></p>
<p>If you do, please read on for instructions to resolve the problem. </p>
<p>We have had a long standing restriction in&nbsp;TFS&nbsp;that global TFS groups (a.k.a server groups) cannot contain team project groups. The error that is the subject of this post&nbsp;indicates that the server that&nbsp;you are&nbsp;upgrading contains data that precedes this restriction. This post explains how to remove these membership(s) and upgrade successfully.</p>
<p>There is nothing in the product that requires this membership, and they are custom ones created by the customer. As a result of the membership, the project group will acquire permission(s) granted the parent global group. The reason why we don't automatically remove them during upgrade is so that they can be examined. If necessary any permissions can be granted directly to the project group(s), and then the memberships removed.</p>
<p>The recommended method is to use the UI or tfssecurity command line tool against the old server (if available still).</p>
<ol>
<li>Get global TFS groups from UI (Team Explorer server -&gt; Settings -&gt; Group Membership).</li>
<li>Record permissions on these groups from Security dialog.</li>
<li>Examine membership of these groups.&nbsp;</li>
<li>Remove team project groups that they contain.</li>
<li>Directly grant required permissions to those team project groups.</li>
</ol>
<p>There are 3 standard server groups in TFS 2005 / 2008: "Team Foundation Administrators", "Service Accounts" and "Team Foundation Valid Users", and perhaps additional custom groups. The Valid Users group is a system managed umbrella group which we don't have to worry about. After removing any member(s) which are project groups, take a fresh back up of the TFS Integration database, restore onto the new server and run upgrade again (be sure to uninstall TFS2010 on the new server to remove the configuration and then re-run setup again and select the Upgrade Wizard to start the upgrade again).</p>
<p>If the old TFS server is no longer available/running, as&nbsp;in the case of an in-place upgrade, then please contact Microsoft Customer Support Services for a SQL script for removing the membership(s), reference article 2266305&nbsp;(you may be asked to provide a credit card number depending on the type of support contract you have, but ultimately the support case will be free).&nbsp;After successfully running the script&nbsp;you can go to the TFS 2010 Administration console on the Application Tier, navigate to the Team Project Collection node and select the "retry" button to resume the upgrade.</p>]]></content:encoded>
    </item>
  </channel>
</rss>