none
Feature Enablement App Fails with TFS 2013

    Question

  • Hello.  I'm trying to update the code from Ewald's blog post (regarding how to programmatically configure features across multiple team projects) so that it can be used to upgrade from TFS 2012.2 to TFS 2013 RTM. I'm compiling the application using VS 2013 Premium.

    (Note: I successfully used this code during our upgrade from TFS 2010 SP1 to TFS 2012 so I know the source code worked previously.)

    So far, I've tried two approaches:

    1. Run the application as-is and see if it still works with TFS 2013 (using TFS 2012 assemblies).
    2. Update the TFS assemblies to TFS 2013 equivalents and run it from there.

    Neither one works, but both result in different errors.

    First Option:

    The application compiles but I encounter a run-time error at the following line in the GetContext() method (see blog post for rest of the code):

    TeamFoundationIdentityService ims = deploymentRequestContext.GetService<TeamFoundationIdentityService>();

    ArgumentException thrown:

    The string must have at least one character.
    Parameter name: connectionString

    Second Option:

    I upgraded the server assemblies to their TFS 2013 equivalents, and had to add Microsoft.TeamFoundation.Server.Core.dll since the TeamFoundationIdentityService class was apparently moved. I also had to add a reference to Microsoft.VisualStudio.Service.WebApi assembly, as the TeamFoundationRequestContext class was complaining about a dependency on this reference (which I still don't understand).

    The type 'Microsoft.VisualStudio.Services.WebApi.VssHttpClientBase' is defined in an assembly that is not referenced. You must add a reference to assembly 'Microsoft.VisualStudio.Services.WebApi, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.

    Once resolving all the compilation errors, I run the application and get a different run-time error at the following line in the GetContext() method (see blog post for rest of code):

    using (TeamFoundationRequestContext deploymentRequestContext = deploymentServiceHost.CreateSystemContext())

    DatabaseConnectionException

    TF246017: Team Foundation Server could not connect to the database. Verify that the server that is hosting the database is operational, and that network problems are not blocking communication with the server.

    I verified that the database is up and running and I can perform a number of user operations within Web Access.

    Any further troubleshooting steps would be much appreciated.

    • Do I have the correct assemblies updated in the Second Option?
    • Do I really need the Microsoft.VisualStudio.Services.WebApi reference or is there a cleaner approach?

    Thanks!

    Matt

    • Changed type Matt Ring Thursday, November 14, 2013 4:40 PM Should have been a question originally
    Thursday, November 14, 2013 3:04 AM

Answers

  • Hi Matt,

    For security reasons the SQL connection string was removed from the registry service. Unfortunately this makes the code sample a bit harder since it cannot bootstrap the server OM (which requires a connection string) by using the client OM. If you have access to the Application Tier (AT) machine you can obtain the same connection string the AT/JobAgent uses to connect to SQL by looking in the web.config file located at: %ProgramFiles%\Microsoft Team Foundation Server 12.0\Application Tier\Web Services\web.config (look under <appSettings><add key="applicationDatabase" value="<connection string you need>" />)

    The other thing you will need to do to prevent the AccessMapping error -- as strange as it sounds -- is to drop Microsoft.VisualStudio.Services.Identity.dll into the bin path of the application (the same place where you've placed Microsoft.TeamFoundation.Framework.Server.dll should work). You can grab this file from %ProgramFiles%\Microsoft Team Foundation Server 12.0\Application Tier\Web Services\bin or %ProgramFiles%\Microsoft Team Foundation Server 12.0\Application Tier\TFSJobAgent.

    The error message (relating to AccessMapping) is obviously not helpful in diagnosing the problem, we will be fixing this in the next update. The problem is that requestContext.GetService<T>() is trying to load a specific implementation called PlatformIdentityService (which resides in Microsoft.VisualStudio.Services.Identity.dll). When it cannot find this type because the assembly is not in the bin or probing path, it defaults to FrameworkIdentityService (which is located in Microsoft.TeamFoundation.Framework.Server.dll). Unfortunately this implementation is not suitable for your environment -- the OM should have  thrown that it could not load the specified type from the specified assembly and the exception message would have been reasonably clear as to what you would need to do. This will be fixed in the next update.

    Please let me know if this works and if you have any other questions -- hopefully you won't hit anymore hurdles with this!


    Russell Tolley

    Tuesday, November 19, 2013 4:34 PM
  • Hi Matt,

    You can find an updated version of the tool here: https://features4tfs.codeplex.com/
    It should work with TFS 2013 server.

    Thanks,
    Oleg
    Wednesday, November 27, 2013 12:22 AM

All replies

  • Hi Matt,

    Thanks for your post.

    I am trying to involve someone familiar with this topic to further look at this issue. There might be some time delay.

    Appreciate your patience.


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Friday, November 15, 2013 5:52 AM
  • Hi Matt,

    When you debug the project in VS2013, you will find that the connectionString in the GetDeploymentServiceHost method is empty. So, instead of using the following code to generate the connectionString by comment them out:

    const string connectionStringPath = "/Configuration/Database/Framework/ConnectionString";
    var registry = teamProjectCollection.ConfigurationServer.GetService<Microsoft.TeamFoundation.Framework.Client.ITeamFoundationRegistry>();
    string connectionString = registry.GetValue(connectionStringPath);

    You can just set the connectionString's value manually:

    string connectionString = "Data Source=DTServerIP;Initial Catalog=Tfs_Configuration;Integrated Security=True";

    Thanks.


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Friday, November 15, 2013 9:07 AM
  • Hi Vicky -

    Thanks for the suggestion.  I confirmed that those TFS registries are removed from TFS 2013 (unfortunately), and I couldn't find any other TFS registries that provided the connection string to the TFS_Configuration database through the TFS SDK. Design-wise, that had made for a cleaner solution as I was able to externalize the collection URL as a command-line parameter and re-use the compiled code against different TFS environments. Oh well... I should be able to externalize the DB server name the same way - just a little extra tweaking.

    In further debugging, I am running against another error later on in the application.  This time, in the ReadRequestIdentity() method of the TeamFoundationIdentityService object:

    private static TeamFoundationRequestContext GetContext(DeploymentServiceHost deploymentServiceHost, Guid instanceId)
    {
      using (TeamFoundationRequestContext deploymentRequestContext = deploymentServiceHost.CreateSystemContext())
      {
        // Get the identity for the tf request context
        TeamFoundationIdentityService ims = deploymentRequestContext.GetService<TeamFoundationIdentityService>();
        TeamFoundationIdentity identity = ims.ReadRequestIdentity(deploymentRequestContext); // FAILS HERE
    
        // Get the tf request context
        TeamFoundationHostManagementService hostManagementService = deploymentRequestContext.GetService<TeamFoundationHostManagementService>();
        return hostManagementService.BeginUserRequest(deploymentRequestContext, instanceId, identity.Descriptor);
      }
    }

    AccessMappingNotRegisteredException

    TF205034: The "AccessMapping" class has an unregistered value for the "Moniker" property. The following value has not been registered: ServerAccessMapping.

    Here is the full stacktrace is that is helpful:

       at Microsoft.TeamFoundation.Framework.Server.TeamFoundationLocationService.GetServerAccessMapping(TeamFoundationRequestContext requestContext, Guid serviceOwner)
       at Microsoft.TeamFoundation.Framework.Server.TeamFoundationRequestContext.GetClient[T]()
       at Microsoft.VisualStudio.Services.Identity.FrameworkIdentityStore.GetHttpClient(TeamFoundationRequestContext context)
       at Microsoft.VisualStudio.Services.Identity.FrameworkIdentityStore.ReadIdentities(TeamFoundationRequestContext requestContext, IdentityDomain hostDomain, IList`1 descriptors, QueryMembership queryMembership, IEnumerable`1 propertyNameFilters, Boolean includeRestrictedVisibility)
       at Microsoft.VisualStudio.Services.Identity.FrameworkIdentityService.ReadIdentities(TeamFoundationRequestContext requestContext, IList`1 descriptors, QueryMembership queryMembership, IEnumerable`1 propertyNameFilters, Boolean includeRestrictedVisibility)
       at Microsoft.VisualStudio.Services.Identity.FrameworkIdentityService.ReadRequestIdentity(TeamFoundationRequestContext requestContext)
       at Microsoft.TeamFoundation.Server.Core.TeamFoundationIdentityService.ReadRequestIdentity(TeamFoundationRequestContext requestContext)
       at FeatureEnablement.Program.GetContext(DeploymentServiceHost deploymentServiceHost, Guid instanceId) in c:\Dev\FeatureEnablement\Program.cs
       at FeatureEnablement.Program.Main(String[] args) in c:\Dev\FeatureEnablement\Program.cs

    The MSDN documentation for TeamFoundationLocationService.GetServerAccessMapping() is interesting... but I'm not sure how that helps me. :)

    Appreciate any further troubleshooting suggestions.

    Thanks!
    Matt



    • Edited by Matt Ring Tuesday, November 19, 2013 4:40 AM
    Tuesday, November 19, 2013 4:37 AM
  • Hi Matt,

    For security reasons the SQL connection string was removed from the registry service. Unfortunately this makes the code sample a bit harder since it cannot bootstrap the server OM (which requires a connection string) by using the client OM. If you have access to the Application Tier (AT) machine you can obtain the same connection string the AT/JobAgent uses to connect to SQL by looking in the web.config file located at: %ProgramFiles%\Microsoft Team Foundation Server 12.0\Application Tier\Web Services\web.config (look under <appSettings><add key="applicationDatabase" value="<connection string you need>" />)

    The other thing you will need to do to prevent the AccessMapping error -- as strange as it sounds -- is to drop Microsoft.VisualStudio.Services.Identity.dll into the bin path of the application (the same place where you've placed Microsoft.TeamFoundation.Framework.Server.dll should work). You can grab this file from %ProgramFiles%\Microsoft Team Foundation Server 12.0\Application Tier\Web Services\bin or %ProgramFiles%\Microsoft Team Foundation Server 12.0\Application Tier\TFSJobAgent.

    The error message (relating to AccessMapping) is obviously not helpful in diagnosing the problem, we will be fixing this in the next update. The problem is that requestContext.GetService<T>() is trying to load a specific implementation called PlatformIdentityService (which resides in Microsoft.VisualStudio.Services.Identity.dll). When it cannot find this type because the assembly is not in the bin or probing path, it defaults to FrameworkIdentityService (which is located in Microsoft.TeamFoundation.Framework.Server.dll). Unfortunately this implementation is not suitable for your environment -- the OM should have  thrown that it could not load the specified type from the specified assembly and the exception message would have been reasonably clear as to what you would need to do. This will be fixed in the next update.

    Please let me know if this works and if you have any other questions -- hopefully you won't hit anymore hurdles with this!


    Russell Tolley

    Tuesday, November 19, 2013 4:34 PM
  • Hi Matt,

    You can find an updated version of the tool here: https://features4tfs.codeplex.com/
    It should work with TFS 2013 server.

    Thanks,
    Oleg
    Wednesday, November 27, 2013 12:22 AM
  • @Russell - Thanks for the input. I was able to get my copy working with those last few suggestions.

    @Oleg - Thanks for packaging this to be re-usable!

    Wednesday, November 27, 2013 3:42 PM
  • Hi Russel,

    your hint with the access mapping works quiet well. Saved my day!

    Kind regards,

    Fabian

    Tuesday, April 08, 2014 3:07 PM