Exercises and Projects
1 - Hands-on exercises
Exercise 1: Initial configuration
Objective: Create an Azure DevOps organization and project.
Tasks:
- Create an organization on dev.azure.com
- Create a "MyApp" project with the Agile process
- Initialize a repo with a README
- Configure branch policies on main
Solution
# Installer Azure CLI et extension DevOps
az extension add --name azure-devops
# Se connecter
az login
az devops configure --defaults organization=https://dev.azure.com/MonOrg
# Créer le projet
az devops project create --name "MyApp" --process Agile --visibility private
# Configurer le projet par défaut
az devops configure --defaults project=MyApp
# Créer le repo (déjà créé avec le projet)
az repos list
# Configurer branch policy (via l'interface ou API)
# Project Settings > Repos > Policies > main
Exercise 2: CI pipeline
Objective: Create a CI pipeline for a Node.js application.
Solution
# azure-pipelines.yml
trigger:
- main
- develop
pool:
vmImage: 'ubuntu-latest'
variables:
NODE_VERSION: '18.x'
stages:
- stage: Build
jobs:
- job: BuildAndTest
steps:
- task: NodeTool@0
inputs:
versionSpec: $(NODE_VERSION)
displayName: 'Install Node.js'
- script: npm ci
displayName: 'Install dependencies'
- script: npm run lint
displayName: 'Lint'
- script: npm run build
displayName: 'Build'
- script: npm test -- --coverage --reporters=default --reporters=jest-junit
displayName: 'Test'
- task: PublishTestResults@2
inputs:
testResultsFormat: 'JUnit'
testResultsFiles: '**/junit.xml'
condition: always()
- task: PublishCodeCoverageResults@1
inputs:
codeCoverageTool: 'Cobertura'
summaryFileLocation: '$(System.DefaultWorkingDirectory)/coverage/cobertura-coverage.xml'
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: 'dist'
artifactName: 'app'
Exercise 3: CD pipeline
Objective: Add deployment stages with approvals.
Solution
# Ajouter après le stage Build
stages:
- stage: Build
# ... (voir exercice 2)
- stage: DeployDev
dependsOn: Build
jobs:
- deployment: DeployToDev
environment: 'Development'
strategy:
runOnce:
deploy:
steps:
- download: current
artifact: app
- task: AzureWebApp@1
inputs:
azureSubscription: 'Azure-Dev'
appType: 'webAppLinux'
appName: 'myapp-dev'
package: '$(Pipeline.Workspace)/app/**/*.zip'
- stage: DeployProd
dependsOn: DeployDev
condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
jobs:
- deployment: DeployToProd
environment: 'Production' # Requires approval
strategy:
runOnce:
deploy:
steps:
- download: current
artifact: app
- task: AzureWebApp@1
inputs:
azureSubscription: 'Azure-Prod'
appType: 'webAppLinux'
appName: 'myapp-prod'
package: '$(Pipeline.Workspace)/app/**/*.zip'
Exercise 4: Azure Boards
Objective: Create a sprint with work items.
Solution
# Créer une itération
az boards iteration project create \
--name "Sprint 1" \
--start-date 2024-01-15 \
--finish-date 2024-01-29
# Créer une Epic
az boards work-item create \
--type "Epic" \
--title "Authentication System"
# Créer une Feature
az boards work-item create \
--type "Feature" \
--title "User Login" \
--parent 1
# Créer des User Stories
az boards work-item create \
--type "User Story" \
--title "As a user, I want to login with email/password" \
--parent 2 \
--iteration "MyApp\Sprint 1"
az boards work-item create \
--type "User Story" \
--title "As a user, I want to reset my password" \
--parent 2 \
--iteration "MyApp\Sprint 1"
# Créer des Tasks
az boards work-item create \
--type "Task" \
--title "Create login form UI" \
--parent 3
az boards work-item create \
--type "Task" \
--title "Implement authentication API" \
--parent 3
2 - Complete project: Full Stack Web Application
Architecture
Project structure
my-fullstack-app/
├── .azure/
│ ├── pipelines/
│ │ ├── ci.yml
│ │ ├── cd.yml
│ │ └── templates/
│ │ ├── build-template.yml
│ │ └── deploy-template.yml
│ └── boards/
│ └── work-item-template.json
├── frontend/
│ ├── package.json
│ ├── Dockerfile
│ └── src/
├── backend/
│ ├── package.json
│ ├── Dockerfile
│ └── src/
├── infrastructure/
│ └── kubernetes/
│ ├── deployment.yml
│ └── service.yml
└── README.md
Complete CI pipeline
# .azure/pipelines/ci.yml
trigger:
branches:
include:
- main
- develop
paths:
exclude:
- README.md
- docs/**
pr:
branches:
include:
- main
variables:
- group: 'CI-Variables'
- name: dockerRegistry
value: 'myacr.azurecr.io'
stages:
- stage: Validate
jobs:
- job: Lint
pool:
vmImage: 'ubuntu-latest'
steps:
- script: npm run lint
workingDirectory: frontend
- script: npm run lint
workingDirectory: backend
- stage: Test
dependsOn: Validate
jobs:
- job: FrontendTests
pool:
vmImage: 'ubuntu-latest'
steps:
- template: templates/build-template.yml
parameters:
workingDirectory: 'frontend'
runTests: true
- job: BackendTests
pool:
vmImage: 'ubuntu-latest'
steps:
- template: templates/build-template.yml
parameters:
workingDirectory: 'backend'
runTests: true
- stage: Build
dependsOn: Test
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
jobs:
- job: BuildImages
pool:
vmImage: 'ubuntu-latest'
steps:
- task: Docker@2
inputs:
containerRegistry: 'ACR-Connection'
repository: 'frontend'
command: 'buildAndPush'
Dockerfile: 'frontend/Dockerfile'
tags: |
$(Build.BuildId)
latest
- task: Docker@2
inputs:
containerRegistry: 'ACR-Connection'
repository: 'backend'
command: 'buildAndPush'
Dockerfile: 'backend/Dockerfile'
tags: |
$(Build.BuildId)
latest
CD pipeline
# .azure/pipelines/cd.yml
trigger: none
resources:
pipelines:
- pipeline: CI
source: 'MyApp-CI'
trigger: true
variables:
- group: 'CD-Variables'
stages:
- stage: DeployDev
jobs:
- deployment: Deploy
environment: 'Development'
strategy:
runOnce:
deploy:
steps:
- task: KubernetesManifest@0
inputs:
action: 'deploy'
kubernetesServiceConnection: 'AKS-Dev'
namespace: 'development'
manifests: |
infrastructure/kubernetes/*.yml
containers: |
$(dockerRegistry)/frontend:$(resources.pipeline.CI.runID)
$(dockerRegistry)/backend:$(resources.pipeline.CI.runID)
- stage: DeployStaging
dependsOn: DeployDev
jobs:
- deployment: Deploy
environment: 'Staging'
strategy:
runOnce:
deploy:
steps:
- task: KubernetesManifest@0
inputs:
action: 'deploy'
kubernetesServiceConnection: 'AKS-Staging'
namespace: 'staging'
manifests: |
infrastructure/kubernetes/*.yml
- stage: DeployProduction
dependsOn: DeployStaging
jobs:
- deployment: Deploy
environment: 'Production'
strategy:
canary:
increments: [10, 50]
preDeploy:
steps:
- script: echo "Pre-deploy checks"
deploy:
steps:
- task: KubernetesManifest@0
inputs:
action: 'deploy'
kubernetesServiceConnection: 'AKS-Prod'
namespace: 'production'
manifests: |
infrastructure/kubernetes/*.yml
3 - Review quiz
-
What is the difference between Azure Repos and GitHub?
-
What are the 5 services of Azure DevOps?
-
How do you secure secrets in a pipeline?
-
What is a branch policy?
-
How do you configure deployment approvals?
Answers
-
Azure Repos is the Git service integrated into Azure DevOps, with native integration to the other services. GitHub is an independent platform but can be connected to Azure Pipelines.
-
Azure Boards, Azure Repos, Azure Pipelines, Azure Artifacts, Azure Test Plans.
-
Use Variable Groups marked as secrets, Azure Key Vault, or the secrets of Service Connections.
-
Rules applied to branches (required reviewers, build validation, etc.) to protect the code.
-
In Environments > Production > Approvals and checks, add Approvals with the approvers.
4 - Microsoft certification
Azure DevOps Engineer Expert (AZ-400)
| Domain | Weight |
|---|---|
| Design and implement processes and communications | 10-15% |
| Design and implement source control | 15-20% |
| Design and implement build and release pipelines | 40-45% |
| Develop a security and compliance plan | 10-15% |
| Implement an instrumentation strategy | 10-15% |
Resources
Course summary
Congratulations! You have completed the Azure DevOps course.
You now have a firm grasp of:
- Azure Repos for source control
- Azure Boards for project management
- Azure Pipelines for CI/CD
- Azure Artifacts for packages
- Azure Test Plans for testing
- The security best practices
Next steps
- Practice with real projects
- Take the AZ-400 certification
- Explore integrations with Azure Cloud
- Study GitHub Actions as an alternative