nkdAgility / azure-devops-migration-tools

Azure DevOps Migration Tools allow you to migrate Teams, Backlogs, Tasks, Test Cases, and Plans & Suits from one Project to another in Azure DevOps / TFS both within the same Organisation, and between Organisations.
https://marketplace.visualstudio.com/items?itemName=nkdagility.vsts-sync-migration
MIT License
475 stars 322 forks source link

FieldValueMapConfig does not work with _Kanban.Column fields #831

Closed david5MX53G closed 3 years ago

david5MX53G commented 3 years ago

I have entered the following item in my JSON config:

{
  "$type": "FieldValueMapConfig",
  "WorkItemTypeName": "*",
  "sourceField": "WEF_0EB2D7D633B04DFAA8BB93EC6CC32B8D_Kanban.Column",
  "targetField": "WEF_11706B94CD66464AA34EBEEB073F6D47_Kanban.Column",
  "defaultValue": "New",
  "valueMapping": {
    "New": "New",
    "Waiting for Approval": "Active",
    "Approved for Development": "Active",
    "Analysis": "Active",
    "Development": "Active",
    "User Acceptance": "Active",
    "Ready for Deployment": "Resolved",
    "Closed": "Closed"
  }
}

Nevertheless, every item I migrate ends up in the "New" column of the target instance. I expected the Tool to use the mappings I entered above. Am I missing something?

david5MX53G commented 3 years ago

I got it working. I think the problem had something to do with the order in which I entered my maps, so I'm posting my entire config here for the reference of anyone coming in my humble footsteps.

{
  "ChangeSetMappingFile": null,
  "Source": {
    "$type": "TfsTeamProjectConfig",
    "Collection": "https://dev.azure.com/AcmeCoinc/",
    "Project": "AcmeCo",
    "ReflectedWorkItemIDFieldName": "Custom.ReflectedWorkItemId",
    "AllowCrossProjectLinking": false,
    "AuthenticationMode": "Prompt",
    "PersonalAccessToken": "",
    "LanguageMaps": {
      "AreaPath": "Area",
      "IterationPath": "Iteration"
    }
  },
  "Target": {
    "$type": "TfsTeamProjectConfig",
    "Collection": "https://dev.azure.com/AcmeCo/",
    "Project": "Global Operations",
    "ReflectedWorkItemIDFieldName": "Custom.ReflectedWorkItemId",
    "AllowCrossProjectLinking": false,
    "AuthenticationMode": "Prompt",
    "PersonalAccessToken": "",
    "LanguageMaps": {
      "AreaPath": "Area",
      "IterationPath": "Iteration"
    }
  },
  "FieldMaps": [
    {
      "$type": "FieldValueMapConfig",
      "WorkItemTypeName": "*",
      "sourceField": "System.AreaPath",
      "targetField": "System.AreaPath",
      "defaultValue": "Global Operations",
      "valueMapping": {
        "AcmeCo\\BusinessApps": "Global Operations\\Enterprise Applications\\CRM"
      }
    },
    {
      "$type": "FieldValueMapConfig",
      "WorkItemTypeName": "*",
      "sourceField": "System.IterationPath",
      "targetField": "System.IterationPath",
      "defaultValue": "Global Operations",
      "valueMapping": {
        "AcmeCo\\2020-01": "Global Operations\\2020\\01",
        "AcmeCo\\2020-02": "Global Operations\\2020\\02",
        "AcmeCo\\2020-03": "Global Operations\\2020\\03",
        "AcmeCo\\2020-04": "Global Operations\\2020\\04",
        "AcmeCo\\2020-05": "Global Operations\\2020\\05",
        "AcmeCo\\2020-06": "Global Operations\\2020\\06",
        "AcmeCo\\2020-07": "Global Operations\\2020\\07",
        "AcmeCo\\2020-08": "Global Operations\\2020\\08",
        "AcmeCo\\2020-09": "Global Operations\\2020\\09",
        "AcmeCo\\2020-10": "Global Operations\\2020\\10",
        "AcmeCo\\2020-11": "Global Operations\\2020\\11",
        "AcmeCo\\2020-12": "Global Operations\\2020\\12",
        "AcmeCo\\2021-01": "Global Operations\\2021\\01"
      }
    },
    {
      "$type": "FieldValueMapConfig",
      "WorkItemTypeName": "*",
      "sourceField": "WEF_0EB2D7D633B04DFAA8BB93EC6CC32B8D_Kanban.Column",
      "targetField": "WEF_11706B94CD66464AA34EBEEB073F6D47_Kanban.Column",
      "defaultValue": "New",
      "valueMapping": {
        "New": "New",
        "Waiting for Approval": "Active",
        "Approved for Development": "Active",
        "Analysis": "Active",
        "Development": "Active",
        "User Acceptance": "Active",
        "Ready for Deployment": "Resolved",
        "Closed": "Closed"
      }
    },
    {
      "$type": "FieldBlankMapConfig",
      "WorkItemTypeName": "*",
      "targetField": "TfsMigrationTool.ReflectedWorkItemId"
    },
    {
      "$type": "FieldtoTagMapConfig",
      "WorkItemTypeName": "*",
      "sourceField": "System.BoardColumn",
      "formatExpression": "BoardColumn:{0}"
    },
    {
      "$type": "FieldtoFieldMapConfig",
      "WorkItemTypeName": "*",
      "sourceField": "Microsoft.VSTS.Common.BacklogPriority",
      "targetField": "Microsoft.VSTS.Common.StackRank",
      "defaultValue": null
    },
    {
      "$type": "FieldtoTagMapConfig",
      "WorkItemTypeName": "*",
      "sourceField": "System.State",
      "formatExpression": "ScrumState:{0}"
    },
    {
      "$type": "FieldtoTagMapConfig",
      "WorkItemTypeName": "*",
      "sourceField": "System.BoardLane",
      "formatExpression": "ScrumLane:{0}"
    },
    {
      "$type": "RegexFieldMapConfig",
      "WorkItemTypeName": "*",
      "sourceField": "COMPANY.PRODUCT.Release",
      "targetField": "COMPANY.DEVISION.MinorReleaseVersion",
      "pattern": "PRODUCT \\d{4}.(\\d{1})",
      "replacement": "$1"
    },
    {
      "$type": "FieldValuetoTagMapConfig",
      "WorkItemTypeName": "*",
      "sourceField": "Microsoft.VSTS.CMMI.Blocked",
      "pattern": "Yes",
      "formatExpression": "Blocked"
    },
    {
      "$type": "TreeToTagMapConfig",
      "WorkItemTypeName": "*",
      "toSkip": 3,
      "timeTravel": 1
    }
  ],
  "GitRepoMapping": null,
  "LogLevel": "Information",
  "Processors": [
    {
      "$type": "WorkItemMigrationConfig",
      "Enabled": true,
      "ReplayRevisions": true,
      "PrefixProjectToNodes": false,
      "UpdateCreatedDate": true,
      "UpdateCreatedBy": true,
      "BuildFieldTable": false,
      "AppendMigrationToolSignatureFooter": false,
      "WIQLQueryBit": "AND [System.Id]=6100 AND [Custom.ReflectedWorkItemId] = ''  AND [System.WorkItemType] IN ('Epic', 'Feature', 'Task', 'User Story', 'Bug')",
      "WIQLOrderBit": "[System.ChangedDate] desc",
      "LinkMigration": true,
      "AttachmentMigration": true,
      "AttachmentWorkingPath": "c:\\temp\\WorkItemAttachmentWorkingFolder\\",
      "FixHtmlAttachmentLinks": false,
      "SkipToFinalRevisedWorkItemType": true,
      "WorkItemCreateRetryLimit": 5,
      "FilterWorkItemsThatAlreadyExistInTarget": true,
      "PauseAfterEachWorkItem": false,
      "AttachmentMaxSize": 480000000,
      "CollapseRevisions": false,
      "LinkMigrationSaveEachAsAdded": false,
      "GenerateMigrationComment": true,
      "NodeBasePaths": ["DUMMY VALUE"],
      "WorkItemIDs": null
    }
  ],
  "Version": "11.9",
  "workaroundForQuerySOAPBugEnabled": false,
  "WorkItemTypeDefinition": {
    "sourceWorkItemTypeName": "targetWorkItemTypeName"
  }
}

Note: the values "WEF_0EB2D7D633B04DFAA8BB93EC6CC32B8D_Kanban" and "WEF_11706B94CD66464AA34EBEEB073F6D47_Kanban" are unique to every board, so I had to use the "Get work item" GET request to find them. That GET request is described in the Azure DevOps API docs.

david5MX53G commented 3 years ago

It turns out the Tool was ignoring my FieldValueMapConfig for Kanban Columns after all; instead, the tool uses the "State mapping" settings of the target board to assign columns for the incoming work items. Thus, I was able to adjust which columns my work items arrived in by changing the state mapping of the source board.