Importing from JIRA to VSTS

In this post we’ll share our research on how to import your current project information from JIRA to VSTS.


There is a rising demand from our customers to sync/move project items from JIRA to VSTS in order to support a wide variety of integration scenarios. Capitalizing on how extensible both platforms are, in this post we will walk you through a step-by-step guide on how to create a simple tool to do so.


You'll need to meet the following prerequisites:


These steps were generated as part of our research and shared “as is”. You are encouraged to use and provide feedback; however, these steps and sample code are not supported, nor are any commitments made as to their longevity.

  1. Start by creating a simple console application.
  2. Add the following NuGet packages to connect to both repositories:
  3. Create a connection to the VSTS repository
    string vstsUrl = "https://{vstsAccount}";string vstsPAT = "{vstsPersonalAccessToken}";VssConnection connection = new VssConnection(new Uri(vstsUrl), new VssBasicCredential(string.Empty, vstsPAT));WorkItemTrackingHttpClient witClient = connection.GetClient<WorkItemTrackingHttpClient>();
    In this case we are connecting using a VSTS Personal Access Token.
  4. Create a connection to the JIRA repository
    string jUserID = "{jiraUserID}";string jPassword = "{jiraUserPassword}";string jUrl = "https://{jiraAccount}";Jira jiraConn = Jira.CreateRestClient(jUrl, jUserID, jPassword);
  5. Get the list of issues from JIRA (the list will contain all versions for every issue)
    IList issues = (from i in jiraConn.Issues.Queryable orderby i.Created select i).ToList(); If the repositories is big, consider fetching issues by slices.
  6. For each issue in the list...
    1. Ensure the imported iteration value exists.
      Get the existing iterations defined in VSTS
      WorkItemClassificationNode all = WorkItemTrackingHttpClient.GetClassificationNodeAsync(project, TreeStructureGroup.Iterations, null, 10).Result; Navigate in the existing iterations and if necessary, create new values:
      WorkItemClassificationNode workItemClassificationNode = witClient.CreateOrUpdateClassificationNodeAsync( new WorkItemClassificationNode() { Name = iterationName, }, projectName, TreeStructureGroup.Iterations, parentPath).Result; Consider creating a local cache of iteration defined in VSTS to avoid querying VSTS multiple times for the same values.
    2. Create the JSON necessary to create/update the WorkItem in VSTS
      JsonPatchDocument document = new JsonPatchDocument(); string title = String.Format ( "[{0}] [{1}] {2}" + , DateTime.Now.ToLongTimeString(), issue.Key, issue.Summary); title = title.Substring(0, Math.Min(title.Length, 128)); document.Add(new JsonPatchOperation { Operation = Operation.Add, Path = "/fields/System.Title", Value = title }); if (issue.Description != null) { document.Add(new JsonPatchOperation { Operation = Operation.Add, Path = "/fields/System.Description", Value = issue.Description }); } [...] This "mapping" process should take care of all the differences between the item definition in JIRA and in VSTS.
      In this case, for example, in VSTS the title property is created by concatenating the issue key with the issue summary value.
      To map identities from JIRA to VSTS use the EMail address defined in the identity.
      JiraUser user = jiraConn.Users.SearchUsersAsync(issue.Reporter).Result.FirstOrDefault(); if (user != null) document.Add(new JsonPatchOperation { Operation = Operation.Add, Path = "/fields/System.CreatedBy", Value = user.Email });
    3. Create/Update the workitem in VSTS
      [...]workItem = witClient.CreateWorkItemAsync(document, project, workItemType).Result;[...]
      [...] workItem = witClient.UpdateWorkItemAsync(document, id).Result; [...] This part of the process should take care of the project template state transitions and the work item type management.
      Additionally, consider maintaining a cache of mapping ids from both system to easily find existing items in the repositories.

Challenges not addressed in the sample:

  • Custom template mapping
  • Custom field mapping
  • Custom field value mapping

Authors: Hosam Kamel, João Paulo Rodrigues, Vladimir Gusarov