Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Azure DevOps Services
After you complete the prerequisites, authenticate into Azure DevOps and then start the initial synchronization for your repository. Use the service connection ID you created in the prerequisites when you start the migration.
Prerequisites
| Requirement | Details |
|---|---|
| Azure DevOps Repository GUID | Obtained from the az repos show --query id command for your Azure DevOps repository, or from the Azure DevOps portal repository settings. |
| Self-hosted Linux agent | Agent installed and registered in your Azure DevOps project, and running (Online) under Project Settings > Agent pools. |
| Azure DevOps Service connection to GitHub | Created in Azure DevOps with GitHub Enterprise Admin's PAT. |
| GitHub Personal Access Token | Generated with required scopes: repo, admin:org, delete_repo. Token must be valid during migration (up to 21 days). |
| Azure DevOps permissions | The migration operator must have the Enterprise Live Migrations: Manage Migrations permission. |
| Network access | Firewall rules allow the agent machine to communicate with both Azure DevOps (dev.azure.com) and GitHub endpoints. |
| (Optional) Pipeline Connection ID | Created from the Azure DevOps service connection for pipeline rewiring. |
| (Optional) Azure Pipelines and Azure Boards | Installed Azure Pipelines and Azure Boards in your target GitHub enterprise. |
If any item is incomplete, see the prerequisites before you continue.
Authenticate into Azure DevOps
ELM requires authenticated access to Azure DevOps.
Sign in to the Azure CLI:
az loginSet your default Azure DevOps organization:
az devops configure --defaults organization=https://dev.azure.com/myorgTip
If your local Git remote points to a different organization, add
--detect falseto all migration commands to prevent autodetection from choosing the wrong organization.Verify your access:
az devops project listGet the repository GUID:
az repos show --org https://dev.azure.com/<org> --project <project> --repository <repo-name> --query id -o tsv
Start the agent
Your self-hosted Linux agent must be online and listening before you start a migration.
Required: Interactive agent startup
Complete these steps to start the agent interactively. This step is required for the migration.
Check the agent status in the Azure DevOps portal under Project Settings > Agent pools. Select your pool, open the Agents tab, and confirm the agent shows as Online.
If the agent is offline, sign in to the Linux machine where the agent is installed and change to the agent install directory.
Start the agent interactively:
./run.sh
The agent runs in the foreground. Keep this terminal session open while the migration is in progress.
Optional: Production setup - run the agent as a service
For long-running migrations, configure the agent to run as a system service so it can restart if the machine reboots or the connection drops.
To run the agent as a service so it stays online across reboots:
sudo ./svc.sh install sudo ./svc.sh start
When to use each mode:
- Use service mode for migrations expected to take more than two hours, migrations during off-hours, or scenarios where the machine might reboot or the connection could drop.
- Use interactive mode for quick migrations (less than 30 minutes), attended migrations where you monitor progress, or environments where system services require extra approval.
- Agent mode doesn't affect sync speed. Whether you run
./run.shorsudo ./svc.sh start, migration speed and data transfer rate are the same.
For more information, see Run a self-hosted agent in Linux.
Troubleshoot agent startup
If the agent fails to start or stays offline after you run ./run.sh, check the following items:
- Permissions: Make sure the agent directory has execute permissions (
chmod +x run.sh) and the user has write access to the directory. - Port conflict: Check that the agent listener port isn't used by another process (
netstat -an | grep <agent-port>). - Network: Confirm the agent machine can reach
dev.azure.comand required GitHub endpoints. - Service startup failure: If you use
sudo ./svc.sh start, review service logs by usingjournalctl -u vstsagent.<org>-<project>-<agent-name>.service.
Choose your migration path
Before you start migration, choose your operating path:
- Move fully to GitHub and stop using Azure DevOps for source control.
- Use a hybrid model and move source code to GitHub while continuing to use Azure DevOps for pipelines and/or boards.
For hybrid mode, decide whether ELM rewires Azure Pipelines and creates the Azure Boards connection, or whether your team handles those updates separately.
Also decide whether to validate first and start sync within 24 hours, or let ELM start synchronization automatically after validation succeeds.
Start the synchronization
Start with this base command for every migration:
az devops migrations create --org https://dev.azure.com/<org>
--repository-id <repo-guid>
--target-repository https://github.com/<org>/<repo>
--github-token <github-pat>
--service-endpoint-id <service-connection-guid>
--agent-pool <agent-pool-name>
In --target-repository, <repo> is the GitHub repository name. Choose any available name. It doesn't need to match the Azure DevOps repository name.
Add optional parameters based on your migration scenario:
| Scenario | Add this parameter | Result |
|---|---|---|
| Validate before syncing | --validate-only |
ELM runs validation only. You must start sync within the 24-hour validation window. |
| Automatically start sync after validation | No extra parameter | ELM validates first, then starts synchronization automatically if validation succeeds. |
| Automatically discover and rewire pipelines | --enable-auto-discover-pipelines and --pipeline-service-connection-id <service-connection-id> |
ELM finds pipelines that reference the source repository and rewires them to GitHub. |
| Manually choose pipelines to rewire later | --pipeline-service-connection-id <service-connection-id> |
ELM prepares migration for manual pipeline rewiring after sync starts. |
| Don't use ELM for pipeline rewiring | Omit both pipeline rewiring parameters | ELM migrates the repository only. You can update pipelines separately. |
Check migration status:
az devops migrations status --org https://dev.azure.com/<org>
--repository-id <repo-guid>
Look for:
status- current migration status (Active,Succeeded,Completed,Failed,Suspended)stage- current migration phase (Queued,Validation,Synchronization,Cutover,ReviewForCutover,ReadyForCutover,Migrated)validationIssues- list of precheck failures with error codes and messageserrorMessage- details about the failure
Validate before syncing
Use this flow if you started migration with --validate-only or selected Run validation and migrate later.
Start sync within 24 hours after validation succeeds:
az devops migrations resume --org https://dev.azure.com/<org> --repository-id <repo-guid> --migrationRecheck status:
az devops migrations status --org https://dev.azure.com/<org> --repository-id <repo-guid>
Manual pipeline rewiring (hybrid mode)
Use this workflow when you choose to rewire pipelines manually.
Note
The az devops migrations pipelines commands (list, submit, update, retry, and delete) are in preview.
Find the pipeline definition IDs that reference the migrating repository.
az devops migrations pipelines listreports only the pipelines that are already enrolled in rewiring and their rewiring status. Before you submit any pipelines, it returns an empty result, so you can't use it to discover candidate pipelines. To find the definition IDs to rewire, list the pipelines that build from the source repository:az pipelines list --org $org \ --project $project \ --repository $rid \ --repository-type tfsgit \ --query "[].{id:id, name:name}" -o tableUse the
idvalues from the output as the--pipeline-idsin the next step.Note
az pipelines list --repositoryreturns only pipelines whose default trigger repository is the source repository. A pipeline that references the repository through a resource, template, or checkout step (rather than as its primary repository) might not appear here. Include those definition IDs as well if you know they depend on the migrating repository.Submit selected pipeline definition IDs for rewiring (maximum 200 IDs per request):
az devops migrations pipelines submit --org $org \ --repository-id $rid \ --pipeline-ids 42 43 44 \ --service-connection-id $scid--service-connection-idis optional if you already attached a connection through--pipeline-service-connection-idinmigrations createor a previouspipelines update --service-connection-id.If a pipeline references other repositories, map each source repository to its GitHub target:
az devops migrations pipelines submit --org $org \ --repository-id $rid \ --pipeline-ids 42 43 44 \ --service-connection-id $scid \ --repository-mapping aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa=<GH_ORG>/shared-templates \ --repository-mapping bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb=<GH_ORG>/another-repoFormat:
--repository-mapping <sourceRepoId>=<targetOwner>/<targetRepo>(repeatable).Monitor and adjust the pipeline set:
az devops migrations pipelines list --org $org --repository-id $rid -o tableAfter you submit pipelines, this command lists each enrolled pipeline and its rewiring status:
Column Meaning DefinitionIdPipeline definition ID NamePipeline name (falls back to the YAML filename if the name isn't available yet) ClassificationHow the pipeline references the repository StatusCurrent rewiring state of the pipeline ErrorMessageFailure detail when the pipeline is in a failed state az devops migrations pipelines update --org $org \ --repository-id $rid \ --add-ids 50 51 \ --remove-ids 42 \ --retry-ids 43 \ --service-connection-id $scid--add-ids: add more pipelines.--remove-ids: remove pipelines from rewiring.--retry-ids: retry pipelines inFailedstate.- At least one of
--add-ids,--remove-ids,--retry-ids,--service-connection-id, or--repository-mappingis required.
Retry-only shortcut:
az devops migrations pipelines retry --org $org --repository-id $rid --pipeline-ids 43Review and approve at cutover when stage is
ReviewForCutover:az devops migrations cutover review --org $org --repository-id $rid -o tableReview key fields:
Field Meaning FailedCount/BlockedCount/PendingCount/TotalUnprocessedCountCounts of unprocessed items (failed, blocked, pending, and total) RequiresPipelineVerification(requiresPipelineVerificationAcknowledgment)If true, approval must include--pipelines-verifiedState/Type/PullRequestUrl(fromfailedItems[])Per-item failure details Approve cutover:
az devops migrations cutover approve --org $org \ --repository-id $rid \ --pipelines-verifiedIf you also accept unprocessed items:
az devops migrations cutover approve --org $org \ --repository-id $rid \ --accept-failures 3 \ --pipelines-verifiedYou must supply at least one of
--accept-failuresor--pipelines-verified.
During the initial sync:
- Azure DevOps remains fully writable. Teams continue working normally.
- GitHub isn't yet the system of record.
- The target GitHub repository is automatically set to private.
Important
After you start a full migration, you must complete cutover within 21 days. The clock starts when initial sync begins.