> ## Documentation Index
> Fetch the complete documentation index at: https://ekso.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrate from Azure DevOps

> Move Azure DevOps work items, comments, attachments, links, and iterations into Ekso — in two commands.

`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](/cli/migrate/overview) and [Before you start](/cli/migrate/before-you-start) first.

## What gets imported

| DevOps concept                                                      | Ekso shape                                                                                                     |
| ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| Team Project                                                        | [`DataContainer`](/data-model/data-container) (`Code = ProjectName`, project GUID in `Meta.devops_project_id`) |
| Work Item (Bug, Task, User Story, Feature, Epic, Issue, …)          | [`DataItem`](/data-model/data-item)                                                                            |
| Discussion comment                                                  | [`DataAnnotation`](/data-model/data-annotation)                                                                |
| Attachment                                                          | [`DataFile`](/data-model/data-file) (multipart upload)                                                         |
| Link (Parent, Child, Related, Successor, Predecessor, Tested By, …) | [`DataLink`](/data-model/data-link)                                                                            |
| Iteration (Sprint)                                                  | [`DataCycle`](/data-model/data-cycle)                                                                          |
| Area Path                                                           | [`ConfigLabel`](/data-model/config-label) (prefix `area:`) + `DataItem.Tags`                                   |
| Tag                                                                 | [`ConfigLabel`](/data-model/config-label)                                                                      |
| Identity (DevOps user)                                              | [`DataUser`](/data-model/data-user) (matched or minted)                                                        |
| `System.WorkItemType`                                               | `DataItem.Meta.devops_workitemtype`                                                                            |
| `Microsoft.VSTS.Scheduling.StoryPoints`                             | [`DataItem.Field[]`](/data-model/data-item-field-value) (via `--field-map`)                                    |
| Custom field (`Custom.<Name>`)                                      | [`DataItem.Field[]`](/data-model/data-item-field-value) (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](/cli/migrate/before-you-start):

* An Azure DevOps organisation (`https://dev.azure.com/<your-org>`).
* A [Personal Access Token (PAT)](https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate) with **Work Items (Read)** + **Identity (Read)** scopes.

Add a `devops` block to your `migration.config.json`:

```json theme={null}
{
  "ekso": { "url": "https://ekso.acme.com", "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

```bash theme={null}
ekso migrate devops list-projects \
    --config migration.config.json \
    --url https://ekso.acme.com
```

Sample output:

```
NAME            ID                                       STATE
Platform        01a3b9c8-1f44-4eaa-9c6e-7d5b2c1d3f4e     wellFormed
Mobile          02b4ca99-2055-5fbb-ad7f-8e6c3d2e4a5f     wellFormed
```

## Step 2 — collect

```bash theme={null}
ekso migrate devops collect \
    --config migration.config.json \
    --project Platform \
    --url https://ekso.acme.com
```

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

```bash theme={null}
ekso migrate devops apply \
    --config migration.config.json \
    --process proc_engineering \
    --url https://ekso.acme.com \
    --dry-run
```

## Step 4 — apply for real

```bash theme={null}
ekso migrate devops apply \
    --config migration.config.json \
    --process proc_engineering \
    --url https://ekso.acme.com
```

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](/cli/migrate/identity-resolution).

## Custom fields for DevOps

DevOps fields use `Reference Name` identifiers like `Microsoft.VSTS.Scheduling.StoryPoints` or `Custom.<Name>`:

```yaml theme={null}
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:

```bash theme={null}
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:

```bash theme={null}
ekso migrate devops apply \
    --config migration.config.json \
    --process proc_engineering \
    --field-map migration.fields.yaml
```

See [Field mapping](/cli/migrate/field-mapping).

## Iterations, Area Paths, Work Item Types

| DevOps                                           | Ekso                                                 | Notes                                                                                                                                          |
| ------------------------------------------------ | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| Iteration                                        | `DataCycle`                                          | Hierarchy 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 Path                                        | `ConfigLabel` (`area:Backend/API`) + `DataItem.Tags` | Slash-joined label preserves the path.                                                                                                         |
| Work Item Type (Bug, Task, Story, Feature, Epic) | `DataItem.Meta.devops_workitemtype`                  | A 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:

```json theme={null}
"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

| Symptom                                       | Fix                                                                                                                          |
| --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `exit 3 — DevOps PAT invalid`                 | Mint a new PAT with **Work Items (Read)** + **Identity (Read)** scopes. Check it's not expired.                              |
| `exit 7 — VssServiceResponseException 429`    | Wait — 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 flattened`               | Documented limitation. `MyProj\Sprint 1\Week 1` flattens to `Sprint 1 / Week 1`.                                             |
| `Work item type not on destination process`   | Item 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-map`     | Reference name didn't match any field on this DevOps project. Run the discovery `curl` to verify.                            |

See [Troubleshooting](/cli/migrate/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](https://ekso.app/compare/ekso-vs-azure-devops) for the broader comparison.

## Where to next

* **[Command reference](/cli/migrate/command-reference)** — full flag surface.
* **[Identity resolution](/cli/migrate/identity-resolution)** — `--user-strategy` deep-dive.
* **[Field mapping](/cli/migrate/field-mapping)** — DevOps reference names.
* **[Troubleshooting](/cli/migrate/troubleshooting)** — exit codes and recovery.
