Category: Azure DevOps

  • Working with Azure DevOps: Create a Project, Import Code, and Set the Default Branch πŸš€

    In this section, we’ll go step-by-step through the very first things you usually do after signing into Azure DevOps:

    • Create a new project inside your organization
    • Import an existing GitHub repository
    • Configure the correct default branch

    These steps are extremely important because they set up the foundation of your DevOps workflow.
    If the project is not organized properly β€” or the wrong branch is default β€” pipelines, pull requests, and deployments can behave incorrectly later.

    πŸ’‘ Key ideas to remember:

    • Organization = your company or workspace container
    • Project = a product/app inside the organization
    • Repository = the source code for that project
    • Default branch = the branch Azure DevOps uses for PRs, builds, and releases

    Video Explanation


    1️⃣ Create a Project in Azure DevOps

    Before you can store code or run pipelines, you need a project.

    A project acts as a workspace that contains:

    • Boards (work items)
    • Repos (code)
    • Pipelines (CI/CD)
    • Test plans
    • Artifacts

    Think of it as a folder that holds everything related to a single application.

    Steps

    1. Open https://dev.azure.com
    2. Create or select an Organization
    3. Click New Project
    4. Enter:
      • Project Name
      • Description
    5. Set visibility β†’ Private (recommended for learning & security)
    6. Click Create

    After creation, the URL will update to include:

    dev.azure.com/{organization-name}/{project-name}

    πŸ“Œ At this point your project is empty β€” no code yet β€” which we’ll fix in the next step.


    2️⃣ Import a Public GitHub Repository into Azure DevOps πŸ“¦

    Instead of starting from scratch, you can import an existing repository.
    Azure DevOps will copy the entire Git history β€” commits, branches, and files.

    This is useful when:

    • Migrating from GitHub to Azure DevOps
    • Testing pipelines on a sample app
    • Centralizing company code

    Because the repository is public, authentication is not required.

    Steps

    1. Go to Repos (left sidebar)
    2. Click Import repository
    3. Copy the GitHub repository HTTPS URL
    4. Paste the URL into Azure DevOps
    5. Click Import

    ⏳ Azure DevOps now clones the repository internally.

    What gets imported?

    • Full commit history
    • All branches
    • Folder structure
    • README and files

    ⚠️ Note:
    Private repositories require authentication (PAT or credentials).
    Public repositories do not.


    3️⃣ Set main as the Default Branch 🌿

    After importing, Azure DevOps automatically picks the default branch alphabetically β€” not logically.

    That means:

    dev   ← might become default ❌
    main ← what we actually want βœ…

    This matters because the default branch is used for:

    • Pull request targets
    • Pipeline triggers
    • Release deployments

    If it’s wrong, automation breaks later.

    Steps

    1. Open Repos β†’ Branches
    2. Find the main branch
    3. Click the three dots (β‹―) beside it
    4. Select Set as default branch

    You will now see the main branch marked as default.


    βœ”οΈ What You Have Achieved

    TaskPurpose
    Created projectWorkspace for DevOps workflow
    Imported repositoryAdded real application code
    Set default branchEnsures correct CI/CD behavior

    Create and Connect to a Linux Virtual Machine in Azure ☁️🐧

    In this section, we’ll set up a Linux virtual machine inside Azure and connect to it securely using SSH.
    This machine will later act as a self-hosted agent for running DevOps pipelines β€” meaning builds and deployments will run on your own server instead of Microsoft-hosted agents.

    Before starting, here’s what matters most:

    πŸ”‘ Why we create this VM

    • Run build & deployment jobs
    • Install custom tools (Docker, kubectl, etc.)
    • Control environment versions
    • Practice real production-like DevOps setups

    πŸ” How we connect

    • Using SSH (secure remote login)
    • With a key pair (.pem file)
    • Over port 22

    πŸ’‘ Important: Always keep the downloaded key safe β€” losing it means losing access to the VM.

    Video Explanation


    1️⃣ Creating a Linux VM in Azure Portal

    We’ll create an Ubuntu server that Azure DevOps can later use as an agent machine.

    A virtual machine automatically comes with supporting resources:

    • Network interface
    • Public IP
    • Disk storage
    • Security rules

    Azure creates these together so the VM can be accessed over the internet.

    Steps

    1. Open Azure Portal
    2. Click Create a resource β†’ Virtual Machine
    3. Configure the basics:
    SettingExample Value
    Resource GroupCreate new (e.g., Test RG)
    VM NameAny name (e.g., my-vm-azure)
    ImageUbuntu Server 22.04
    Usernameazureuser

    Configure Authentication πŸ”

    • Choose SSH Public Key
    • Generate new key pair
    • Give the key a name

    Azure will download a .pem file β€” keep it safe!

    This key is your password replacement.


    Networking Setup 🌐

    To allow remote login:

    • Allow inbound port β†’ SSH (22)

    Without this, connection will fail.


    Create the VM

    1. Review settings
    2. Click Create
    3. Download the private key file (.pem)

    After deployment completes, open the VM resource and note:

    • Public IP address
    • Resource group
    • Networking details

    Your Linux machine is now live πŸŽ‰


    2️⃣ Connecting to the Linux VM (SSH Login)

    Now we log into the server remotely from your computer.

    You can use:

    • PowerShell (Windows)
    • Terminal (Mac/Linux)
    • Windows Terminal

    Step 1: Go to Key Location

    Open terminal and navigate to the folder containing the .pem file.

    Example:

    cd Downloads

    Step 2: Run SSH Command

    Use this format:

    ssh -i yourkey.pem azureuser@PUBLIC_IP

    Example:

    ssh -i mykey.pem azureuser@20.55.xxx.xxx

    If successful, your prompt changes to:

    azureuser@vm-name:~$

    You are now inside the Azure Linux machine πŸ–₯️


    Fix Permission Error (Very Common ⚠️)

    You may see:

    “Permissions are too open”

    SSH blocks insecure keys.

    Fix (Windows PowerShell)

    Restrict file access so only you can read it.

    Fix Windows SSH key permission:

    icacls <key> /inheritance:r
    icacls <key> /grant:r "$($env:USERNAME):(R)"
    icacls <key> /remove "Authenticated Users" "BUILTIN\Users" "Everyone"
    

    Login:

    ssh -i <key> azureuser@<public-ip>

    After fixing permissions, run the SSH command again.


    βœ”οΈ What You Achieved

    TaskResult
    Created Linux VMCloud server ready
    Generated SSH keySecure authentication
    Opened port 22Remote connectivity
    Logged into VMReady for DevOps agent setup

    You now have your own cloud machine ready to install tools and run CI/CD pipelines πŸš€

    πŸš€ Setting Up a Self-Hosted Linux Agent for Azure DevOps

    In this section, we’ll configure a Linux Virtual Machine (VM) to act as a self-hosted agent for Azure DevOps pipelines. Instead of using Microsoft-hosted agents, your own VM will execute pipeline jobs β€” giving you more control, flexibility, and customization options.

    Here’s what we’ll cover:

    • 🐳 Installing and verifying Docker
    • 🧰 Installing additional utilities (if required)
    • πŸ” Generating a Personal Access Token (PAT)
    • πŸ— Creating an Agent Pool
    • πŸ”— Connecting the VM to Azure DevOps
    • β–Ά Running the self-hosted agent

    Video Explanation

    By the end of this section, your Linux VM will be fully ready to execute CI/CD pipeline jobs 🎯


    🐳 Install and Configure Docker

    Docker is commonly required in DevOps pipelines, especially for container-based builds and deployments.

    Since we are logged in as azureuser, we must grant this user permission to run Docker commands without sudo.

    πŸ”Ή Grant Docker Permissions

    Run:

    sudo usermod -aG docker azureuser

    This adds the user to the Docker group.

    πŸ”„ Restart Docker Service

    sudo systemctl restart docker

    Restarting ensures permission changes take effect.

    πŸšͺ Log Out and Log Back In

    Exit the VM session:

    exit

    Then reconnect using your SSH command. This refreshes group membership.

    βœ… Verify Docker Installation

    To confirm everything works correctly, pull the hello-world image:

    docker pull hello-world

    If the image downloads successfully:

    • βœ” Docker is installed
    • βœ” The service is running
    • βœ” The user has proper permissions

    Your Docker setup is now complete πŸŽ‰


    🧰 Install Additional Utilities (If Required)

    Because this is a self-hosted VM, we are responsible for maintaining it.

    Pipelines may require additional tools such as:

    • unzip
    • wget
    • git
    • language runtimes (Node, Java, etc.)

    Even if your current project doesn’t require them, it’s important to understand that future pipelines might.

    πŸ“¦ Example: Install unzip

    sudo apt install unzip -y

    πŸ’‘ Tip: Only install what your pipeline needs. Self-hosted agents provide flexibility β€” but also require proper maintenance.


    πŸ”— Connect the Linux VM to Azure DevOps

    Now we’ll connect the VM so that it can execute pipeline jobs from:

    • Azure DevOps

    This involves generating a Personal Access Token and configuring the VM as an agent.


    πŸ” Generate a Personal Access Token (PAT)

    The PAT allows secure communication between Azure DevOps and your VM.

    Steps:

    1. Go to your Azure DevOps organization.
    2. Click User Settings (top-right).
    3. Select Personal Access Tokens.
    4. Click New Token.

    Configure:

    • Provide a name
    • Select Full Access (sufficient for learning purposes)
    • Click Create
    • Copy and securely store the token πŸ“‹

    ⚠ In production, always follow the Principle of Least Privilege.


    πŸ— Create an Agent Pool

    Next, create a pool where your VM agent will be registered.

    1. Go to Organization Settings.
    2. Select Agent Pools.
    3. Click Add Pool.
    4. Choose Self-hosted.
    5. Provide a meaningful name (e.g., My Agent Pool).
    6. Allow access to all pipelines if required.
    7. Click Create.

    Your agent pool is now ready β€” but currently empty.


    πŸ’» Configure the Linux VM as an Agent

    From the Agent Pool page:

    1. Open your pool.
    2. Click New Agent.
    3. Select the Linux tab.
    4. Follow the provided instructions.

    Now switch to your Linux VM.

    πŸ“ Create a Working Directory

    mkdir myagent
    cd myagent

    Verify:

    ls

    πŸ“₯ Download the Agent Package

    Run the wget command shown in the Azure DevOps portal:

    wget <agent-download-url>

    Confirm the file exists:

    ls

    You should see a .tar.gz file.

    πŸ“¦ Extract the Package

    tar zxvf <agent-file-name>.tar.gz

    After extraction, you should see files including:

    • config.sh
    • run.sh

    βš™ Configure the Agent

    Run:

    ./config.sh

    Follow the prompts:

    • Accept the license (Y)
    • Enter your Azure DevOps organization URL
    • Choose PAT authentication (press Enter)
    • Paste your Personal Access Token
    • Enter the agent pool name (My Agent Pool)
    • Confirm or provide agent name

    If successful, the configuration will complete and return you to the terminal.


    β–Ά Start the Agent

    Run:

    ./run.sh

    If successful, you will see:

    Listening for Jobs

    Now return to:

    Organization Settings β†’ Agent Pools β†’ My Agent Pool

    You should see:

    • βœ” Your agent listed
    • βœ” Status: Online

    Your self-hosted Linux VM is now fully connected and ready to execute Azure DevOps pipeline jobs πŸš€


    🎯 Section Summary

    In this section, we:

    • Installed and verified Docker 🐳
    • Discussed installing additional utilities 🧰
    • Generated a Personal Access Token πŸ”
    • Created an Agent Pool πŸ—
    • Configured and started a self-hosted Linux agent πŸ’»

    Your environment is now prepared to run CI/CD pipelines using your own infrastructure β€” giving you greater flexibility and control over your DevOps workflows.

    πŸš€ Azure DevOps Pipeline: Build & Push Docker Image to Azure Container Registry

    In this section, we’ll walk through the complete setup required to build and push a Docker image using Azure DevOps.

    This includes:

    • πŸ“¦ Creating an Azure Container Registry (ACR)
    • πŸ”§ Creating a pipeline using the Docker template
    • πŸ“ Understanding repository structure
    • βš™οΈ Breaking down the full pipeline YAML code
    • πŸ”— Understanding how Build and Push stages work together

    By the end, you’ll clearly understand how the pipeline builds your Docker image and pushes it securely to Azure.

    Video Explanation


    πŸ“¦ 1️⃣ Create Azure Container Registry (ACR)

    Before a pipeline can push an image, we need a registry to store it.

    We create this inside:

    • Microsoft Azure

    πŸ›  Steps

    1. Go to the Azure Portal.
    2. Search for Container Registries.
    3. Click Create.
    4. Select:
      • Subscription
      • Resource Group
    5. Provide a unique registry name.
    6. Keep default settings (Basic SKU is fine).
    7. Click Review + Create β†’ Create.

    After deployment:

    • Open the registry.
    • Click Repositories in the left pane.

    At this point, it will be empty.
    Our pipeline will push images here.


    πŸ”§ 2️⃣ Create a New Pipeline in Azure DevOps

    Now we configure automation in:

    • Azure DevOps

    πŸ›  Steps

    1. Go to Pipelines β†’ Create Pipeline.
    2. Select Azure Repos Git.
    3. Choose your repository.
    4. Select the template:
      Docker: Build and Push an image to Azure Container Registry

    During setup, Azure DevOps will ask for:

    • Azure subscription
    • Container registry
    • Image repository name
    • Dockerfile path

    It will also create or use a service connection so DevOps can securely authenticate with Azure.


    πŸ“ 3️⃣ Understand the Repository Structure

    Before analyzing the pipeline, we need to understand the project structure.

    In the repository root, we have three microservices:

    • πŸ“ Result
    • πŸ“ Vote
    • πŸ“ Worker

    Each microservice:

    • Contains application code
    • Has its own Dockerfile
    • Requires its own pipeline

    In this section, we are working with the Result microservice.


    βš™οΈ Full Pipeline YAML Explained

    Below is the pipeline we configured:

    trigger:
    paths:
    include:
    - result/*resources:
    - repo: selfvariables:
    dockerRegistryServiceConnection: 'bc565285-3a3d-4b9b-b39e-52859b47c3ec'
    imageRepository: 'resultapp'
    containerRegistry: 'myACRForCiCd2.azurecr.io'
    dockerfilePath: '$(Build.SourcesDirectory)/result/Dockerfile'
    tag: '$(Build.BuildId)'pool:
    name: 'myagentpool'stages:
    - stage: Build
    displayName: Build stage
    jobs:
    - job: Build
    displayName: Build
    steps:
    - task: Docker@2
    displayName: Build image
    inputs:
    containerRegistry: '$(dockerRegistryServiceConnection)'
    repository: '$(imageRepository)'
    command: 'build'
    Dockerfile: 'result/Dockerfile'
    tags: '$(tag)'- stage: Push
    displayName: Push stage
    jobs:
    - job: Push
    displayName: Push
    steps:
    - task: Docker@2
    displayName: Push image
    inputs:
    containerRegistry: '$(dockerRegistryServiceConnection)'
    repository: '$(imageRepository)'
    command: 'push'
    Dockerfile: 'result/Dockerfile'
    tags: '$(tag)'

    Let’s break it down.


    πŸ” Trigger Section

    trigger:
    paths:
    include:
    - result/*

    This means:

    βœ… The pipeline runs only when changes are made inside the result folder.

    It prevents unnecessary builds when other microservices change.


    πŸ“¦ Resources Section

    resources:
    - repo: self

    self refers to the current repository.
    This is the default behavior.


    🧠 Variables Section

    Instead of hardcoding values inside tasks, we defined reusable variables:

    VariablePurpose
    dockerRegistryServiceConnectionAuthenticates to Azure
    imageRepositoryName of image in ACR
    dockerfilePathLocation of Dockerfile
    tagUnique image version

    We use:

    tag: $(Build.BuildId)

    Build.BuildId is a system-generated variable that uniquely identifies the pipeline run.

    Each build gets a unique Docker image tag.


    πŸ–₯ Pool Section

    pool:
    name: myagentpool

    This defines where the pipeline runs.

    Both stages use the same self-hosted agent pool.

    This is important because Docker images are stored locally on the agent machine.


    πŸ— Build Stage

    Structure:

    Stage β†’ Job β†’ Task

    The Build stage:

    • Contains one job
    • That job contains one Docker task
    • Command used: build

    What it does:

    πŸ‘‰ Builds the Docker image using result/Dockerfile
    πŸ‘‰ Tags it using $(Build.BuildId)

    The image is created locally on the agent machine.


    πŸš€ Push Stage

    The Push stage:

    • Contains one job
    • That job contains one Docker task
    • Command used: push

    What it does:

    πŸ‘‰ Pushes the Docker image to Azure Container Registry
    πŸ‘‰ Uses the same image name and tag


    πŸ”— How Build and Push Work Together

    There is no automatic transfer of images between stages.

    Here’s what actually happens:

    1️⃣ Build stage creates the image locally
    2️⃣ Image is tagged using Build ID
    3️⃣ Push stage runs on the same agent
    4️⃣ Push stage pushes that image to ACR

    The connection works because:

    • Same agent pool
    • Same image repository
    • Same tag

    If the stages ran on different agents, the push would fail because the image would not exist there.


    πŸ“Œ Complete Workflow Summary

    Here’s the full flow:

    1️⃣ Developer commits code in result folder
    2️⃣ Pipeline triggers automatically
    3️⃣ Build stage creates Docker image
    4️⃣ Image is tagged uniquely
    5️⃣ Push stage uploads image to ACR
    6️⃣ Image appears under ACR β†’ Repositories


    🎯 What This Pipeline Achieves

    In this section, we:

    • Created Azure Container Registry πŸ“¦
    • Created a Docker-based Azure DevOps pipeline πŸ”§
    • Used service connections for secure access πŸ”
    • Used variables for clean configuration 🧠
    • Built and pushed Docker images automatically πŸš€

    This setup forms the foundation of a container-based CI pipeline in Azure DevOps.