Skip to content

Instantly share code, notes, and snippets.

@morcibacsi
Forked from flcdrg/CreateVSTSItems.ps1
Last active January 18, 2021 08:29
Show Gist options
  • Save morcibacsi/5fff0698275eb64e8455ac121e0d8d12 to your computer and use it in GitHub Desktop.
Save morcibacsi/5fff0698275eb64e8455ac121e0d8d12 to your computer and use it in GitHub Desktop.
Migrate Redmine issues to TFS (Azure DevOps) work items
param(
$user, # "TFS username - doesn't need @domain suffix"
$token, # TFS personal access token
$fileToImport, # path to the .json file
$redmineIssueUrl # URL to legacy redmine server. eg. https://redmine/issues
)
# The following command gets the open issues through the Redmine API:
# For reference check: http://www.redmine.org/projects/redmine/wiki/Rest_Issues
# Sample call
# http://redmine/issues.json?offset=0&limit=100&sort=id
# Set these according to your instance:
$serverUrl = 'http://desktop-9a6epju'
$collectionName = 'DefaultCollection'
$projectName = 'MyProject'
$apiVersion = '4.1' #check: https://docs.microsoft.com/en-us/azure/devops/integrate/concepts/rest-api-versioning?view=azure-devops
$ErrorActionPreference = "Stop"
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token)))
$headers = @{Authorization=("Basic {0}" -f $base64AuthInfo)}
# Get users (if you need to map between usernames)
# $result=Invoke-RestMethod -Method GET -Uri "https://$($vstsAccountName).vsaex.visualstudio.com/_apis/userentitlements?api-version=5.0-preview.1" -Headers $headers
#
# $userMap = @{
# }
#
# $result.value | ForEach-Object {
# $userMap.Add($_.user.displayName, $_.user.displayName)
# }
$priorityMap = @{
'High' = '1'
'Normal' = '2'
'Low' = '3'
}
$jsonText = Get-Content $fileToImport -Encoding UTF8 | Out-String
$json = $jsonText | ConvertFrom-Json
$issues = $json.issues
foreach ($item in $issues) {
switch ($item.tracker.name) {
"Bug" {
$workItemType = "Bug"
$descriptionField = "Microsoft.VSTS.TCM.ReproSteps"
}
"Enhancement" {
$workItemType = "Product Backlog Item"
$descriptionField = "System.Description"
}
"Feature" {
$workItemType = "Product Backlog Item"
$descriptionField = "System.Description"
}
"New request" {
$workItemType = "Product Backlog Item"
$descriptionField = "System.Description"
}
Default {
$workItemType = "Product Backlog Item"
$descriptionField = "System.Description"
#Write-Warning "Skipping $($item.Tracker)"
#continue;
}
}
$itemsubject = $item.subject
$subject = '{0} (#{1})' -f $itemSubject, $item.id
$description = '{0}' -f $item.description -replace '\r\n', '<br>' -replace "\t", " - "
$description = $description -replace '•', ''
$bodyArray = @(
@{
op = "add"
path = "/fields/System.Title"
value = $subject
}
@{
op = "add"
path = "/fields/$descriptionField"
value = $description
}
@{
op = "add"
path = "/relations/-"
value = @{
rel = "Hyperlink"
url = "$($redmineIssueUrl)/$($item.id)"
attributes = @{
comment = "Link to original ticket"
}
}
}
@{
op = "add"
path = "/fields/System.CreatedDate"
value = $item.created_on
}
#@{
# op = "add"
# path = "/fields/System.State"
# value = $item.status.name
#}
)
if ($item.assigned_to.name -ne "") {
$name = '{0}' -f $item.assigned_to.name
$bodyArray += @{
op = "add"
path = "/fields/System.AssignedTo"
value = $name
}
}
$body = $bodyArray | ConvertTo-Json -Depth 3
$uri = "$serverUrl/$collectionName/$projectName/_apis/wit/workitems/`$$($workItemType)?api-version=$apiVersion&bypassRules=true"
$body = [System.Text.Encoding]::UTF8.GetBytes($body)
Write-Host $uri
#Write-Host $body
$result = Invoke-RestMethod -Method POST -Uri $uri -Headers $headers -ContentType "application/json-patch+json" -Body $body
Write-Host "Created item $($result.id) from legacy ticket $($item.ID)"
}
@wpoely86
Copy link

I've mode a more extended version of this script (it also migrated attachments, parent-child relations, etc) at https://github.com/wpoely86/redmine-azure-devops-migrate

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment