Public/WorkItemTracking/Export-WorkItemLinkInventory.ps1
|
function Export-WorkItemLinkInventory { <# .SYNOPSIS Writes a file recording every work item link of the given link types, before they are deleted. .DESCRIPTION Deleting a custom link type with witadmin deletelinktype also deletes every link of that type, and the links cannot be recovered afterwards. Run this first: it captures the full relationship set so the links can be reviewed with the customer, re-created as related links after the migration, or simply evidenced as "this is what was lost". Two files are written from the same data: a .json record carrying the collection, the link types covered and every link (the restoration source of truth), and a sibling .csv of the same rows for reading and sharing. A file is written even when there are no links, so a runbook step always leaves proof that the check ran. .PARAMETER ReferenceName Link types to record. Omit to record every custom link type in the collection. .PARAMETER Project Scope the query to one team project. Use when a collection-wide query trips the WIQL limit. .PARAMETER Path Destination .json file. Defaults to WorkItemLinks.json in today's export snapshot for the collection (see New-ExportSnapshot), which needs an initialised workspace. .PARAMETER Pat Personal access token. Omit to authenticate as the current Windows identity. .EXAMPLE Export-WorkItemLinkInventory -Collection $collection .EXAMPLE Export-WorkItemLinkInventory -Collection $collection -ReferenceName 'Custom.Affects' -Path 'C:\work\affects-links.json' #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Collection, [string[]]$ReferenceName, [string]$Project, [string]$Path, [string]$Pat, [switch]$UseDefaultCredentials, [string]$ApiVersion = '5.0' ) Write-FixStep "Recording work item links before custom link types are deleted" $types = @( if ($ReferenceName) { foreach ($name in $ReferenceName) { Get-WorkItemLinkType -Collection $Collection -UseDefaultCredentials:$UseDefaultCredentials -ReferenceName $name -Pat $Pat -ApiVersion $ApiVersion } } else { Get-WorkItemLinkType -Collection $Collection -UseDefaultCredentials:$UseDefaultCredentials -CustomOnly -Pat $Pat -ApiVersion $ApiVersion } ) $links = @(Get-WorkItemLink -Collection $Collection -UseDefaultCredentials:$UseDefaultCredentials -ReferenceName $ReferenceName -Project $Project -Pat $Pat -ApiVersion $ApiVersion) if (-not $Path) { $source = ([uri]$Collection).Segments[-1].Trim('/') $snapshot = New-ExportSnapshot -Source $source $name = if ($ReferenceName -and $ReferenceName.Count -eq 1) { "WorkItemLinks.$($ReferenceName[0]).json" } else { 'WorkItemLinks.json' } $Path = Join-Path $snapshot.FullName (Join-Path 'json' $name) } $folder = Split-Path -Parent $Path if ($folder -and -not (Test-Path -LiteralPath $folder)) { New-Item -Path $folder -ItemType Directory -Force | Out-Null } $csvPath = [System.IO.Path]::ChangeExtension($Path, '.csv') $record = [ordered]@{ collection = $Collection project = $Project exportedUtc = (Get-Date).ToUniversalTime().ToString('o') linkTypes = @($types | Select-Object Name, ReferenceName, Topology, IsCustom, IsReverse) linkCount = $links.Count links = $links } $record | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath $Path -Encoding utf8 $links | Export-Csv -LiteralPath $csvPath -NoTypeInformation -Encoding utf8 Write-FixStep " wrote $($links.Count) link(s) to $Path" Write-FixStep " wrote $csvPath" if ($links.Count -eq 0) { Write-FixStep " no links of these types exist - deleting them loses no relationships" } return [pscustomobject]@{ Collection = $Collection LinkTypes = @($types.ReferenceName) LinkCount = $links.Count JsonPath = $Path CsvPath = $csvPath } } |