Skip to main content

Documentation Index

Fetch the complete documentation index at: https://ekso.dev/llms.txt

Use this file to discover all available pages before exploring further.

ekso migrate devops pulls work items, comments, attachments, links, and iterations from Azure DevOps (and on-prem Azure DevOps Server / TFS) into your Ekso tenant. Two commands — collect and apply. Read Migrate overview and Before you start first.

What gets imported

DevOps conceptEkso shape
Team ProjectDataContainer (Code = ProjectName, project GUID in Meta.devops_project_id)
Work Item (Bug, Task, User Story, Feature, Epic, Issue, …)DataItem
Discussion commentDataAnnotation
AttachmentDataFile (multipart upload)
Link (Parent, Child, Related, Successor, Predecessor, Tested By, …)DataLink
Iteration (Sprint)DataCycle
Area PathConfigLabel (prefix area:) + DataItem.Tags
TagConfigLabel
Identity (DevOps user)DataUser (matched or minted)
System.WorkItemTypeDataItem.Meta.devops_workitemtype
Microsoft.VSTS.Scheduling.StoryPointsDataItem.Field[] (via --field-map)
Custom field (Custom.<Name>)DataItem.Field[] (via --field-map) or Meta
Authoring fidelity preserved — every comment, every change is attributed to the original DevOps identity (matched by email).

What does NOT get imported

  • Revision history. Each work item has full edit history in DevOps; v1 imports only the current state. Re-evaluate if a customer needs SOX/audit migration.
  • State transition rules. The migrator snapshots the current state of each item; transition rules don’t transfer.
  • Build & release pipelines. Out of scope.
  • Repos / Git history. Out of scope — work items only.
  • Test plans / suites / runs. Test management is out of scope for v1.
  • Boards & queries (saved WIQL). Re-author on the Ekso side.
  • Service identities ([Project]\Project Collection Build Service, etc.) are filtered at collect — they’re not real users.

Before you start

Pre-flight checklist on top of the general one:
  • An Azure DevOps organisation (https://dev.azure.com/<your-org>).
  • A Personal Access Token (PAT) with Work Items (Read) + Identity (Read) scopes.
Add a devops block to your migration.config.json:
{
  "ekso": { "tenant": "acme", "apiKey": "ek_live_..." },

  "source": "devops",

  "devops": {
    "organisation": "your-org",
    "personalAccessToken": "..."
  }
}
Or use the env-var override EKSO_MIGRATE_DEVOPS_PAT for CI use cases. The organisation field is the segment after dev.azure.com/ in your DevOps URL. For on-prem Azure DevOps Server, use the full base URL — see the on-prem section below.

Step 1 — list projects

ekso migrate devops list-projects \
    --config migration.config.json \
    --tenant acme
Sample output:
NAME            ID                                       STATE
Platform        01a3b9c8-1f44-4eaa-9c6e-7d5b2c1d3f4e     wellFormed
Mobile          02b4ca99-2055-5fbb-ad7f-8e6c3d2e4a5f     wellFormed

Step 2 — collect

ekso migrate devops collect \
    --config migration.config.json \
    --project Platform \
    --tenant acme
Repeat --project for multiple. Sample run:
fetching projects... 1 fetched
running WIQL query... 4127 work items found
fetching work items... 4127/4127 (chunked at 200)  ok
fetching discussion comments... 9803/9803  ok
fetching attachments... 421/421  ok
fetching iterations... 23 iterations  ok
saved cache: ~/.ekso/migrate/devops-2026-04-29T143205.sqlite
Useful flags:
  • --no-attachments — skip downloads.
  • --no-comments — skip discussion.
  • --resume — pick up where a killed collect left off.

Step 3 — dry-run apply

ekso migrate devops apply \
    --config migration.config.json \
    --process proc_engineering \
    --tenant acme \
    --dry-run

Step 4 — apply for real

ekso migrate devops apply \
    --config migration.config.json \
    --process proc_engineering \
    --tenant acme
If interrupted, re-run with --resume.

Identity resolution for DevOps

DevOps identities have email — --user-strategy match-or-create is reliable. Service identities ([Project]\Project Collection Build Service, [Acme]\Project Collection Service Accounts, etc.) are filtered out at collect time — they aren’t real users and don’t pollute your Ekso user list. There’s a regression test pinning this. Read Identity resolution.

Custom fields for DevOps

DevOps fields use Reference Name identifiers like Microsoft.VSTS.Scheduling.StoryPoints or Custom.<Name>:
devops:
  Microsoft.VSTS.Scheduling.StoryPoints: { ekso: StoryPoints, kind: decimal }
  Microsoft.VSTS.Common.Severity:
    ekso: Severity
    kind: picker
    picker:
      "1 - Critical": P0
      "2 - High":     P1
      "3 - Medium":   P2
      "4 - Low":      P3
  Custom.RootCause: { ekso: RootCause, kind: text }
Discover your reference names:
curl -u :PAT \
    https://dev.azure.com/your-org/your-project/_apis/wit/fields?api-version=7.1 \
    | jq '.value[] | {referenceName, name, type}'
Pass it to apply:
ekso migrate devops apply \
    --config migration.config.json \
    --process proc_engineering \
    --field-map migration.fields.yaml
See Field mapping.

Iterations, Area Paths, Work Item Types

DevOpsEksoNotes
IterationDataCycleHierarchy flattens in v1. MyProj\Sprint 1\Week 1 becomes a cycle named Sprint 1 / Week 1. Hierarchical cycle support is on the v2 roadmap.
Area PathConfigLabel (area:Backend/API) + DataItem.TagsSlash-joined label preserves the path.
Work Item Type (Bug, Task, Story, Feature, Epic)DataItem.Meta.devops_workitemtypeA single Ekso process can hold all work item types. The original type is preserved in Meta for audit.
Cycles attach to a board on the Ekso side. Pass --board <BOARD_ID> if the tenant has more than one board.

On-prem Azure DevOps Server (TFS)

The same CLI works against on-prem Azure DevOps Server / TFS — point the organisation at the full base URL of your collection:
"devops": {
  "organisation": "https://tfs.acme.local/tfs/DefaultCollection",
  "personalAccessToken": "..."
}
Collection-scoped PATs work the same way as Azure DevOps Service PATs. The Microsoft TFS SDK bundled with the CLI handles both.

Iron rule — WIQL chunking and 429 retry

WIQL queries return up to 200 work-item IDs per call (Azure cap). The migrator paginates by chunking IDs client-side at 200. The WorkItemTrackingHttpClient.GetWorkItemsAsync call also caps at 200 IDs per request — chunked the same way. DevOps rate-limit responses surface as VssServiceResponseException with HttpStatusCode==429. The CLI catches this, honours Retry-After, and backs off up to 6 attempts (~64 s of accumulated backoff). Past 6 attempts, exit code is 7.

Binary footprint note

The DevOps adapter bundles the official Microsoft TFS SDK (Microsoft.TeamFoundationServer.Client + Microsoft.VisualStudio.Services.Client), adding ~30 MB to the standalone Ekso CLI binary. This is the bundled trade-off — single binary, no plugin install. If binary size becomes a constraint, the v2 roadmap includes a plugin model that makes DevOps separately installable.

Troubleshooting DevOps-specific issues

SymptomFix
exit 3 — DevOps PAT invalidMint a new PAT with Work Items (Read) + Identity (Read) scopes. Check it’s not expired.
exit 7 — VssServiceResponseException 429Wait — DevOps rate limits are tied to TUs (throughput units). Re-run with --resume.
Service identity filtered (info, not error)“Project Collection Build Service” etc. is correctly filtered. No action needed.
Iteration hierarchy flattenedDocumented limitation. MyProj\Sprint 1\Week 1 flattens to Sprint 1 / Week 1.
Work item type not on destination processItem created; type stored in Meta.devops_workitemtype. Add the type to your Ekso process if you want first-class handling.
Custom.<FieldName> unknown to field-mapReference name didn’t match any field on this DevOps project. Run the discovery curl to verify.
See Troubleshooting for the full per-source error table.

Why migrate to Ekso

Azure DevOps centralises work items, repos, builds, and tests; Ekso focuses on the work-item layer with financial intelligence and AI primitives DevOps doesn’t model. See Ekso vs Azure DevOps for the broader comparison.

Where to next