TFS Integration Platform – What is the Lookup Service? Q&A-27

Revised on August 12, 2010.

Clipart Illustration of a Blue Man Wearing A Tie, Shaking Hands With Another Upon Agreement Of A Business Deal The Lookup Service has been floating around as part of the TFS Integration Platform tools for some time, but has not received much attention to date. After it has been thoroughly tested and is adding value at one of our proof-of-concepts, it is time to let the service our of the binary bucket.

Quotation from Bob, Visual Studio ALM Ranger, who is running one of our CQ—>TFS proof of concepts: “ClearQuest uses an “open user name format” and so it would be very difficult to develop a standardized algorithm to covert CQ user names to the AD display names used by TFS. However, CQ does maintain the AD alias (account ID) for each user. The LookupService enables us to take the user account for each CQ user, look up the display name in the AD and use that when populating user fields in TFS work items. This means we have out of the box, 100% reliability of mapping user names for our CQ to TFS migration.

So, what is it and why should we care?

The service is intended to assist us with the translation of the source user information to the target user information, in scenarios where we are dealing with different environments such as ClearQuest on the source and TFS on the target, or cross domain migrations.

The LookupService uses a three phased translation process:
image

  1. Lookup
    During the first phase the TFS Integration Platform will ask the source adapter for more information, such as email address, display name or domain name. For example, migrating from CQ, we only have the users alias or account name, from information stored in a record field.
  2. Transformation
    The second phase is performed by the UserIdentityLookupService in the platform to perform string replacements, string concatenations and formatting. It is basically the user information transformation phase, excluding validation of the user account. The transformation is done during the “Analysis” phase of the migration pipeline and the adapters are not involved.
  3. Validation and Population
    Phase three performs the target user validation, by talking to Team Foundation Server (TFS), Active Directory (AD) or another custom target lookup provider. It essentially validates the target user information and populates the target system information.

Common Question:

  • Should all adapters implement support for this service?
    • There is out of box support for AD, CQ and TFS 2010 (see UserIdentityLookupService.cs and TFS2010UserIdLookupAdapter.cs for details).
    • The lookup service is an add-in, which means the adapters do not have to implement anything. Instead we “add-in” the functionality through configuration and the add-in at run-time.
  • Why not just use value mapping rules in the configuration?
    • The Lookup Service provides additional validation and retrieval of additional run-time information.
    • User information stored with an artefacts (Changes, WIT) only contains display name, which would force us to make a lot of assumptions.

Example configurations

Here is an example configuration file (extracts) that configures the service for a migration from ClearQuest to TFS. Let us briefly explore the contents:

  • Lines 7-9 … we configure the TFS Active Directory User Id Lookup Service addin.
  • Lines 14-16 … we configure the CQ migration source user identity lookup to use the addin.
  • Lines 29-31 … we configure the TFS migration source user identity lookup to use the addin as well.
  • Lines 60 - 67 … we configure stage 1 lookup information. In this case we need to lookup the alias for the owner on CQ side and the DisplayName for the AssignedTo on the TFS side.
  • Lines 103 – 111 …we configure stage 2, the transformation, using SimpleReplacement in this example.

NOTE 2011-10-26: The UserIdLookupEnabled attribute has been deprecated and replaced with a more readanble EnableValidation attribute with the build dated June 2010.

  1: <?xml version="1.0" encoding="utf-16"?>
  2: <Configuration UniqueId="d853ac81-2582-4c79-9097-4688a9365f18" FriendlyName="BugBash1">
  3:   <Providers>
  4:     <Provider ReferenceName="d9637401-7385-4643-9c64-31585d77ed16" FriendlyName="ClearQuest 7.1.0 Adapter" />
  5:     <Provider ReferenceName="04201d39-6e47-416f-98b2-07f0013f8455" FriendlyName="TFS 2010 Migration WIT Provider" />
  6:   </Providers>
  7:   <Addins>
  8:     <Addin ReferenceName="cdde6b6b-72fc-43b6-bbd1-b8a89a788c6f" FriendlyName="TFS Active Directory User Id Lookup Service Addin" />
  9:   </Addins>
  10:   <SessionGroup CreationTime="2010-03-03T07:08:50.3305827Z" FriendlyName="BugBashDemo" SessionGroupGUID="aa27d9d1-1cbb-47d5-a264-4bb4a2627e0a" Creator="XYZ\testuser" SyncIntervalInSeconds="0" SyncDurationInMinutes="0">
  11:     <MigrationSources>
  12:       <MigrationSource InternalUniqueId="a14dc073-e3da-4cc4-b03e-fdc1a9caa347" FriendlyName="CQ source" ServerIdentifier="Server identifier" ServerUrl="7.0.0" SourceIdentifier="UCM01" ProviderReferenceName="d9637401-7385-4643-9c64-31585d77ed16">
  13:         <Settings>
  14:           <UserIdentityLookup>
  15:          <LookupAddin Precedence="1" ReferenceName="cdde6b6b-72fc-43b6-bbd1-b8a89a788c6f" />
  16:      </UserIdentityLookup>
  17:         </Settings>
  18:         <CustomSettings>
  19:           <CustomSetting SettingKey="LoginCredentialConfigType" SettingValue="UseTextUsernamePasswordPairInConfig" />
  20:           <CustomSetting SettingKey="UserName" SettingValue="testuser" />
  21:           <CustomSetting SettingKey="Password" SettingValue="nicetry" />
  22:           <CustomSetting SettingKey="AdminUserName" SettingValue="" />
  23:           <CustomSetting SettingKey="AdminPassword" SettingValue="" />
  24:         </CustomSettings>
  25:         <StoredCredential />
  26:       </MigrationSource>
  27:       <MigrationSource InternalUniqueId="21761303-70c2-4d6f-84bc-8d58e07581d4" FriendlyName="Test3 (va02-tfs4)" ServerIdentifier="2561585b-d29d-4eec-838a-db773cd9d409" ServerUrl="https://va02-tfs4:8080/collection0" SourceIdentifier="Test3" ProviderReferenceName="04201d39-6e47-416f-98b2-07f0013f8455">
  28:         <Settings>
  29:           <UserIdentityLookup>
  30:         <LookupAddin Precedence="1" ReferenceName="cdde6b6b-72fc-43b6-bbd1-b8a89a788c6f" />
  31:      </UserIdentityLookup>
  32:         </Settings>
  33:         <CustomSettings>
  34:           <CustomSetting SettingKey="EnableBypassRuleDataSubmission" SettingValue="" />
  35:           <CustomSetting SettingKey="DisableAreaPathAutoCreation" SettingValue="" />
  36:           <CustomSetting SettingKey="DisableIterationPathAutoCreation" SettingValue="" />
  37:         </CustomSettings>
  38:         <StoredCredential />
  39:       </MigrationSource>
  40:     </MigrationSources>
  41:     <Sessions>
  42:       <Session CreationTime="2010-03-03T07:08:50.3205827Z" SessionUniqueId="be898261-968c-4078-9ba2-e164e7497d36" FriendlyName="Work Item Tracking session" LeftMigrationSourceUniqueId="a14dc073-e3da-4cc4-b03e-fdc1a9caa347" RightMigrationSourceUniqueId="21761303-70c2-4d6f-84bc-8d58e07581d4" SessionType="WorkItemTracking">
  43:         <EventSinks />
  44:         <CustomSettings>
  45:           <SettingXml>
  46:             <WITSessionCustomSetting>            
  47:     <WorkItemTypes
  48:       &lt;WorkItemType LeftWorkItemTypeName="Defect" RightWorkItemTypeName="Bug" fieldMap="CQType2TFSTypeFieldMap" />        
  49:     </WorkItemTypes>
  50:     <FieldMaps>
  51:       <FieldMap name="CQType2TFSTypeFieldMap">
  52:         <MappedFields>
  53:           <MappedField LeftName="Headline" RightName="System.Title" MapFromSide="Left" valueMap="" />
  54:           <MappedField LeftName="State" RightName="System.State" MapFromSide="Left" valueMap="StateMap" />
  55:           <MappedField LeftName="History" RightName="System.History" MapFromSide="Left" valueMap="" />
  56:           <MappedField LeftName="@@MissingField@@" RightName="System.Reason" MapFromSide="Left" valueMap="ReasonDefault" />
  57:           <MappedField LeftName="Priority" RightName="Microsoft.VSTS.Common.Priority" MapFromSide="Left" valueMap="PriorityMap" />
  58:           <MappedField LeftName="Owner" RightName="System.AssignedTo" MapFromSide="Left" valueMap="" />
  59:         </MappedFields>
  60:         <UserIdentityFields>
  61:           <LeftUserIdentityFields>
  62:             <UserIdField FieldReferenceName="Owner" UserIdPropertyName="Alias" />
  63:           </LeftUserIdentityFields>
  64:           <RightUserIdentityFields>
  65:             <UserIdField FieldReferenceName="System.AssignedTo" UserIdPropertyName="DisplayName" />
  66:           </RightUserIdentityFields>
  67:         </UserIdentityFields>
  68:       </FieldMap>
  69:     </FieldMaps>
  70:     <ValueMaps>
  71:       <ValueMap name="ReasonDefault">
  72:         <Value LeftValue="*" RightValue="New" />
  73:       </ValueMap>
  74:       <ValueMap name="StateMap">
  75:         <Value LeftValue="Submitted" RightValue="Active" />
  76:       </ValueMap>
  77:       <ValueMap name="PriorityMap">
  78:         <Value LeftValue="1-Resolve Immediately" RightValue="1" />
  79:         <Value LeftValue="2-Give High Attention" RightValue="2" />
  80:         <Value LeftValue="3-Normal Queue" RightValue="3" />
  81:         <Value LeftValue="4-Low Priority" RightValue="3" />
  82:       </ValueMap>
  83:     </ValueMaps>
  84:        </WITSessionCustomSetting>
  85:           </SettingXml>
  86:           <SettingXmlSchema />
  87:         </CustomSettings>
  88:         <Filters>
  89:           <FilterPair Neglect="false">
  90:             <FilterItem MigrationSourceUniqueId="a14dc073-e3da-4cc4-b03e-fdc1a9caa347" FilterString="Defect::id='UCM0100002148' or id='UCM0100002149' or id='UCM0100004449' " />
  91:             <FilterItem MigrationSourceUniqueId="21761303-70c2-4d6f-84bc-8d58e07581d4" FilterString="[System.Id] = 0" />
  92:           </FilterPair>
  93:         </Filters>
  94:       </Session>
  95:     </Sessions>
  96:     <Linking>
  97:       <CustomSettings />
  98:       <LinkTypeMappings>
  99:         <LinkTypeMapping LeftMigrationSourceUniqueId="a14dc073-e3da-4cc4-b03e-fdc1a9caa347" LeftLinkType="ClearQuestAdapter.LinkType.Duplicate" RightLinkType="Microsoft.TeamFoundation.Migration.TFS.LinkType.WorkItemToWorkItem" RightMigrationSourceUniqueId="21761303-70c2-4d6f-84bc-8d58e07581d4" />
  100:       </LinkTypeMappings>
  101:     </Linking>
  102:     <WorkFlowType Frequency="ContinuousManual" DirectionOfFlow="Unidirectional" SyncContext="Disabled" />
  103:     <UserIdentityMappings EnableValidation="true">
  104:       <UserIdentityLookupAddins>
  105:         <UserIdentityLookupAddin>cdde6b6b-72fc-43b6-bbd1-b8a89a788c6f</UserIdentityLookupAddin>
  106:       </UserIdentityLookupAddins>
  107:       <AliasMappings DirectionOfMapping="LeftToRight">
  108:         <AliasMapping Left="*" Right="*" MappingRule="SimpleReplacement" />
  109:         <AliasMapping Left="" Right="testuser" MappingRule="SimpleReplacement" />
  110:       </AliasMappings>
  111:     </UserIdentityMappings>
  112:   </SessionGroup>
  113: </Configuration>

Here is a Domain to Domain Alias (TFS to TFS) Mapping Example:

  1: <?xml version="1.0"?>
  2: <Configuration xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="https://www.w3.org/2001/XMLSchema" UniqueId="bb4daf36-a590-473a-8500-1e1bfb3ed532" FriendlyName="DomainAliasMappingTest">
  3:   <Providers>
  4:     <Provider ReferenceName="2F82C6C4-…" FriendlyName="TFS 2008 VC Migration Provider" />
  5:     <Provider ReferenceName="FEBC091F-…" FriendlyName="TFS 2010 VC Migration Provider" />
  6:   </Providers>
  7:   <Addins />
  8:   <SessionGroup FriendlyName="Session Group Friendly Name" SessionGroupGUID="352f930f-…"
  9:                 SyncIntervalInSeconds="0" SyncDurationInMinutes="0">
  10:     <MigrationSources>
  11:       <MigrationSource InternalUniqueId="436c872b-…" FriendlyName="TFS 2010 source" 
  12:                        ServerIdentifier="TFS 2010" ServerUrl="serverurl" SourceIdentifier="Test1" 
  13:                         ProviderReferenceName="FEBC091F-…">
  14:         <Settings>
  15:           <DefaultUserIdProperty UserIdPropertyName="DomainAlias" />
  16:         </Settings>
  17:       </MigrationSource>
  18:       <MigrationSource InternalUniqueId="b6639fe5-…" FriendlyName="TFS 2010 target" 
  19:                        ServerIdentifier="TFS 2010" ServerUrl="serverurl" SourceIdentifier="Test2" 
  20:                        ProviderReferenceName="FEBC091F-…">
  21:         <Settings>
  22:           <DefaultUserIdProperty UserIdPropertyName="DomainAlias" />
  23:         </Settings>
  24:       </MigrationSource>
  25:     </MigrationSources>
  26:     <Sessions>
  27:       <Session SessionUniqueId="d7dae825-…" FriendlyName="Version Control session"
  28:                LeftMigrationSourceUniqueId="436c872b-…" RightMigrationSourceUniqueId="b6639fe5-…" 
  29:                SessionType="VersionControl">
  30:         <Filters>
  31:           <FilterPair Neglect="false">
  32:             <FilterItem MigrationSourceUniqueId="436c872b-…" FilterString="$/T1/DomainAliasMappingTest" />
  33:             <FilterItem MigrationSourceUniqueId="b6639fe5-…" FilterString="$/T2/DomainAliasMappingTest" />
  34:           </FilterPair>
  35:         </Filters>
  36:       </Session>
  37:     </Sessions>
  38:     <Linking>
  39:       <CustomSettings />
  40:       <LinkTypeMappings />
  41:     </Linking>
  42:     <WorkFlowType Frequency="ContinuousManual" DirectionOfFlow="Unidirectional" 
  43:                   SyncContext="Unidirectional" />
  44:     <UserIdentityMappings EnableValidation="false">
  45:       <UserIdentityLookupAddins>
  46:         <UserIdentityLookupAddin>CDDE6B6B-…</UserIdentityLookupAddin>
  47:       </UserIdentityLookupAddins>
  48:       <AliasMappings DirectionOfMapping="LeftToRight">
  49:         <AliasMapping Left="*" Right="*" MappingRule="SimpleReplacement" />
  50:       </AliasMappings>
  51:       <DomainMappings DirectionOfMapping="LeftToRight">
  52:         <DomainMapping Left="s-domain" Right="t-domain" MappingRule="SimpleReplacement" />
  53:       </DomainMappings>
  54:     </UserIdentityMappings>
  55:   </SessionGroup>
  56: </Configuration>

Note the DefaultUserIdProperty (line 22) and DomainMappings (lines 51-53) configuration elements, which are different from the previous example.

A bit more information on some of the configuration elements

UserIdPropertyName

MappingRules (enum) Description
DisplayName Users display name, which specific to TFS WIT migration sources
Domain Users domain name.
Alias Users alias name.
DomainAlias Users fully qualified domain alias name, which specific to TFS VC migration sources
EmailAddress Users email address.
UniqueId Users security identifier (SID).
QualifiedName Users distinguished name. (CN=Jeff Smith,OU=Sales,DC=Fabrikam,DC=COM)

MappingRule

MappingRules (enum) Description
SimpleReplacement Simply replace the field value as shown in our example configuration file. Note that Line 109, without a wildcard, takes precedence over line 109, replacing all fields with no value in the source with testuser on the target.
FormatStringComposition See What is the Lookup Service? Q&A-27 Errata 1
FormatStringDecomposition See What is the Lookup Service? Q&A-27 Errata 1
Ignore Ignore the field and perform no mapping.

NOTE: We recommend that you refer to the latest documentation that is included with the TFS Integration Platform for the latest configuration features and rules.