| render_with_liquid | false |
|---|
When migrating repositories from Azure DevOps Repos to GitHub while continuing to use Azure Pipelines for CI/CD, organizations face significant challenges related to cross-repository references, pipeline templates, and service connections. This document provides a deep analysis of the impacts, challenges, and solutions for maintaining Azure Pipelines functionality after repository migration.
- Understanding the Hybrid Model
- Critical Impact Areas
- Pipeline Template References
- Repository Resources and Checkout
- Service Connections Requirements
- Triggers and Automation Impact
- Authentication and Authorization
- Migration Scenarios and Solutions
- Step-by-Step Remediation Guide
- Best Practices and Recommendations
Many organizations choose a phased migration approach:
- Phase 1: Migrate repositories from Azure DevOps Repos to GitHub
- Phase 2: Continue using Azure Pipelines for build/deploy (until GitHub Actions migration is complete)
This creates a hybrid model where:
- Source code resides in GitHub
- CI/CD pipelines remain in Azure Pipelines
- Pipeline templates may reference repositories in both locations
Azure Pipelines was designed to work seamlessly with Azure Repos. When you move repositories to GitHub:
- Repository resource types change from
git(Azure Repos) togithub - Service connections become mandatory for GitHub repos
- Trigger mechanisms work differently
- Cross-repository template resolution changes
| Area | Impact Level | Effort to Fix | Description |
|---|---|---|---|
Pipeline templates (extends, template) |
🔴 High | High | All template references must be updated |
Repository resources (checkout) |
🔴 High | Medium | Multi-repo checkout requires service connections |
| CI/CD triggers | 🟡 Medium | Medium | Trigger configuration differs for GitHub |
| Variable groups & secrets | 🟢 Low | Low | No change needed |
| Artifact publishing | 🟢 Low | Low | Works the same way |
| Service connections | 🔴 High | High | New connections required for each GitHub repo |
-
Central Template Repositories: If you use a central repository for pipeline templates (common pattern), every pipeline that
extendsor includes templates from that repo must be updated. -
Resource Triggers: Repository resource triggers only work for Azure Repos Git - they do NOT work for GitHub repositories.
-
Service Connection Proliferation: Each GitHub repository/organization requires a dedicated service connection.
-
Simultaneous Updates Required: You cannot migrate repos one-by-one if they have interdependencies - templates and resources must be updated atomically.
# Current: Template in Azure Repos
resources:
repositories:
- repository: templates
type: git # Azure Repos type
name: MyProject/PipelineTemplates # Project/Repo format
extends:
template: ci-template.yml@templates# After: Template in GitHub
resources:
repositories:
- repository: templates
type: github # Changed type
name: MyOrg/PipelineTemplates # Org/Repo format
endpoint: GitHubServiceConnection # Required!
extends:
template: ci-template.yml@templates| Aspect | Azure Repos (git) |
GitHub (github) |
|---|---|---|
| Type | type: git |
type: github |
| Name format | Project/Repository |
Organization/Repository |
| Service connection | Optional (same org) | Required |
| Default branch | Uses pipeline's branch | Defaults to refs/heads/main |
| Triggers | Supported | Not supported |
If you use the extends pattern for pipeline security (recommended by Microsoft):
# Central template repository (security-enforced)
# File: templates/secure-pipeline.yml
parameters:
- name: buildSteps
type: stepList
stages:
- stage: Build
jobs:
- job: SecureBuild
steps:
- script: echo "Security scan starting"
- ${{ parameters.buildSteps }}
- script: echo "Security scan complete"Impact: Every pipeline that extends this template must:
- Update the repository resource type to
github - Add a service connection reference
- Update the repository name format
# Before: All repos in Azure DevOps
resources:
repositories:
- repository: tools
type: git
name: MyProject/BuildTools
- repository: shared
type: git
name: MyProject/SharedLibraries
steps:
- checkout: self
- checkout: tools
- checkout: shared# After: Repos migrated to GitHub
resources:
repositories:
- repository: tools
type: github
name: MyOrg/BuildTools
endpoint: GitHubConnection
- repository: shared
type: github
name: MyOrg/SharedLibraries
endpoint: GitHubConnection
steps:
- checkout: self
- checkout: tools
- checkout: sharedDuring migration, you may have repositories in both locations:
# Mixed: Some repos in Azure DevOps, some in GitHub
resources:
repositories:
# Already migrated to GitHub
- repository: app-code
type: github
name: MyOrg/ApplicationCode
endpoint: GitHubConnection
# Still in Azure DevOps
- repository: legacy-tools
type: git
name: MyProject/LegacyTools
steps:
- checkout: self
- checkout: app-code
- checkout: legacy-tools| Connection Type | Use Case | Recommendations |
|---|---|---|
| GitHub App | CI/CD pipelines | ✅ Recommended - Uses Azure Pipelines identity |
| OAuth | Personal repos | |
| PAT | Automation scenarios |
- Azure DevOps Portal → Project Settings → Service connections
- New service connection → GitHub
- Choose authentication type (GitHub App recommended)
- Authorize and configure repository access
- Builds use Azure Pipelines identity (not personal identity)
- Supports GitHub Checks for PR status
- Better rate limiting handling
- Can be configured for specific repositories only
# In pipeline YAML
resources:
repositories:
- repository: myrepo
type: github
name: MyOrg/MyRepo
endpoint: MyGitHubServiceConnection # Must be authorized for this pipelineFirst-time authorization: When a pipeline first uses a GitHub repository, you may see:
- "This pipeline needs permission to access a resource"
- Click "Authorize resources" to grant access
Repository resource triggers only work for Azure Repos Git repositories in the same organization. They do NOT work for GitHub or Bitbucket repository resources.
Before (Azure Repos) - This worked:
resources:
repositories:
- repository: shared-lib
type: git
name: MyProject/SharedLibrary
trigger: # ✅ Triggers when SharedLibrary changes
branches:
include:
- main
- releases/*
# Pipeline runs when SharedLibrary repo is updatedAfter (GitHub) - This does NOT work:
resources:
repositories:
- repository: shared-lib
type: github
name: MyOrg/SharedLibrary
endpoint: GitHubConnection
trigger: # ❌ IGNORED - GitHub triggers not supported
branches:
include:
- main
# Pipeline does NOT run when SharedLibrary repo is updatedConfigure a webhook in GitHub to call Azure Pipelines:
resources:
webhooks:
- webhook: SharedLibraryUpdated
connection: GitHubWebhookConnection
filters:
- path: repository.full_name
value: MyOrg/SharedLibrary# In GitHub: .github/workflows/trigger-azure-pipeline.yml
name: Trigger Azure Pipeline
on:
push:
branches: [main]
jobs:
trigger:
runs-on: ubuntu-latest
steps:
- name: Trigger Azure Pipeline
uses: Azure/pipelines@v1
with:
azure-devops-project-url: https://dev.azure.com/MyOrg/MyProject
azure-pipeline-name: 'My-Pipeline'
azure-devops-token: ${{ secrets.AZURE_DEVOPS_PAT }}schedules:
- cron: "0 */4 * * *" # Every 4 hours
displayName: Regular sync
branches:
include:
- main
always: trueFor Azure Pipelines GitHub integration:
| PAT Type | Supported | Notes |
|---|---|---|
| Classic PAT | ✅ Yes | Required scopes: repo, admin:repo_hook, read:user, user:email |
| Fine-grained PAT | May not work for all scenarios |
| Permission | Purpose |
|---|---|
| Write access to code | Commit YAML files (optional) |
| Read access to metadata | Fetch repo info |
| Read/write to checks | Display build status |
| Read/write to pull requests | PR validation |
If your GitHub repos are in different organizations:
- Create separate service connections per GitHub organization
- Each connection requires authorization from that org's admin
Problem: You have a central PipelineTemplates repo that 50+ pipelines extend from.
Solution:
- Create GitHub service connection before migration
- Prepare all pipeline updates in branches
- Migrate template repo to GitHub
- Immediately deploy all pipeline updates
- No phased approach - must be atomic
Updated Pipeline Example:
# Before
resources:
repositories:
- repository: templates
type: git
name: CentralProject/PipelineTemplates
# After
resources:
repositories:
- repository: templates
type: github
name: MyGitHubOrg/PipelineTemplates
endpoint: GitHub-CentralTemplates
ref: refs/heads/main # Pin to specific branch
extends:
template: dotnet-ci.yml@templatesProblem: Application repo depends on a shared library repo for build-time resources.
Before:
resources:
repositories:
- repository: shared-lib
type: git
name: MyProject/SharedLibrary
trigger:
branches:
include:
- main
steps:
- checkout: self
- checkout: shared-lib
- script: |
# Use files from shared-lib
cp shared-lib/config/*.json src/After:
resources:
repositories:
- repository: shared-lib
type: github
name: MyOrg/SharedLibrary
endpoint: GitHubConnection
# Note: trigger removed - not supported for GitHub
steps:
- checkout: self
- checkout: shared-lib
- script: |
# Same usage - checkout paths preserved
cp shared-lib/config/*.json src/Workaround for Trigger:
- Use GitHub Actions in SharedLibrary to trigger Azure Pipeline via webhook
- Or use scheduled builds
Problem: Single repo with multiple applications, each with its own pipeline.
Solution: Path-based triggers work for GitHub repos:
# Pipeline for Service A
trigger:
branches:
include:
- main
paths:
include:
- services/service-a/**
- shared/**
pr:
branches:
include:
- main
paths:
include:
- services/service-a/**- Inventory all pipelines that reference the repos being migrated
- Identify template dependencies - which pipelines use
extendsortemplatefrom other repos - Create GitHub service connections in advance
- Test service connections with a sample pipeline
- Document current trigger configurations (CI, PR, resource triggers)
- Plan for resource triggers that won't work after migration
Azure DevOps → Project Settings → Service connections → New → GitHub
Choose Azure Pipelines GitHub App (recommended)
Find and replace pattern:
# Find (Azure Repos pattern)
- repository: <alias>
type: git
name: <Project>/<Repo>
# Replace with (GitHub pattern)
- repository: <alias>
type: github
name: <GitHubOrg>/<Repo>
endpoint: <ServiceConnectionName>For CI triggers (push to main repo): No change needed - works the same.
For PR triggers: No change needed - works the same.
For resource triggers: Remove and implement alternative (webhook/GitHub Action).
- Create test branch with updated pipeline YAML
- Run manual pipeline to verify checkout works
- Verify template resolution succeeds
- Test PR trigger behavior
Deploy all pipeline changes simultaneously if they share template dependencies.
- CI triggers work on push
- PR triggers work on pull requests
- Templates resolve correctly
- Multi-repo checkout works
- Artifacts publish correctly
- GitHub Checks status appears on commits/PRs
# Recommended: Uses Azure Pipelines identity
resources:
repositories:
- repository: code
type: github
name: MyOrg/MyApp
endpoint: AzurePipelines-GitHubApp # GitHub App connectionAlways pin to a specific ref to avoid unexpected changes:
resources:
repositories:
- repository: templates
type: github
name: MyOrg/PipelineTemplates
endpoint: GitHubConnection
ref: refs/tags/v2.0.0 # Pin to tag
# or
ref: refs/heads/release/stable # Pin to branch- Use consistent naming conventions for service connections
- Document which pipelines use which connections
- Implement least-privilege access (connection per repository when needed)
Before migration, identify all resource triggers and plan alternatives:
| Current Trigger | Alternative |
|---|---|
| Resource trigger on library repo | GitHub Actions → Azure Pipeline webhook |
| Resource trigger on shared config | Scheduled builds + caching |
| Multi-repo trigger | Webhook-based triggers |
# Use a test pipeline to verify configuration
trigger: none # Manual only for testing
resources:
repositories:
- repository: migrated-repo
type: github
name: MyOrg/MigratedRepo
endpoint: TestGitHubConnection
steps:
- checkout: migrated-repo
- script: |
echo "Checkout succeeded"
ls -la $(Build.SourcesDirectory)Create a tracking document with:
- All repos being migrated
- All pipelines affected
- Service connections created
- Trigger changes required
- Dependencies between pipelines
- Rollback plan
| Aspect | Impact | Action Required |
|---|---|---|
| Repository type | Must change from git to github |
Update all pipeline YAML |
| Service connections | Mandatory for GitHub repos | Create before migration |
| Template references | Must include endpoint | Update all extends/template references |
| Resource triggers | Not supported for GitHub | Implement webhooks or alternatives |
| CI/PR triggers | Work normally | No change needed |
| Multi-repo checkout | Works with service connection | Add endpoint to resources |
✅ All pipelines build successfully
✅ Templates resolve from GitHub
✅ Multi-repo checkout works
✅ CI triggers fire on push
✅ PR validation triggers fire
✅ GitHub Checks status displayed
✅ No "resource not authorized" errors