azure-devops-migration-tools icon indicating copy to clipboard operation
azure-devops-migration-tools copied to clipboard

[Bug]: Assignee (AssignedTo, ResolvedBy, ClosedBy) is set to migration user

Open JDAVF opened this issue 6 months ago • 4 comments

Version

  • [x] I confirm that I am using the latest version

Source Version

Azure DevOps Server 2022

Target Version

Azure DevOps Service

Relevant configuration

// We disabled that because the usernames have chars like ö in it.
      "StringManipulatorTool": {
        "Enabled": false
      },
      "TfsUserMappingTool": {
        "Enabled": true,
        "UserMappingFile": "D:\\Temp\\userExport.json",
        "MatchUsersByEmail": true,
        "IdentityFieldsToCheck": [
          "System.AssignedTo",
          "System.ChangedBy",
          "System.CreatedBy",
          "Microsoft.VSTS.Common.ActivatedBy",
          "Microsoft.VSTS.Common.ResolvedBy",
          "Microsoft.VSTS.Common.ClosedBy"
        ]
      }



    "Processors": [
      {
        "ProcessorType": "TfsWorkItemMigrationProcessor",
        "Enabled": true,
        "UpdateCreatedDate": true,
        "UpdateCreatedBy": true,
        //"WIQLQuery": "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.Id] > 9000 and [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc",
        "WIQLQuery": "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject and [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc",
        "FixHtmlAttachmentLinks": true,
        "WorkItemCreateRetryLimit": 5,
        "FilterWorkItemsThatAlreadyExistInTarget": false,
        "PauseAfterEachWorkItem": false,
        "AttachRevisionHistory": false,
        "GenerateMigrationComment": false,
        "SourceName": "Source",
        "TargetName": "Target",
        "WorkItemIDs": [],
        "MaxGracefulFailures": 4,
        "SkipRevisionWithInvalidIterationPath": false,
        "SkipRevisionWithInvalidAreaPath": false
      },

Relevant log output

Variant 1:
[11:39:09 DBG] [16.2.9] ###################################################################################### 
[11:39:09 DBG] [16.2.9] ProcessWorkItem: 9005 
[11:39:09 DBG] [16.2.9] ###################################################################################### 
[11:39:09 DBG] [16.2.9] WorkItemQuery: ===========GetWorkItems============= 
..
[11:39:09 DBG] [16.2.9] Query sent 
[11:39:09 INF] [16.2.9] [                Task][Complete:   472/8132][sid:  9005|Rev:  8][tid:  null | Work Item has 8 revisions and revision migration is set to true 
[11:39:09 DBG] [16.2.9] TfsRevisionManagerTool::EnforceDatesMustBeIncreasing 
[11:39:09 INF] [16.2.9] Source: Found 8 revisions to migrate on  Work item:Source 
[11:39:09 DBG] [16.2.9] Source: RevisionsToMigrate:---------------------------------------------------- 
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:0 - Number:1 - ChangedDate:"2025-06-04T14:03:12.3400000+02:00" 
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:1 - Number:2 - ChangedDate:"2025-06-04T14:03:37.0700000+02:00" 
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:2 - Number:3 - ChangedDate:"2025-06-05T08:49:17.7870000+02:00" 
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:3 - Number:4 - ChangedDate:"2025-06-05T08:49:20.7470000+02:00" 
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:4 - Number:5 - ChangedDate:"2025-06-05T09:00:04.3570000+02:00" 
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:5 - Number:6 - ChangedDate:"2025-06-05T09:05:43.0730000+02:00" 
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:6 - Number:7 - ChangedDate:"2025-06-10T17:03:47.3100000+02:00" 
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:7 - Number:8 - ChangedDate:"2025-06-13T17:17:26.6230000+02:00" 
[11:39:09 DBG] [16.2.9] Source: RevisionsToMigrate:---------------------------------------------------- 
[11:39:09 DBG] [16.2.9] Target: RevisionsToMigrate: No revisions to migrate 
[11:39:09 DBG] [16.2.9] TfsRevisionManagerTool::GetRevisionsToMigrate: Raw Source Has 8 revisions 
[11:39:09 DBG] [16.2.9] TfsRevisionManagerTool::GetRevisionsToMigrate::RemoveRevisionsAlreadyOnTarget Target is null 
[11:39:09 INF] [16.2.9] Source: Found 8 revisions to migrate on  Work item:Source 
[11:39:09 DBG] [16.2.9] Source: RevisionsToMigrate:---------------------------------------------------- 
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:0 - Number:1 - ChangedDate:"2025-06-04T14:03:12.3400000+02:00" 
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:1 - Number:2 - ChangedDate:"2025-06-04T14:03:37.0700000+02:00" 
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:2 - Number:3 - ChangedDate:"2025-06-05T08:49:17.7870000+02:00" 
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:3 - Number:4 - ChangedDate:"2025-06-05T08:49:20.7470000+02:00" 
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:4 - Number:5 - ChangedDate:"2025-06-05T09:00:04.3570000+02:00" 
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:5 - Number:6 - ChangedDate:"2025-06-05T09:05:43.0730000+02:00" 
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:6 - Number:7 - ChangedDate:"2025-06-10T17:03:47.3100000+02:00" 
[11:39:09 DBG] [16.2.9] RevisionsToMigrate: Index:7 - Number:8 - ChangedDate:"2025-06-13T17:17:26.6230000+02:00" 
[11:39:09 DBG] [16.2.9] Source: RevisionsToMigrate:---------------------------------------------------- 
..
[11:39:09 DBG] [16.2.9] TfsUserMappingTool::MapUserIdentityField [ReferenceName|Microsoft.VSTS.Common.ClosedBy] 
[11:39:09 DBG] [16.2.9] TfsUserMappingTool::MapUserIdentityField [ReferenceName|Microsoft.VSTS.Common.ActivatedBy] 
[11:39:09 DBG] [16.2.9] TfsUserMappingTool::MapUserIdentityField [ReferenceName|System.CreatedBy] 
[11:39:09 DBG] [16.2.9] PopulateWorkItem:FieldUpdate: System.CreatedBy | Source:ORIGINALUSER Target:CLOUDUSER
[11:39:09 DBG] [16.2.9] StringManipulatorTool::ProcessString 
[11:39:09 DBG] [16.2.9] StringManipulatorTool::ProcessString::Disabled 
[11:39:09 DBG] [16.2.9] PopulateWorkItem:FieldUpdate: System.CreatedDate | Source:"2025-06-04T14:03:12.3400000+02:00" Target:"2025-06-04T14:03:12.3400000+02:00" 
[11:39:09 DBG] [16.2.9] TfsUserMappingTool::MapUserIdentityField [ReferenceName|System.AssignedTo] 
[11:39:09 DBG] [16.2.9] PopulateWorkItem:FieldUpdate: System.AssignedTo | Source: Target:MIGRATIONUSER

[11:39:09 DBG] [16.2.9] TfsExtensions::SaveToAzureDevOps 
[11:39:09 DBG] [16.2.9] TfsExtensions::SaveToAzureDevOps: ChangedBy: ORGINALUSER, AuthorisedBy: MIGRATIONUSER
[11:39:10 INF] [16.2.9] [                Task][Complete:   472/8132][sid:  9005|Rev:  8][tid:  null |  Saved TargetWorkItem 551852. Replayed revision 1 of 8 

Variant 2:
[11:39:21 INF] [16.2.9] [                Task][Complete:   472/8132][sid:  9005|Rev:  8][tid:  null |  Processing Revision [7] 
[11:39:21 DBG] [16.2.9] TfsUserMappingTool::MapUserIdentityField [ReferenceName|Microsoft.VSTS.Common.ClosedBy] 
[11:39:21 DBG] [16.2.9] PopulateWorkItem:FieldUpdate: Microsoft.VSTS.Common.ClosedBy | Source:ORGINALUSER Target:MIGRATIONUSER

What happened?

The migration is working successfully, but assignee are changed

  1. Work item (bug, task) is created Bug-Variant 1: "Assigned To" is empy in the source, but is set to migration user
  2. Work item is closed: ClosedBy is set to correct user
  3. Work item is updated (attachment or link added) Bug-Variant 2: ClosedBy is changed from the correct user to migration user

Debug in Visual Studio

  • [x] Visual Studio Debug

JDAVF avatar Jun 30 '25 10:06 JDAVF

This usually only happens when the user running the migration does not have "on behalf of" permission...

MrHinsh avatar Jul 03 '25 09:07 MrHinsh

@MrHinsh but it works fine to set it: Rev 1: Work item is created and Assignee is set to MIGRATIONUSER. History entry: "ORIGINALCREATOR (via MIGRATIONUSER) created the XY I suspect that an empty assignee is set to the default -> creator (Bug Variant 1) Rev 2: Work item is assigned to a normal person (change of state from Proposed to active) Rev 3: ORIGINALUSER changes state from active to closed -> Resolved by is set to MIGRATIONUSER and Closed By to ORGINALUSER Rev 4: "Migration (via MIGRATIONUSER)" -> Closed By is changed from ORGINALUSER to MIGRATIONUSER and Parent link is added.

So, I assume these are multiple bugs, but I fear "on behalf of" is not the problem.

JDAVF avatar Jul 03 '25 10:07 JDAVF

Still happens with the latest version 16.3.1

JDAVF avatar Sep 21 '25 18:09 JDAVF

Hi @JDAVF and @MrHinsh,

Thanks for the detailed reproduction steps and logs. From what I understand, the behavior seems to be caused by how TfsUserMappingTool interacts with TfsWorkItemMigrationProcessor when an empty assignee or state changes occur. Specifically:

  • Bug Variant 1: An empty "Assigned To" gets set to MIGRATIONUSER by default.
  • Bug Variant 2: The "Closed By" field changes from the correct user to MIGRATIONUSER during state transitions.

It looks like multiple related issues are occurring rather than a single cause, and the "on behalf of" permission explanation may not fully address it.

I’d be happy to help investigate this further. My plan would be to:

  • Examine the user mapping logic in TfsUserMappingTool and its integration with work item revisions.
  • Identify why certain revisions or state changes override the mapped user fields.
  • Propose a fix or at least suggest configuration adjustments to preserve the original user assignments.

Could I get assigned to this issue so I can start exploring it? I’ll provide updates as I make progress.

krishnaharshap avatar Oct 15 '25 07:10 krishnaharshap