functions/invoke-adocustomfieldmigration.ps1
<# .SYNOPSIS Migrate custom work item fields from source to target organization. .DESCRIPTION This function migrates custom work item fields (those starting with 'Custom.') from a source organization to a target organization. .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 TargetProcessName The name of the target process in the target organization. .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' # Obtain source project & process to derive target process name $sourceProjectMeta = (Get-ADOProjectList -Organization $sourceOrg -Token $sourceToken -ApiVersion $apiVersion -StateFilter All) | Where-Object name -eq $sourceProjectName $sourceProject = Get-ADOProject -Organization $sourceOrg -Token $sourceToken -ProjectId $sourceProjectMeta.id -IncludeCapabilities -ApiVersion $apiVersion $proc = Invoke-ADOProcessMigration -SourceOrganization $sourceOrg -TargetOrganization $targetOrg -SourceToken $sourceToken -TargetToken $targetToken -SourceProject $sourceProject -ApiVersion $apiVersion $targetProcessName = $proc.TargetProcess.name Invoke-ADOCustomFieldMigration -SourceOrganization $sourceOrg -TargetOrganization $targetOrg ` -SourceToken $sourceToken -TargetToken $targetToken -TargetProcessName $targetProcessName -ApiVersion $apiVersion # Copies all Custom.* fields and ensures tracking field Custom.SourceWorkitemId exists. .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-ADOCustomFieldMigration { [CmdletBinding()] param( [Parameter(Mandatory)][string]$SourceOrganization, [Parameter(Mandatory)][string]$TargetOrganization, [Parameter(Mandatory)][string]$SourceToken, [Parameter(Mandatory)][string]$TargetToken, [Parameter(Mandatory)][string]$TargetProcessName, [Parameter(Mandatory)][string]$ApiVersion ) Convert-FSCPSTextToAscii -Text "Migrate wit fields.." -Font "Standard" Write-PSFMessage -Level Host -Message "Fetching custom work item fields from source organization '$SourceOrganization'." $sourceFields = (Get-ADOWitFieldList -Organization $SourceOrganization -Token $SourceToken -Expand 'extensionFields' -ApiVersion $ApiVersion).Where({$_.referenceName.StartsWith('Custom.')}) Write-PSFMessage -Level Host -Message "Found $($sourceFields.Count) custom fields in source organization." Write-PSFMessage -Level Host -Message "Fetching custom work item fields from target organization '$TargetOrganization'." $targetFields = (Get-ADOWitFieldList -Organization $TargetOrganization -Token $TargetToken -Expand 'extensionFields' -ApiVersion $ApiVersion).Where({$_.referenceName.StartsWith('Custom.')}) Write-PSFMessage -Level Host -Message "Found $($targetFields.Count) custom fields in target organization." foreach ($field in $sourceFields) { if (-not ($targetFields.Where({$_.name -eq $field.name}))) { Write-PSFMessage -Level Host -Message "Custom field '$($field.name)' does not exist in target organization. Adding it." $fullField = Get-ADOWitField -Organization $SourceOrganization -Token $SourceToken -FieldNameOrRefName $field.referenceName -ApiVersion $ApiVersion $body = @{ name=$fullField.name; referenceName=$fullField.referenceName; description=$fullField.description; type=$fullField.type; usage=$fullField.usage; readOnly=$fullField.readOnly; isPicklist=$fullField.isPicklist; isPicklistSuggested=$fullField.isPicklistSuggested; isIdentity=$fullField.isIdentity; isQueryable=$fullField.isQueryable; isLocked=$fullField.isLocked; canSortBy=$fullField.canSortBy; supportedOperations=$fullField.supportedOperations } | ConvertTo-Json -Depth 10 Write-PSFMessage -Level Verbose -Message "Adding custom field '$($fullField.name)' to target process '$TargetProcessName' with body: $body" $null = Add-ADOWitField -Organization $TargetOrganization -Token $TargetToken -Body $body -ApiVersion $ApiVersion } else { Write-PSFMessage -Level Host -Message "Custom field '$($field.name)' already exists in target organization. Skipping." } } # Ensure SourceWorkitemId tracking field $trackingName = 'SourceWorkitemId' $trackingRef = "Custom.$trackingName" if (-not ($targetFields.Where({$_.referenceName -eq $trackingRef}))) { $body = @{ name=$trackingName; referenceName=$trackingRef; description=''; type='string'; usage='workItem'; readOnly=$false; isQueryable=$true; isLocked=$false; canSortBy=$true } | ConvertTo-Json -Depth 10 Write-PSFMessage -Level Verbose -Message "Ensuring tracking field '$trackingRef' in target organization." Add-ADOWitField -Organization $TargetOrganization -Token $TargetToken -Body $body -ApiVersion $ApiVersion -ErrorAction SilentlyContinue -WarningAction SilentlyContinue | Out-Null } } |