1 – Azure DevOps Mini-Project

Table of Contents

  1. Working with Azure DevOps: Create a Project, Import Code, and Set the Default Branch πŸš€
  2. Create and Connect to a Linux Virtual Machine in Azure ☁️🐧
  3. πŸš€ Setting Up a Self-Hosted Linux Agent for Azure DevOps
  4. πŸš€ Azure DevOps Pipeline: Build & Push Docker Image to Azure Container Registry

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.

Comments

Leave a Reply

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

TechMilestoneHub

Build Skills, Unlock Milestones

Β© 2025 TechMilestoneHub


The content on TechMilestoneHub is for educational purposes only and may not always reflect the latest official guidance. Tutorials, quizzes, and examples do not guarantee certification success or specific results. We are not affiliated with certification vendors unless stated. Some pages may contain affiliate links, which may earn us a commission at no extra cost to you. By using this site, you agree to use the information at your own risk. See our Disclaimer, Terms & Conditions, and Privacy Policy for details.