BlogDevOps

CI/CD Pipelines with Azure DevOps and GitHub Actions

A CI/CD pipeline is the backbone of a high-performing engineering team.

Author

Artan Ajredini

Artan Ajredini

CEO & Cloud Architect

4 min read
14 April 2025

Introduction to CI/CD

A CI/CD pipeline is the backbone of a high-performing engineering team. Without one, deployments are manual, error-prone, and slow. With one, every code change is automatically tested, built, and delivered to production — or stopped at the gate if something breaks.

CI stands for Continuous Integration: the practice of automatically building and testing code every time a developer pushes a change. CD stands for Continuous Delivery (or Deployment): automatically delivering that tested code to one or more environments.

High-performing engineering teams deploy to production multiple times per day. The pipeline is what makes that safe — not heroics, not careful humans, but automated gates that catch problems before they reach users.

In the Azure ecosystem, two tools dominate: Azure DevOps Pipelines and GitHub Actions. They are not competitors — they are complementary. Many teams use both, letting each do what it does best. This article explains how to combine them into a complete, production-grade pipeline.

What a complete pipeline looks like

  1. Developer pushes code to a feature branch on GitHub.
  2. GitHub Actions triggers: runs unit tests, builds a Docker image, pushes it to Azure Container Registry.
  3. A pull request is opened — the CI checks must pass before merge is allowed.
  4. Code is merged to main. GitHub Actions tags the image with the commit SHA.
  5. Azure DevOps detects the new image in ACR and triggers a release pipeline.
  6. The release pipeline deploys to staging, runs smoke tests, waits for manual approval, then deploys to production.

Azure DevOps vs GitHub Actions

Both tools can build, test, and deploy code. The question is not which is better — it is which is better for each part of the job.

GitHub Actions strengths

  • Native to GitHub — triggers on every GitHub event: push, PR, issue comment, release, schedule.
  • Massive marketplace of pre-built actions (checkout, Docker build, Azure login, etc.).
  • YAML-first, simple syntax — easy to version alongside application code.
  • Free for public repositories; generous free tier for private repos.
  • Best for CI: building, testing, linting, security scanning, and image publishing.

Azure DevOps strengths

  • Mature release pipeline UI with approval gates, deployment stages, and rollback controls.
  • Deep integration with Azure: service connections, Azure Key Vault variable groups, environments with checks.
  • Boards, Repos, Test Plans, Artifacts — a full ALM suite if you need it.
  • Better audit trail for enterprise compliance — who approved what, when, and why.
  • Best for CD: controlled multi-stage releases with approvals and environment governance.

The winning combination for Azure workloads: use GitHub Actions for the CI phase (test, build, push image to ACR) and Azure DevOps for the CD phase (pull from ACR, deploy to AKS or App Service with approval gates). Each tool does what it is designed for.

Building the Pipeline

The pipeline below covers the full path from code push to production deployment. It uses GitHub Actions for CI and Azure DevOps for the release.

GitHub Actions: CI workflow

This workflow runs on every push to main and on pull requests. It builds and pushes a Docker image to Azure Container Registry tagged with the Git commit SHA — a stable, immutable reference for the release pipeline to consume.

yaml
# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  ACR_NAME: myregistry.azurecr.io
  IMAGE_NAME: myapp

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run unit tests
        run: dotnet test --configuration Release

      - name: Log in to Azure Container Registry
        uses: azure/docker-login@v1
        with:
          login-server: ${{ env.ACR_NAME }}
          username: ${{ secrets.ACR_USERNAME }}
          password: ${{ secrets.ACR_PASSWORD }}

      - name: Build and push Docker image
        run: |
          docker build -t $ACR_NAME/$IMAGE_NAME:${{ github.sha }} .
          docker push $ACR_NAME/$IMAGE_NAME:${{ github.sha }}

Azure DevOps: multi-stage release pipeline

The Azure DevOps pipeline picks up the image published by GitHub Actions and deploys it through staging and production. The approval gate between stages ensures a human confirms before production is touched.

yaml
# azure-pipelines.yml
trigger: none  # triggered by ACR image push, not code push

resources:
  containers:
    - container: app
      type: ACR
      azureSubscription: my-azure-service-connection
      resourceGroup: my-rg
      registry: myregistry
      repository: myapp
      trigger:
        tags:
          include: ['*']

stages:
  - stage: Staging
    jobs:
      - deployment: DeployStaging
        environment: staging
        strategy:
          runOnce:
            deploy:
              steps:
                - task: KubernetesManifest@1
                  inputs:
                    action: deploy
                    kubernetesServiceConnection: aks-staging
                    manifests: k8s/deployment.yaml
                    containers: myregistry.azurecr.io/myapp:$(resources.container.app.tag)

  - stage: Production
    dependsOn: Staging
    jobs:
      - deployment: DeployProduction
        environment: production  # has approval check configured
        strategy:
          runOnce:
            deploy:
              steps:
                - task: KubernetesManifest@1
                  inputs:
                    action: deploy
                    kubernetesServiceConnection: aks-production
                    manifests: k8s/deployment.yaml
                    containers: myregistry.azurecr.io/myapp:$(resources.container.app.tag)

In Azure DevOps, go to Pipelines → Environments → production → Approvals and checks to add required reviewers. The pipeline will pause at the Production stage until an approver confirms, giving your team a final human gate before every production release.

Secrets Management and Rollback Strategies

Two things break most pipelines in production: secrets leaking into logs or source control, and no clear path to roll back a bad deployment. Both are preventable.

Secrets with Azure Key Vault

Never store secrets in pipeline YAML files or repository settings beyond what is absolutely necessary. Use Azure Key Vault as the single source of truth. In Azure DevOps, link a Key Vault to a variable group — the pipeline reads secrets at runtime without any human ever seeing them.

yaml
# In azure-pipelines.yml — reference a Key Vault-linked variable group
variables:
  - group: production-secrets  # linked to Azure Key Vault

steps:
  - script: echo "Deploying with connection string $(DatabaseConnectionString)"
    # DatabaseConnectionString is fetched from Key Vault at runtime
    # It is masked in logs automatically

In GitHub Actions, use the azure/get-keyvault-secrets action to pull secrets from Key Vault into the workflow environment. Secrets fetched this way are automatically masked in logs.

yaml
- name: Get secrets from Key Vault
  uses: Azure/get-keyvault-secrets@v1
  with:
    keyvault: my-keyvault
    secrets: 'DatabaseConnectionString, ApiKey'
  id: keyvaultSecrets

- name: Use secret
  run: echo ${{ steps.keyvaultSecrets.outputs.DatabaseConnectionString }}

Rollback with deployment slots (App Service)

Azure App Service deployment slots give you zero-downtime deployments with an instant rollback path. Deploy to a staging slot, run smoke tests, then swap slots to promote to production. If something goes wrong, swap back — it takes seconds.

yaml
- task: AzureWebApp@1
  displayName: Deploy to staging slot
  inputs:
    azureSubscription: my-service-connection
    appName: my-web-app
    deployToSlotOrASE: true
    resourceGroupName: my-rg
    slotName: staging

- task: AzureAppServiceManage@0
  displayName: Swap staging to production
  inputs:
    azureSubscription: my-service-connection
    action: Swap Slots
    webAppName: my-web-app
    resourceGroupName: my-rg
    sourceSlot: staging

Rollback with Kubernetes (AKS)

On AKS, every deployment is versioned by Kubernetes. If a bad deployment goes out, you can roll back to the previous revision in a single command:

yaml
# Roll back to the previous deployment revision
kubectl rollout undo deployment/myapp -n production

# Or roll back to a specific revision
kubectl rollout undo deployment/myapp --to-revision=3 -n production

# Check rollout status
kubectl rollout status deployment/myapp -n production

For automated rollback, configure a Kubernetes liveness probe and readiness probe on your deployment. If the new pods fail their health checks, Kubernetes will stop the rollout and the old pods remain in service — the pipeline fails visibly rather than silently leaving a broken deployment running.

Want us to build your CI/CD pipeline?

We design and implement production-grade pipelines for Azure workloads — from first commit to automated multi-environment delivery.

Schedule a call

Closing Thoughts

A well-designed CI/CD pipeline is not a nice-to-have — it is the difference between a team that ships with confidence and one that dreads release day. GitHub Actions handles CI beautifully: fast, cheap, and tightly integrated with your repository. Azure DevOps handles CD with the governance controls that production environments demand.

Start with the GitHub Actions CI workflow in this guide, get your image building and pushing to ACR, then add the Azure DevOps release pipeline with a staging environment and a single approval gate. That combination alone will transform how your team ships software.

More articles

View all
Azure Cost Optimisation: Cut Your Cloud Bill by 40%
about 1 year ago1 min read

Azure Cost Optimisation: Cut Your Cloud Bill by 40%

Cloud costs have a habit of growing faster than the business value they deliver. In our experience working with Azure customers across industries, most organisations have between 25% and 45% immediate savings available without any impact on performance or reliability. In this article, we walk through the most impactful cost reduction techniques: right-sizing virtual machines using Azure Advisor recommendations, converting pay-as-you-go workloads to Reserved Instances or Savings Plans, enabling auto-shutdown for non-production environments, replacing always-on VMs with Azure Container Apps or Functions for batch workloads, and deleting orphaned resources like unused disks and public IPs. We also show how to set up cost alerts and budgets in Azure Cost Management so that surprises are caught early, before they appear on the invoice.

Read article
Building RAG Pipelines with Azure AI Search and GPT-4o
about 1 year ago1 min read

Building RAG Pipelines with Azure AI Search and GPT-4o

Retrieval-Augmented Generation (RAG) is the architecture that turns a general-purpose language model into a domain expert grounded in your own data. Instead of fine-tuning — which is expensive and produces models that go stale — RAG retrieves the most relevant documents at query time and passes them as context to the model. In this article, we build a complete RAG pipeline on Azure: documents are uploaded to Azure Blob Storage, indexed by Azure AI Search using vector embeddings from Azure OpenAI, and retrieved at query time using hybrid search (vector + keyword). The retrieved chunks are then assembled into a prompt sent to GPT-4o, which generates a grounded answer with source citations. We cover chunking strategies, embedding model selection, index schema design, semantic ranking, and how to evaluate retrieval quality. Full code examples in Python using the Azure SDK are included.

Read article
Getting Started with Azure OpenAI Service
about 1 year ago1 min read

Getting Started with Azure OpenAI Service

Azure OpenAI Service brings powerful large language models — including GPT-4o, GPT-4 Turbo, and Embeddings — directly into your Azure environment, giving you enterprise-grade security, compliance, and regional data residency. In this guide, we walk through provisioning your first Azure OpenAI resource, deploying a model, and making your first API call from a .NET or Python application. We also cover key concepts like token limits, system prompts, temperature settings, and how to structure effective prompts for consistent results. Whether you are building a customer support chatbot, a document summarisation tool, or an internal knowledge assistant, this article gives you a solid foundation to start shipping AI features with confidence.

Read article