You've successfully subscribed to Florin Loghiade
Great! Next, complete checkout for full access to Florin Loghiade
Welcome back! You've successfully signed in
Success! Your account is fully activated, you now have access to all content.
Migrating from Azure DevOps Pipelines to GitHub Actions - Insights

Migrating from Azure DevOps Pipelines to GitHub Actions - Insights


Before we get started, let's address the elephant in the room. No, you should not migrate all your pipelines to Azure DevOps, but if it makes sense for you, then go right ahead :) The purpose of this post is to give some information and what to look for when you're planning or thinking about migrating pipelines to GitHub Actions.

The systems are similar in one way and different in another, making a migration relatively easy as long as you consider that GitHub requires some explicit configurations, e.g., You cannot skip some YAML configurations as you can in Azure DevOps, which might be an issue depending on your style of creating pipelines.

Another critical difference between the systems is that GitHub has a different way of operating stages than Azure DevOps. The difference is that you can define stages in your YAML file and a different deployment workload, while in GitHub Actions, these things need to be separated.

Anyway, I don't want to rehash what the team at GitHub wrote here:

Migrating from Azure Pipelines to GitHub Actions - GitHub Docs
GitHub Actions and Azure Pipelines share several configuration similarities, which makes migrating to GitHub Actions relatively straightforward.

The article is an excellent starting point when considering migration towards Actions.

Another awesome thing they provide is an automated tool that converts your existing pipelines into actions with minimal hassle.

Automating migration with GitHub Actions Importer - GitHub Docs
Use GitHub Actions Importer to plan and automate your migration to GitHub Actions.

Before I started playing around with this tool, I had to go through the process of adapting very complex pipelines manually. I only migrated some of them as it didn't make sense to lift & shift just for the sake of doing that, but some pipelines were worth the hassle as it made more sense for them to be in GitHub.

How to tackle it.

The planning and evaluation phase should never be skipped as this is your chance to cancel before you over-commit too much time and have to deliver it, even if it's nonsensical.

If you're working with pipeline templates, review them individually and see your dependencies and how much you rely on Azure DevOps-installed extensions. If you rely too much on specific extensions and GitHub doesn't have an equivalent, you would need to write something similar or eliminate it.

A good way to figure out the amount of work required is to use the GitHub Importer and run it with the audit flag and then with the dry-run flag, as it would give you a sense of what

For example, I ran the Action Importer on a simple AzDo Pipeline, and here's what I got.

name: builder
      - feature/builderPipeline
  # Github Actions does not support schedule branch filters. Scheduled workflows run on the latest commit on the default or base branch
    - cron: 0 12 1 1-12 *
  serviceConnectionName: subscription

  locationName_we: westeurope
  owner: florinloghiade

    name: pipelines_templates_azdoRuns_runbuilderstage
    uses: './.github/workflows/pipelines_templates_azdoruns_builderstage.yml'
      serviceConnection: '${{ env.serviceConnectionName }}'
      baseImage: pwsh
# Environment variables defined in a calling workflow are not accessible to this reusable workflow. Refer to the documentation for further details on this limitation.
name: pipelines_templates_azdo_runbuilderstage
        required: false
        default: ''
        type: string
        required: false
        default: succeeded()
        type: string
  pipelines_templates_azdo_runbuilderjob-${{ parameters.jobName }}:
      jobName: build_builder_${{ inputs.builder }}
      serviceConnection: "${{ inputs.serviceConnection }}"
      codebase: "${{ github.workspace }}/builder/base"
      builder: "${{ inputs.builder }}"
      condition: succeeded()
    runs-on: ubuntu-latest
    - name: checkout
      uses: actions/[email protected]
    - name: Set Build Date
      run: |-
        $currentDate = Get-Date -Format "yyyyMMdd"
        echo "##vso[task.setvariable variable=BuildDate]$currentDate"
      shell: pwsh
    - name: Build ${{ env.builder }} base container image
      if: inputs.condition()
      run: docker build . --file "Dockerfile" -t ${{ env.DOCKER_REGISTRY }}/build:${{ env.BuildDate }} -t ${{ env.DOCKER_REGISTRY }}/build:latest
    - name: Push ${{ env.builder }} base container image
      if: inputs.condition()
      run: docker push ${{ env.DOCKER_REGISTRY }}/build:${{ env.BuildDate }}
    if: inputs.condition()
stage file

I trimmed the YAML files to give you a meaningful example because I have multiple reusable templates, including parameter, variable, and other job/task files, which get referenced in the main pipeline file and expanded.

The results from above contained the expanded YAML files from the main pipeline and separated the stages in a dedicated file, which is not a deal breaker but could be a better thing too. For example, a 20-file pipeline got reduced to two files as well.

The good part is that I can copy-paste and create my way of dealing with the pipelines; however, I only save time by running the tool. Getting started with the Actions Importer is relatively easy; you need to have Docker and the GitHub CLI and then install the extension.

The part that's mandatory to do is to run the gh action-importer update command as it downloads the latest version of the container. Afterward, gh action-importer configure where you will give it PATs for AzDo and GitHub and locations for it to scan and provide you with the information you're looking for.

Based on what the tool gave me, I can understand that parameter.yaml and variables.yaml files are not supported or implemented, which sometimes complicates things.

Parameters and variables are set as environment variables in the GitHub Runner environment and are named inputs and vars. Secrets should be set as encrypted secrets; otherwise, they will be unmasked in the output window.

Integration with Azure is done using federated credentials or a service principal. You will probably be migrating with references to Service Connections and have inline scripts that reference those credentials. So, starting with client secrets is a good idea, then migrating toward federated secrets.

Connect GitHub and Azure
Resources to connect to GitHub from Azure and other services

So far, I figured out that while working toward that migration, and I'm still evaluating some aspects. The main thing I wanted to migrate is a PowerShell Module builder and set it as a release and a base image builder for AzDO agents, which might be adapted soon to create self-hosted runners.

This is not an exhaustive post on migrating towards GitHub actions, but more of a be careful as it's not as seamless as it might seem.

I hope you learned something and as always, have a good one!