Last Reviewed: Jun 15, 2026
9 min read

Azure DevOps & CI/CD: Pipelines, GitHub Actions, and Tooling Guide

Automate builds, tests, and deployments for fast, reliable cloud application delivery

Overview

In modern cloud-native development, speed, reliability, and security are non-negotiable. Azure DevOps and GitHub Actions provide the foundational tooling to implement Continuous Integration and Continuous Delivery (CI/CD) — a practice that automates the entire software lifecycle, from code commit to production deployment. This lesson bridges infrastructure (covered in previous lessons on Azure security and compliance) with operational excellence, enabling you to build, test, and deploy applications at scale while maintaining governance, auditability, and resilience.

By mastering Azure Pipelines and GitHub Actions, you'll learn how to create repeatable, secure, and scalable workflows that integrate with Azure services like Azure Kubernetes Service (AKS), Azure App Service, and Azure SQL Database — all while enforcing security policies and cost controls established in prior modules.

Key objectives:

  • Design CI/CD pipelines using Azure Pipelines and GitHub Actions
  • Secure pipelines with service connections, variable groups, and secrets management
  • Automate infrastructure provisioning (IaC) and application deployment
  • Integrate testing, code quality gates, and compliance checks
  • Implement deployment strategies (e.g., canary, blue/green) in Azure

Core Concepts

What is CI/CD?

CI/CD is a combination of two practices:

  • Continuous Integration (CI): Developers frequently merge code changes into a shared repository. Each merge triggers automated builds and tests to catch integration issues early.
  • Continuous Delivery/Deployment (CD): After successful CI, code is automatically released to staging or production environments. Delivery means deployments are ready to happen on demand; Deployment means releases happen automatically.

CI/CD pipelines reduce human error, accelerate feedback loops, and ensure every change is reproducible and auditable — critical for meeting compliance requirements like ISO 27001, SOC 2, or HIPAA.

Exam Tip (AZ-900 & AZ-104): Understand the difference between Continuous Delivery (human approval required before release) and Continuous Deployment (fully automatic release to production). AZ-104 candidates must know how Azure Pipelines supports both.

Key Components of an Azure DevOps Pipeline

An Azure Pipeline consists of:

  • Pipeline (YAML or classic UI): Defines the workflow steps.
  • Agent pool: Compute resources (Microsoft-hosted or self-hosted) that execute jobs.
  • Stages: Logical groupings (e.g., Build, Test, Deploy) with conditional execution.
  • Tasks: Atomic units of work (e.g., DownloadPipelineArtifact, AzureRmWebAppDeployment).
  • Variables & Variable Groups: Reusable values (e.g., subscription ID, app settings).

Service Connections & Security

Pipelines need secure access to Azure resources. A service connection establishes trust between Azure DevOps and Azure using a service principal (SP) with minimal required permissions. Follow the principle of least privilege — never use owner-level credentials.

Service connections can be scoped to:

  • Subscription: Full access (use with caution)
  • Resource Group: Recommended for most deployments
  • Azure Kubernetes Service (AKS): Direct cluster access
Security Best Practice: Store secrets (e.g., API keys, connection strings) in Azure Key Vault, then reference them in pipelines via keyVaultName and secretName parameters — never hardcode them in YAML files.

How It Works

Architecture of a CI/CD Pipeline in Azure

Here's a typical end-to-end CI/CD flow for a web app:

  1. Developer pushes code to GitHub or Azure DevOps Git repo.
  2. Pipeline triggers on push/PR (CI).
  3. Build stage: Restore dependencies, compile code, run unit tests, create build artifacts (e.g., ZIP, Docker image).
  4. Release stage: Deploy artifact to Azure (e.g., Azure App Service) using Azure CLI or ARM/Bicep templates.
  5. Post-deployment: Run smoke tests, validate health, notify stakeholders.

Every step is logged, versioned, and reproducible — with audit trails for compliance.

YAML vs. Classic Pipelines

Azure DevOps supports two pipeline types:

  • YAML Pipelines (Declarative, Versioned, Git-integrated): Pipeline definition stored alongside source code. Ideal for infrastructure-as-code, reproducibility, and team collaboration.
  • Classic Pipelines (UI-based, GUI-driven): Good for simple workflows or legacy projects, but less portable and harder to version-control.

We recommend YAML pipelines for all new workloads. They enable pipeline-as-code, peer reviews, and automated pipeline updates.

Integration with GitHub Actions

GitHub Actions is a CI/CD platform built into GitHub. Azure DevOps and GitHub Actions can coexist — for example, use GitHub for code hosting and PR reviews, then trigger Azure Pipelines for deployment to Azure. Alternatively, use GitHub Actions directly to deploy to Azure.

Both platforms support:

  • Matrix builds, parallel jobs, and reusable workflows
  • Integration with Azure services via Azure CLI, REST APIs, or Azure SDKs
  • Secure secrets management (via GitHub Secrets or Azure DevOps Library)

For teams using GitHub, GitHub Actions is often the most seamless choice. For teams heavily invested in Microsoft ecosystems (e.g., Azure DevOps Server, Microsoft Teams), Azure Pipelines offers deeper integration.

Exam Tip (AZ-104): Know how to configure service connections and service principals in Azure for pipeline authentication. Understand how to restrict permissions and rotate credentials securely.

Hands-On Lab: Build & Deploy a Node.js App to Azure App Service

In this lab, you'll create a CI/CD pipeline using Azure Pipelines to build and deploy a Node.js web app to Azure App Service. You'll use a YAML pipeline, secure secrets via Key Vault, and enforce code quality gates.

Prerequisites

  • Azure subscription (with Contributor or Owner role)
  • Azure DevOps organization and project
  • GitHub repo (or Azure DevOps Git repo)
  • Azure CLI installed locally (v2.30.0+)

Step 1: Provision Azure Resources

First, create an Azure App Service and Application Insights resource using Azure CLI. Replace placeholders with your values.

RESOURCE_GROUP="rg-devops-lab-$(date +%Y%m%d)"
LOCATION="eastus"
APP_NAME="app-devops-lab-$(date +%s)"
APP_PLAN_NAME="asp-devops-lab"

# Create resource group
az group create --name $RESOURCE_GROUP --location $LOCATION

# Create App Service plan (B1 Basic tier for cost efficiency)
az appservice plan create --name $APP_PLAN_NAME --resource-group $RESOURCE_GROUP --sku B1 --location $LOCATION

# Create web app
az webapp create --name $APP_NAME --resource-group $RESOURCE_GROUP --plan $APP_PLAN_NAME --runtime "NODE|18-lts"

# Enable Application Insights
az monitor app-insights component create --app $APP_NAME --resource-group $RESOURCE_GROUP --location $LOCATION
Pro Tip: Use $(date +%Y%m%d) and $(date +%s) for unique resource names — avoids naming conflicts in shared subscriptions.

Step 2: Set Up Service Connection (Azure DevOps)

  1. In Azure DevOps, go to Project Settings > Service connections.
  2. Click New service connection > Azure Resource Manager > Next.
  3. Select Service principal (automatic).
  4. Choose your subscription and resource group scope (e.g., the rg-devops-lab-... group).
  5. Name the connection azure-subscription-conn and click Verify.

This creates a service principal in Entra ID (formerly Azure AD) with permissions scoped to your resource group.

Step 3: Configure Secrets in Azure Key Vault

# Create Key Vault
KV_NAME="kv-devops-lab-$(date +%s)"
az keyvault create --name $KV_NAME --resource-group $RESOURCE_GROUP --location $LOCATION

# Add app settings as secrets (e.g., for Node.js app)
az keyvault secret set --vault-name $KV_NAME --name "APPINSIGHTS_INSTRUMENTATIONKEY" --value "your-ai-key"
az keyvault secret set --vault-name $KV_NAME --name "APPLICATIONINSIGHTS_CONNECTION_STRING" --value "your-ai-conn-string"

Step 4: Create a YAML Pipeline in Azure DevOps

  1. In your Azure DevOps project, go to Pipelines > Create Pipeline.
  2. Select Use classic editor > Azure Repos Git > choose your repo > Continue.
  3. Select Empty job > click View job layout.
  4. Switch to YAML tab and paste this pipeline:
trigger:
  branches:
    include:
      - main

pool:
  vmImage: 'ubuntu-latest'

variables:
  - group: 'devops-variables'
  - name: 'AZURE_RESOURCE_GROUP'
    value: 'rg-devops-lab-$(Build.BuildId)'
  - name: 'APP_NAME'
    value: 'app-devops-lab-$(Build.BuildId)'
  - name: 'KV_NAME'
    value: 'kv-devops-lab-$(Build.BuildId)'

steps:
- task: AzureCLI@2
  displayName: 'Deploy to Azure App Service'
  inputs:
    azureSubscription: 'azure-subscription-conn'
    scriptType: 'bash'
    scriptLocation: 'inlineScript'
    inlineScript: |
      # Build and package Node.js app
      npm install
      npm run build --if-present
      zip -r app.zip .

      # Download secrets from Key Vault (using managed identity)
      APPINSIGHTS_KEY=$(az keyvault secret show --vault-name $KV_NAME --name "APPINSIGHTS_INSTRUMENTATIONKEY" --query "value" -o tsv)
      APPINSIGHTS_CONN=$(az keyvault secret show --vault-name $KV_NAME --name "APPLICATIONINSIGHTS_CONNECTION_STRING" --query "value" -o tsv)

      # Set app settings in Azure App Service
      az webapp config appsettings set --name $APP_NAME --resource-group $RESOURCE_GROUP \
        --settings APPINSIGHTS_INSTRUMENTATIONKEY=$APPINSIGHTS_KEY \
        APPLICATIONINSIGHTS_CONNECTION_STRING=$APPINSIGHTS_CONN

      # Deploy artifact
      az webapp deploy --name $APP_NAME --resource-group $RESOURCE_GROUP \
        --src-url $(Pipeline.Workspace)/drop/s$(Build.BuildId).zip --type zip

- task: AzureCLI@2
  displayName: 'Run Smoke Test'
  inputs:
    azureSubscription: 'azure-subscription-conn'
    scriptType: 'bash'
    scriptLocation: 'inlineScript'
    inlineScript: |
      # Wait for deployment to stabilize
      sleep 30
      # Call app endpoint (example)
      RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" "https://$APP_NAME.azurewebsites.net/api/health")
      if [ "$RESPONSE" -ne 200 ]; then
        echo "Smoke test failed. HTTP status: $RESPONSE"
        exit 1
      fi
      echo "Smoke test passed (HTTP 200)"

Step 5: Create Variable Group

  1. In Azure DevOps, go to Pipelines > Library > + Variable group.
  2. Name: devops-variables
  3. Add variables:
    • RESOURCE_GROUP = rg-devops-lab
    • LOCATION = eastus
  4. Check Link from Azure Key Vault to connect to your Key Vault and auto-populate secrets.
Security Note: When using az keyvault secret show in pipelines, ensure your service connection uses a managed identity (not a service principal with secrets) to avoid credential exposure. Managed identities are automatically rotated and don't require secret rotation.

Step 6: Run & Monitor the Pipeline

Commit the YAML file to your main branch. The pipeline triggers automatically. Monitor execution in the Azure DevOps Pipelines UI:

  • Build stage: Compiles app, runs tests (e.g., npm test), creates artifact.
  • Deploy stage: Uses Azure CLI to configure and deploy to App Service.
  • Test stage: Validates health endpoint.

Screenshot description (text only): Azure DevOps Pipeline run page showing 3 stages: Build (green checkmark), Deploy (green checkmark), Smoke Test (green checkmark). Logs show Azure CLI commands executing successfully.

Exam Tip (AZ-104): Azure Pipelines supports stages with dependsOn, condition, and variables. You may be asked to design pipelines with approval gates (e.g., require manager approval before production deployment). Use approval steps in YAML.

Best Practices & Patterns

Environment Strategy

Use multiple environments (e.g., dev, test, prod) to enforce quality gates:

stages:
- stage: Build
  jobs:
  - job: BuildJob
    steps:
    - task: Npm@1
      inputs:
        command: 'install'

- stage: DeployToDev
  dependsOn: Build
  condition: eq(variables['Environment'], 'dev')
  jobs:
  - deployment: DeployDev
    environment: 'dev'
    strategy:
      runOnce:
        deploy:
          steps:
          - download: current
          - task: AzureRmWebAppDeployment@4
            inputs:
              appType: 'webApp'
              WebAppName: 'myapp-dev'

- stage: DeployToProd
  dependsOn: DeployToDev
  condition: and(succeeded(), eq(variables['Environment'], 'prod'), eq(variables['RequireApproval'], 'true'))
  jobs:
  - deployment: DeployProd
    environment: 'prod'
    strategy:
      canary:
        initialRun:
          deploymentInput: ...
        trigger:
          triggerConditions: ...
        approval:
          owners: ['devops-team@contoso.com']
          timeoutInMinutes: 10

Infrastructure as Code (IaC)

Integrate Bicep or ARM templates directly in pipelines:

- task: AzureResourceManagerTemplateDeployment@3
  displayName: 'Deploy Bicep Template'
  inputs:
    deploymentScope: 'Resource Group'
    azureResourceManagerConnection: 'azure-subscription-conn'
    subscriptionId: '$(subscriptionId)'
    resourceGroupName: '$(resourceGroupName)'
    location: '$(location)'
    templateLocation: 'Linked artifact'
    csmFile: 'infra/main.bicep'
    overrideParameters: '-environment $(environment)'

Image Builds & Containerization

For containerized apps, build and push to Azure Container Registry (ACR) in CI:

- task: Docker@2
  displayName: 'Build & Push Docker Image'
  inputs:
    containerRegistry: 'acr-service-connection'
    repository: 'myapp'
    command: 'buildAndPush'
    Dockerfile: '**/Dockerfile'
    tags: |
      $(Build.BuildNumber)
      latest

GitHub Actions Alternative

Same workflow in GitHub Actions (stored in .github/workflows/deploy.yml):

name: Deploy to Azure App Service

on:
  push:
    branches: [ main ]

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

    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: '18'

    - name: Install dependencies
      run: npm ci

    - name: Build
      run: npm run build --if-present

    - name: Archive artifacts
      uses: actions/upload-artifact@v4
      with:
        name: app-build
        path: dist/

    - name: Deploy to Azure
      uses: azure/webapps-deploy@v3
      with:
        app-name: 'app-devops-lab'
        slot-name: 'production'
        package: './dist'
        publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
Pro Tip: Use secrets.AZURE_WEBAPP_PUBLISH_PROFILE (exported from Azure App Service > Get Publish Profile) instead of service principals for simple deployments. For higher security, prefer service connections with managed identity.

Summary & Key Takeaways

In this lesson, you learned how Azure DevOps and GitHub Actions empower teams to implement robust CI/CD workflows — transforming manual deployments into automated, auditable, and secure processes. Key highlights:

  • YAML pipelines offer version-controlled, reproducible workflows — the industry standard for modern DevOps.
  • Service connections and managed identities provide secure, least-privilege access to Azure resources — critical for compliance.
  • Key Vault integration keeps secrets out of source code and logs, reducing breach risk.
  • Stages, environments, and approvals enforce governance (e.g., QA sign-off before production).
  • IaC integration (Bicep/ARM) ensures infrastructure changes follow the same CI/CD principles as application code.

Remember: CI/CD isn't just about automation — it's about building quality, security, and reliability into every stage of the software lifecycle. As you move forward, combine these practices with the security and cost controls from previous lessons to create a full cloud application lifecycle.

Next Steps

Ready to optimize performance and budget? In the next lesson, Azure Cost Management, Monitoring & Scaling, you'll learn how to monitor application health with Azure Monitor, set up alerts, implement autoscaling, and control spend using tags, budgets, and reservations.

Alternatively, revisit the previous lesson: Azure Security & Compliance: Entra ID, Governance, and Data Protection.

Practice Assessment & e-Certificate

Ready to Test Your Skills in This Track?

Take our interactive 15-question practice assessment. Score 90% or higher to unlock and download a professional practice completion e-Certificate!

Take DP-203 Practice Assessment →

Get Azure & AI Workflow Tips in Your Inbox

Join 5,000+ developers and cloud professionals. Zero spam, unsubscribe anytime.