functions/invoke-adoworkitemdatamigration.ps1


<#
    .SYNOPSIS
        Migrates work items from source project into target project using tracking field.
    .DESCRIPTION
        This function migrates work items from a source Azure DevOps project to a target Azure DevOps project.
        It uses a tracking field to associate work items in the source project with their counterparts in the target project.
         
    .PARAMETER SourceOrganization
        The name of the source Azure DevOps organization.
         
    .PARAMETER TargetOrganization
        The name of the target Azure DevOps organization.
         
    .PARAMETER SourceToken
        The authentication token for accessing the source Azure DevOps organization.
         
    .PARAMETER TargetToken
        The authentication token for accessing the target Azure DevOps organization.
         
    .PARAMETER SourceProjectName
        The name of the source Azure DevOps project.
         
    .PARAMETER TargetProjectName
        The name of the target Azure DevOps project.
         
    .PARAMETER ApiVersion
        The version of the Azure DevOps REST API to use.
         
    .EXAMPLE
        $apiVersion = '7.1'
        $sourceOrg = 'srcOrg'
        $targetOrg = 'tgtOrg'
        $sourceToken = 'pat-src'
        $targetToken = 'pat-tgt'
        $sourceProjectName = 'Sample'
        $targetProjectName = 'MigratedProject'
         
        Invoke-ADOWorkItemDataMigration -SourceOrganization $sourceOrg -TargetOrganization $targetOrg `
            -SourceToken $sourceToken -TargetToken $targetToken `
            -SourceProjectName $sourceProjectName -TargetProjectName $targetProjectName -ApiVersion $apiVersion
        # Migrates work items not yet copied (no tracking field value present in target).
    .NOTES
        This function is part of the ADO Tools module and adheres to the conventions used in the module for logging, error handling, and API interaction.
        Author: Oleksandr Nikolaiev (@onikolaiev)
#>

function Invoke-ADOWorkItemDataMigration {
    [CmdletBinding()] param(
        [Parameter(Mandatory)][string]$SourceOrganization,
        [Parameter(Mandatory)][string]$TargetOrganization,
        [Parameter(Mandatory)][string]$SourceToken,
        [Parameter(Mandatory)][string]$TargetToken,
        [Parameter(Mandatory)][string]$SourceProjectName,
        [Parameter(Mandatory)][string]$TargetProjectName,
        [Parameter(Mandatory)][string]$ApiVersion
    )
    Convert-FSCPSTextToAscii -Text "Migrate work items.." -Font "Standard"
    Write-PSFMessage -Level Host -Message "Starting work item migration from '$SourceProjectName' to '$TargetProjectName'."
    $sourceItems = Get-ADOSourceWorkItemsList -SourceOrganization $SourceOrganization -SourceProjectName $SourceProjectName -SourceToken $SourceToken -ApiVersion $ApiVersion
    Write-PSFMessage -Level Verbose -Message "Loaded $($sourceItems.Count) source work items."

    # Prefetch target items ONCE to avoid O(n^2) calls; build map of already migrated Source IDs
    $targetMap = @{}
    $existingTargetItems = Get-ADOSourceWorkItemsList -SourceOrganization $TargetOrganization -SourceProjectName $TargetProjectName -SourceToken $TargetToken -Fields @('System.Id','System.Title','System.Description','System.WorkItemType','System.State','System.Parent','Custom.SourceWorkitemId') -ApiVersion $ApiVersion
    if ($existingTargetItems) {
        foreach ($t in $existingTargetItems) {
            $srcId = $t.'Custom.SourceWorkitemId'
            if ($srcId -and -not $targetMap.ContainsKey($srcId)) { $targetMap[$srcId] = $t.Url }
        }
    }
    $initialMapped = $targetMap.Count
    Write-PSFMessage -Level Verbose -Message "Found $initialMapped existing mapped work items in target (tracking field present)."

    $processed = 0
    foreach ($item in $sourceItems) {
        $processed++
        if ($targetMap.ContainsKey($item.'System.Id')) { continue }
        Invoke-ADOWorkItemsProcessing -SourceWorkItem $item -SourceOrganization $SourceOrganization -SourceProjectName $SourceProjectName -SourceToken $SourceToken -TargetOrganization $TargetOrganization -TargetProjectName $TargetProjectName -TargetToken $TargetToken -TargetWorkItemList ([ref]$targetMap) -ApiVersion $ApiVersion
    }
    $totalMapped = $targetMap.Count
    $createdThisRun = $totalMapped - $initialMapped
    Write-PSFMessage -Level Host -Message "Completed work item migration. Total mapped: $totalMapped (created this run: $createdThisRun, pre-existing: $initialMapped)."
}