Getting Started with Azure DevOps Pipelines: From Hello World to Real-World Deployments¶
Learn how to build and deploy Azure DevOps Pipelines, from a simple "Hello World" to real-world multi-job deployments.
Hello-World Pipeline¶
Azure DevOps YAML Pipeline to Print "Hello World"
# Basic Azure DevOps Pipeline YAML to print "Hello World"
# 'trigger' defines which branch will trigger the pipeline automatically.
trigger:
- main # (1)!
# 'pool' specifies the agent (virtual machine) where the pipeline will run.
pool:
vmImage: ubuntu-latest # (2)!
# 'steps' define the tasks the pipeline will execute.
steps:
- script: echo "Hello World from Azure DevOps!" # (3)!
displayName: 'Print Hello World'
-
trigger — Start the Pipeline Automatically. The trigger field specifies the branch that kicks off the pipeline.
-
main
-
development
-
feature1
-
feature2
-
-
pool — Choose an Agent. The pool defines where the pipeline runs.
-
ubuntu-latest uses a ready-to-go Ubuntu machine provided by Microsoft.
-
windows-latest
-
macos-latest
-
-
steps — Define the Tasks. steps list the actions the agent should perform.
-
script: echo "Hello World..." simply runs a command to print a message in the pipeline logs.
-
script: run main.py #Run Python Script
-
script: Any other "Commands Here"
-
📌 Summary:
Concept | Description |
---|---|
trigger | Tells when to run (based on branch). |
pool | Tells where to run (which agent image). |
steps | Tells what to run (the actual commands). |
Variables¶
Inline variables inside YAML
# Azure DevOps Pipeline to print Hello World using variables
trigger:
- main
variables:
greeting: "Hello World from Azure DevOps with Variables!"
pool:
vmImage: ubuntu-latest
steps:
- script: echo $(greeting) #Calling Variable
displayName: 'Print Hello World using Variable'
Using External Variables in Azure DevOps Pipeline
Earlier, we learned how to define variables inside the YAML file itself.
Now, let's take the next step and separate variables cleanly.
-
Version 1: Using a file to store variables.
-
Version 2: Using a Variable Group from Azure DevOps Library.
Loading Variable from a File (for example, variables/vars.txt) and use it inside our pipeline.
- Create a file in your Repo in this location
variables/vars.txt
mkdir -p variables/vars.txt
variables:
greeting: "Hello World from Template!"
- Create the Main Pipeline YAML
trigger:
- main
# Load variables from an external YAML file
variables:
- template: variables/vars.yml # Load greeting variable from variables/vars.yml
pool:
vmImage: ubuntu-latest
steps:
- script: echo $(greeting)
displayName: 'Print Hello World from Template Variable'
This time, we will create a Variable Group from the Azure DevOps UI and use it inside the pipeline.
-
Create a Variable Group
-
Go to Azure DevOps Portal → Pipelines → Library → + Variable group
- Create a group named (for example):
hello-world-vars
- Add a variable:
- Name: greeting
- Value: Hello World from Variable Group!
✅ Save it.
- Create the Main Pipeline YAML
trigger:
- main
# Load variables from Azure DevOps Variable Groups
variables:
- group: hello-world-vars
pool:
vmImage: ubuntu-latest
steps:
- script: echo $(greeting)
displayName: 'Print Hello World from Variable Group'
Pre-defined Variables¶
Pre-defined variables in Azure DevOps are automatically available during pipeline execution. These variables provide useful information about the pipeline's execution environment, build system, and process details. These variables can be used in pipeline definitions to access dynamic data, such as paths, build numbers, and agent details.
Working Directory Variables
Variable Name | Description | Example Value |
---|---|---|
$(Build.SourcesDirectory) |
The directory where the source code is checked out. | /home/vsts/work/1/s |
$(Build.ArtifactStagingDirectory) |
The directory where build artifacts are staged before being published. | /home/vsts/work/1/a |
$(Build.BinariesDirectory) |
The directory where build output (binaries) are stored. | /home/vsts/work/1/b |
$(Build.DropDirectory) |
The directory for the final drop of artifacts or outputs. | /home/vsts/work/1/d |
Artifact Variables
Variable Name | Description | Example Value |
---|---|---|
$(Build.ArtifactStagingDirectory) |
The directory where the build artifacts are staged before being published. | /home/vsts/work/1/a |
$(Build.ArtifactName) |
The name of the artifact being published. | myArtifact.zip |
$(Build.BuildId) |
A unique ID of the current build. | 12345 |
$(Build.DefinitionName) |
The name of the build definition. | BuildPipeline |
Agent/System Variables
Variable Name | Description | Example Value |
---|---|---|
$(Agent.HomeDirectory) |
The home directory of the agent on the machine. | /home/vsts/agent |
$(Agent.OS) |
The operating system of the agent. | Linux |
$(Agent.WorkFolder) |
The directory used by the agent to store temporary files. | /home/vsts/work |
$(Agent.TempDirectory) |
The temporary directory for the agent during builds. | /home/vsts/temp |
$(Pipeline.Workspace) |
The workspace directory where pipeline data is stored. | /home/vsts/work/1/s |
$(Pipeline.BuildId) |
The unique identifier of the current build. | 12345 |
Build and Release Variables
Variable Name | Description | Example Value |
---|---|---|
$(Build.BuildId) |
A unique ID for the current build. | 12345 |
$(Build.DefinitionName) |
The name of the build pipeline definition. | CI-CD Pipeline |
$(Build.SourceBranch) |
The source branch for the current build. | refs/heads/main |
$(Build.Repository.Name) |
The name of the repository associated with the build. | MyRepo |
$(Release.ReleaseId) |
The unique identifier of the release. | 98765 |
$(Release.EnvironmentName) |
The name of the environment in the release pipeline. | Production |
Note
For more details on available variables in Azure DevOps, please refer to the official Microsoft documentation.
Agent Workspace Path: /home/vsts/work/s/a
Breakdown
Full Path Component | Description | Example Value |
---|---|---|
/home/vsts/ |
The base directory for the agent. | /home/vsts/ |
/work/ |
The working directory where the agent works. | /home/vsts/work/ |
/1/ |
A unique number identifying a specific build. | /home/vsts/work/1/ |
/s/ |
The source code directory where your repository is checked out. | /home/vsts/work/1/s/ |
Sample Pipeline
trigger:
- main
pool:
vmImage: ubuntu-latest
steps:
- checkout: self # Checkout the code from the main branch of the Azure Repo
- script: |
echo "Listing files in $(Build.SourcesDirectory):"
ls $(Build.SourcesDirectory) # List all files in the source directory
displayName: 'List Files from Repo'
Environment¶
Environment in Azure DevOps is a way to define a target place (like Dev, QA, Prod servers) where you deploy your application and control approvals, checks, history, etc.
Creating Environment
- Go to Azure DevOps → Pipelines → Environments.
- Create a New Environment (example: Dev-Environment).
- Inside that Environment, go to Approvals and Checks.
- Add an Approval → Example: Add yourself or your team to approve.
⚡ Super Simple Flow:
Code Push ➔ Pipeline Trigger ➔ Environment Approval Needed ➔ Approver Clicks "Approve" ➔ Pipeline Continues
trigger:
- main
variables:
- template: variables/vars.yml # Load greeting variable from variables/vars.yml
pool:
vmImage: ubuntu-latest
# Link this pipeline to an Environment
environment: Dev-Environment # Specify the environment name
# Define the steps
steps:
- script: echo $(greeting)
displayName: 'Print Hello World from Template Variable'
Info
✅ You can attach your pipeline to an Environment, and then configure approvals (like someone must manually approve) before the pipeline can proceed.
Jobs¶
- A job is a collection of steps that run together on the same agent.
- Each job gets its own clean machine (agent).
- If you have multiple jobs, they can run parallel or sequential based on how you design.
- Jobs help organize tasks like "Build", "Test", "Deploy" into separate logical blocks.
⚡ Super Simple Flow:
Job Start ➔ Step 1 ➔ Step 2 ➔ Step "n" ➔ Job End
trigger:
- main
variables:
- template: variables/vars.yml # Load greeting variable from external file
pool:
vmImage: ubuntu-latest
jobs:
- job: PrintJob # Define a Job named 'PrintJob'
displayName: 'Print Greeting Job'
environment: Dev-Environment # Attach Environment directly to Job ✅
steps: # Steps inside the Job
- script: echo $(greeting)
displayName: 'Print Hello World from Template Variable'
trigger:
- main
variables:
- template: variables/vars.yml # Load variables from external template
pool:
vmImage: ubuntu-latest
jobs:
- job: BuildJob
displayName: 'Build Job'
environment: Dev-Environment # Attach Environment directly to the Build job
steps:
- script: |
echo $(greeting) # Use the greeting variable from the template
echo "Building the project..."
displayName: 'Run Build Steps'
- job: DeployJob
displayName: 'Deploy Job'
dependsOn: BuildJob # This job depends on 'BuildJob'
environment: Dev-Environment # Attach the same environment to Deploy job (can be different too)
steps:
- script: |
echo $(greeting) # Use the greeting variable again
echo "Deploying the project..."
displayName: 'Run Deployment Steps'
Info
If you have only a single dependency (example: dependsOn a single job like dependsOn: BuildJob), you can skip square brackets and just write it directly without [ ].
trigger:
- main
variables:
- template: variables/vars.yml # Load variables from external template
pool:
vmImage: ubuntu-latest
jobs:
- job: BuildJob
displayName: 'Build Job'
environment: Dev-Environment # Attach Environment directly to the Build job
steps:
- script: |
echo $(greeting) # Use the greeting variable from the template
echo "Building the project..."
displayName: 'Run Build Steps'
- job: TestJob
displayName: 'Test Job'
dependsOn: BuildJob # This job depends on 'BuildJob'
environment: Test-Environment # Attach a different environment to the Test job
steps:
- script: |
echo $(greeting) # Use the greeting variable again
echo "Running tests..."
displayName: 'Run Test Steps'
- job: DeployJob
displayName: 'Deploy Job'
dependsOn: [BuildJob, TestJob] # This job depends on both 'BuildJob' and 'TestJob'
environment: Test-Environment # Attach the Test environment to Deploy job
steps:
- script: |
echo $(greeting) # Use the greeting variable again
echo "Deploying the project..."
displayName: 'Run Deployment Steps'
Info
When you have multiple dependencies (example: dependsOn multiple jobs like dependsOn: [BuildJob, TestJob]), you must use square brackets [ ] to list them.
📌 Summary:
Concept | Description |
---|---|
Steps | Small tasks (scripts, copy files, etc.) |
Job | A collection of steps |
Environment | Can be attached at the Job level too if needed |
dependsOn Usage | Use [ ] when depending on multiple jobs, no [ ] needed for single job |
Stages¶
A stage in Azure DevOps is like a big chapter inside your pipeline.
Each stage groups related steps together.
Example:
- One stage for building your code (Build
stage)
- Another stage for deploying your app (Deploy
stage)
✅ Stages help you organize your pipeline.
✅ You can also control approvals, conditions, and flows between stages.
In Simple Terms
If Build succeeds ➔ then go to Deploy after approval.
⚡ Super Simple Flow:
Trigger ➔ Build Stage ➔ Deploy Stage (Approval) ➔ Done
trigger:
- main
# Load variables from external file
variables:
- template: variables/vars.yml
# Use Ubuntu agent
pool:
vmImage: ubuntu-latest
# Define stages
stages:
- stage: Build
displayName: 'Build Stage'
jobs:
- job: BuildJob
displayName: 'Run Build Job'
steps:
- script: echo "Building project..."
displayName: 'Build Step'
- script: echo $(greeting)
displayName: 'Print Greeting Message'
- stage: Deploy
displayName: 'Deploy Stage'
dependsOn: Build # wait for build to complete
jobs:
- job: DeployJob
displayName: 'Run Deploy Job'
environment: Dev-Environment # Add Approval Here ✅
steps:
- script: echo "Deploying to server..."
displayName: 'Deployment Step'
📌 Summary:
Concept | Description |
---|---|
Steps | Small tasks (scripts, copy files, etc.) |
Job | A collection of steps |
Stages | A collection of jobs, representing a larger unit of work |
Environment | Can be attached at the Job level too if needed |