Azure DevOps: From GitHub to Live – A Beginner’s CI/CD Guide

Azure DevOps: Automating your software delivery process is no longer a “nice to have”—it’s a must. Manually building, testing, and deploying code is slow, error-prone, and… well, just not very fun. This is where Continuous Integration/Continuous Delivery (CI/CD) comes in.

You’ve got your code in a GitHub repository, and you’ve heard the Azure cloud is the place to run it. But how do you connect the two? How do you make it so that merging a new feature to your main branch automatically builds and deploys your application?

If you’re doing this for the first time, it can seem intimidating. This guide will walk you through every single step, from a commit in GitHub to a live website on Azure.

We will build a multi-stage YAML pipeline, which is the modern, code-based way to define your CI/CD process right alongside your application code.

📋 What You’ll Need (The Prerequisites)

Before we start, make sure you have the following:

  1. A GitHub Repository: This should contain a simple application (e.g., a Node.js, Python, or .NET web app). For this guide, we’ll assume a basic Node.js app that has a package.json file with build and start scripts.
  2. An Azure DevOps Organization: This is free to create. Go to dev.azure.com and sign in with your Microsoft account.
  3. An Azure Subscription: This is where we’ll deploy our application. You’ll need an “Azure App Service” to host the website. You can get a free trial account if you don’t have one.

Step 1: Create Your “Target” in Azure

We need a place to deploy our code. The easiest way to start is with an Azure App Service.

  1. Log in to the Azure Portal.
  2. Click “Create a resource” in the top-left corner.
  3. Search for “App Service” and click “Create”.
  4. Fill out the basics:
    • Subscription: Choose your subscription.
    • Resource Group: Create a new one (e.g., MyWebApp-RG).
    • Name: Give your app a globally unique name (e.g., my-awesome-cicd-app-123). This will be part of its URL.
    • Runtime stack: Select the one that matches your code (e.g., Node 18 LTS, .NET 7, etc.).
    • Operating System: Choose Linux or Windows.
    • App Service Plan: A plan defines the underlying server. You can create a new one. The F1 (Free) tier is perfect for this demo.
  5. Click “Review + create” and then “Create”. Wait for it to deploy. You now have an empty “bucket” to deploy your code into.

Step 2: Create Your Azure DevOps Project

  1. Go to your Azure DevOps organization (e.g., https://dev.azure.com/YourOrgName).
  2. Click “New project” in the top-right.
  3. Give it a Project name (e.g., MyGitHub-Pipeline) and set the Visibility to Private.
  4. Click “Create”.

Step 3: Create Your First (CI) Pipeline

This is where we connect Azure DevOps to your GitHub repository.

  1. Inside your new project, navigate to Pipelines in the left-hand menu.
  2. Click the “Create Pipeline” button.
  3. On the “Where is your code?” screen, select “GitHub”.
  4. You may be prompted to authorize Azure Pipelines. Follow the steps to install the Azure Pipelines GitHub App on your chosen repository. This is crucial as it gives Azure DevOps permission to “see” your code and set up webhooks.
  5. After authorizing, select the GitHub repository you want to build.
  6. On the “Configure your pipeline” screen, Azure DevOps will analyze your code. It will likely suggest a template. For example, if it sees a package.json, it will suggest “Node.js”. Select this suggested template.

You will now see a new file called azure-pipelines.yml. This is the heart of your pipeline!


Step 4: Understand and Refine the CI (Build) YAML

The template Azure DevOps gives you is a great start. It sets up the CI (Continuous Integration) part, which is responsible for building your code and packaging it.

Let’s modify it to be a complete CI/CD pipeline. Your file will look something like this. Replace the entire file content with this:

# This pipeline is triggered on pushes to the 'main' branch.
# This is what fires when you merge a pull request!
trigger:
- main

# This uses a Microsoft-hosted agent (a temporary virtual machine)
pool:
  vmImage: 'ubuntu-latest'

# This defines the two main stages: Build and Deploy
stages:
- stage: Build
  displayName: 'Build Stage'
  jobs:
  - job: BuildJob
    displayName: 'Build'
    steps:
    # 1. Select the Node.js version to use
    - task: NodeTool@0
      inputs:
        versionSpec: '18.x' # Change this to your app's version
      displayName: 'Install Node.js'

    # 2. Run 'npm install'
    - script: |
        npm install
      displayName: 'npm install'

    # 3. Run the 'build' script from your package.json (if you have one)
    - script: |
        npm run build --if-present
      displayName: 'npm run build'

    # 4. Archive all the built files into a .zip file
    - task: ArchiveFiles@2
      inputs:
        rootFolderOrFile: '$(Build.SourcesDirectory)' # The root of your code
        includeRootFolder: false
        archiveType: 'zip'
        archiveFile: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip' # The output .zip
        replaceExistingArchive: true
      displayName: 'Archive files'

    # 5. This is the most important CI step!
    # It "publishes" the .zip file so the Deploy stage can use it.
    - task: PublishPipelineArtifact@1
      inputs:
        targetPath: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip'
        artifactName: 'drop' # We'll call our artifact 'drop'
        publishLocation: 'pipeline'
      displayName: 'Publish Artifact'

# ----------------- DEPLOY STAGE -----------------

- stage: Deploy
  displayName: 'Deploy Stage'
  # This stage will only run if the 'Build' stage succeeds
  dependsOn: Build
  condition: succeeded() 
  jobs:
  - deployment: DeployJob
    displayName: 'Deploy to Azure App Service'
    # 'environment' is a logical wrapper for your App Service
    environment: 'production' 
    pool:
      vmImage: 'ubuntu-latest'
    strategy:
      runOnce:
        deploy:
          steps:
          # 1. Download the 'drop' artifact we published in the Build stage
          - task: DownloadPipelineArtifact@2
            inputs:
              buildType: 'current'
              downloadType: 'single'
              artifactName: 'drop'
              downloadPath: '$(System.ArtifactsDirectory)'

          # 2. This is the task that deploys to your App Service
          - task: AzureWebApp@1
            displayName: 'Deploy Azure Web App'
            inputs:
              # This is the "permission slip" we will create next
              azureSubscription: '<YOUR_SERVICE_CONNECTION_NAME>' 
              # This is the unique name you created in Step 1
              appName: '<YOUR_APP_SERVICE_NAME>' 
              # This points to the .zip file we just downloaded
              package: '$(System.ArtifactsDirectory)/drop/$(Build.BuildId).zip'

Step 5: Create the “Permission Slip” (Service Connection)

Did you see the <YOUR_SERVICE_CONNECTION_NAME> placeholder in the YAML? Our pipeline’s Deploy stage needs permission to access your Azure account. We provide this by creating a Service Connection.

  1. In your Azure DevOps project, click Project settings in the bottom-left corner.
  2. Under the “Pipelines” section, click Service connections.
  3. Click “Create service connection”.
  4. Select “Azure Resource Manager” and click Next.
  5. Select “Workload Identity federation (automatic)”. This is the most secure and modern method. Click Next.
  6. The tool will sign you into Azure. Select the Subscription and Resource Group that contain your App Service from Step 1.
  7. Give the connection a Service connection name. For example, AzureProdConnection.
  8. IMPORTANT: Make sure to check the box “Grant access permission to all pipelines”.
  9. Click “Save”.

You now have a secure “permission slip.” Go back to your azure-pipelines.yml file (click Pipelines > select your pipeline > Edit) and replace:

  • <YOUR_SERVICE_CONNECTION_NAME> with the name you just created (e.g., AzureProdConnection).
  • <YOUR_APP_SERVICE_NAME> with the name of your App Service (e.g., my-awesome-cicd-app-123).

Click “Save” in the top right. A new file azure-pipelines.yml will be committed to your GitHub repository.


Step 6: See the Magic Happen!

You are all set. Now, let’s test the full workflow.

  1. In GitHub, create a new branch from main (e.g., feature/update-readme).
  2. Make a small change to a file (e.g., add a line to README.md).
  3. Commit and push this change to your new branch.
  4. Go to your GitHub repository and create a Pull Request (PR) to merge your feature/update-readme branch into the main branch.
  5. MERGE THE PULL REQUEST.

This is the moment. The merge is a “push” to the main branch.

Now, go back to Azure DevOps and click on “Pipelines”:

You will see your pipeline has automatically started running!

Click on it. You will see the two stages:

  1. The Build stage will run first. You can click it to watch the logs as it installs Node.js, builds your code, and publishes the artifact.
  2. Once “Build” is green, the Deploy stage will automatically start. It will download the artifact and push it to your Azure App Service.

Once the “Deploy” stage is green, your CI/CD pipeline is complete.

You can now visit your website at https://<YOUR_APP_SERVICE_NAME>.azurewebsites.net to see your deployed application!

🥳 You Did It! What’s Next?

Congratulations! You have successfully built a full-featured, automated CI/CD pipeline. Any time a developer merges code into the main branch, it will be automatically built, tested (if you add test steps), and deployed to your users.

From here, you can explore:

  • Adding a Test Step: Add npm test to your Build stage.
  • PR Validation: Add a pr: trigger to your YAML to run the Build stage on Pull Requests (before they are merged).
  • Environments: Create a “Staging” environment to deploy to first, with an approval check before deploying to “Production.”

You’ve taken the most important step into the world of DevOps. Happy building!

Find more blog post at: https://subhadip.ca/blog/

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top