Complete CI/CD Pipeline for .NET 9 Microservices on Azure Using Azure DevOps, Docker, ACR and AKS
Introduction
Modern enterprise applications require a reliable and automated process for building, testing, packaging, and deploying applications across multiple environments.
In a typical microservices-based application, developers continuously make changes to individual services. Manually building and deploying these services is time-consuming and error-prone.
This is where CI/CD — Continuous Integration and Continuous Delivery/Deployment — becomes extremely important.
In this article, we will build a complete CI/CD pipeline for the following enterprise architecture:
Angular Web Application
.NET 9 Web API / Microservices
Docker
Azure Container Registry (ACR)
Azure Kubernetes Service (AKS)
Azure SQL Database
Azure Service Bus
Azure Key Vault
Azure DevOps
Kubernetes Ingress
DEV, QA, UAT and PRODUCTION environments
We will specifically use the Azure DevOps Classic/Visual UI approach, so you can understand how to configure the pipeline directly through the Azure DevOps portal without initially writing an azure-pipelines.yml file.
Note: Microsoft recommends YAML pipelines for new development, but Classic pipelines remain useful for understanding the Azure DevOps pipeline concepts and for organizations that still use the Classic UI.
1. What Are We Going to Build?
The overall CI/CD architecture will look like this:
Developer
|
| Git Push
v
Azure Repos
|
v
+-----------------------------+
| CI / Build Pipeline |
| |
| 1. Restore |
| 2. Build |
| 3. Unit Test |
| 4. Publish |
| 5. Docker Build |
| 6. Docker Push |
+-------------+---------------+
|
v
Azure Container Registry
|
| Docker Image
v
+-----------------------------+
| CD / Release Pipeline |
| |
| DEV |
| | |
| v |
| QA |
| | |
| v |
| UAT |
| | |
| Approval |
| | |
| v |
| PRODUCTION |
+-------------+---------------+
|
v
AKS
|
+------+------+
| | |
v v v
Order Customer Product
API API API
The basic principle is:
Code → Build → Test → Docker Image → ACR → Deploy → AKS
2. Prerequisites
Before creating the pipeline, we need several Azure and Azure DevOps resources.
Azure Resources
You should have:
Azure Subscription
Resource Group
Azure Container Registry
Azure Kubernetes Service
Azure SQL Database
Azure Service Bus
Azure Key Vault
Azure Monitor / Application Insights
Azure DevOps
You should have:
Azure DevOps Organization
Azure DevOps Project
Azure Repos
Pipeline permissions
Microsoft-hosted agent capability
Microsoft-hosted agents are suitable for many standard .NET builds. Self-hosted agents can be used when an organization requires custom tooling or private-network access.
3. Example Enterprise Application
Let's assume our solution has the following structure:
EnterpriseApp
│
├── EnterpriseApp.sln
│
├── src
│ ├── CustomerService
│ │ └── CustomerService.csproj
│ │
│ ├── OrderService
│ │ └── OrderService.csproj
│ │
│ └── ProductService
│ └── ProductService.csproj
│
├── tests
│ └── OrderService.Tests
│
├── Dockerfile
│
└── k8s
├── namespace.yaml
├── order-deployment.yaml
├── order-service.yaml
├── customer-deployment.yaml
├── customer-service.yaml
└── ingress.yaml
For our first example, we will deploy:
OrderService
Once the process is understood, the same approach can be applied to:
CustomerService
ProductService
InventoryService
PaymentService
NotificationService
Other microservices
4. Step 1 — Create an Azure DevOps Project
Open your Azure DevOps organization and create a project.
For example:
Organization
|
└── EnterpriseProject
Inside the project, Azure DevOps provides services such as:
Boards
Repos
Pipelines
Test Plans
Artifacts
For our CI/CD implementation, the most important areas are:
Repos
Pipelines
Project Settings
5. Step 2 — Add Source Code to Azure Repos
Navigate to:
Repos
|
└── Files
Create or import your Git repository.
For example:
EnterpriseApp
The repository might contain:
EnterpriseApp.sln
src/
CustomerService/
OrderService/
ProductService/
tests/
OrderService.Tests/
Dockerfile
k8s/
Developers can then work with the repository using Git:
git clone <repository>
After making changes:
git add .
git commit -m "Update order validation"
git push
This Git push can eventually trigger the CI pipeline automatically.
6. Step 3 — Create Azure Container Registry
The Azure Container Registry (ACR) stores the Docker images generated by the CI pipeline.
In the Azure Portal:
Create a resource
↓
Container Registry
For example:
Registry Name:
enterpriseacr
The registry endpoint could be:
enterpriseacr.azurecr.io
Our Docker image can then be tagged as:
enterpriseacr.azurecr.io/order-service:1.0
or, preferably, with a unique build number:
enterpriseacr.azurecr.io/order-service:1234
Using a unique build identifier makes it easier to identify exactly which source version produced a container image.
7. Step 4 — Create Azure Kubernetes Service
Create an AKS cluster from the Azure Portal or Azure CLI.
For example:
EnterpriseAKS
An AKS cluster contains:
AKS Cluster
|
├── Node Pools
|
├── Kubernetes Control Plane
|
└── Workloads
After connecting your local environment to the cluster, you can verify the nodes:
kubectl get nodes
8. Step 5 — Connect ACR with AKS
AKS needs permission to pull Docker images from Azure Container Registry.
A common Azure CLI approach is:
az aks update \
--resource-group EnterpriseRG \
--name EnterpriseAKS \
--attach-acr enterpriseacr
The relationship becomes:
Azure Container Registry
|
| Pull Docker Image
v
AKS
This is important because the CI pipeline pushes the image into ACR, while AKS pulls that image when deploying the application.
9. Step 6 — Create Azure DevOps Service Connection
Azure DevOps needs permission to access Azure resources.
Navigate to:
Azure DevOps
↓
Project Settings
↓
Service connections
↓
New service connection
Select:
Azure Resource Manager
Configure the connection according to your organization's authentication and security requirements.
For example:
Service Connection Name:
Azure-Enterprise-Connection
Conceptually:
Azure DevOps
|
| Service Connection
v
Azure Subscription
|
+---- ACR
+---- AKS
+---- Other Azure Resources
Service connections are a critical security boundary, so they should be granted only the permissions required by the pipeline.
10. Step 7 — Create the CI Build Pipeline
Now we can create the Continuous Integration pipeline.
Navigate to:
Pipelines
↓
Builds
Depending on your Azure DevOps UI, select the option to create a new pipeline and choose the Classic editor.
The Classic editor allows you to configure the build process using the Azure DevOps UI rather than writing YAML.
Select:
Azure Repos Git
Then select:
Project:
EnterpriseProject
Repository:
EnterpriseApp
Branch:
main
Click:
Continue
11. Step 8 — Select Empty Job
Instead of allowing Azure DevOps to automatically create all the tasks, select:
Empty Job
You should now see something similar to:
Agent Job 1
This approach is useful when learning CI/CD because you can understand each pipeline task individually.
12. Step 9 — Configure the Build Agent
Select:
Agent Job 1
Choose an agent specification.
For example:
ubuntu-latest
or:
windows-latest
For Docker and Linux-based containers, Ubuntu is commonly convenient.
Our pipeline now starts with:
Agent Job
|
v
Use .NET SDK
13. Step 10 — Install / Select .NET SDK
Add a task:
+
Search for the .NET SDK task.
Configure the required .NET version.
For example:
SDK Version:
9.x
This ensures that the build agent uses the expected .NET SDK version.
14. Step 11 — Restore NuGet Packages
Add a .NET Core task.
Configure:
Command:
restore
For example:
Path to project:
EnterpriseApp.sln
The pipeline becomes:
Use .NET SDK
|
v
Restore
This downloads the NuGet dependencies required by the solution.
15. Step 12 — Build the Application
Add another .NET task.
Configure:
Command:
build
Project:
EnterpriseApp.sln
Arguments:
--configuration Release --no-restore
The pipeline is now:
Use .NET SDK
|
v
Restore
|
v
Build
The equivalent .NET CLI operation is:
dotnet build EnterpriseApp.sln \
--configuration Release \
--no-restore
16. Step 13 — Execute Unit Tests
Add another .NET task.
Configure:
Command:
test
Project:
tests/**/*.csproj
Arguments:
--configuration Release --no-build
The pipeline becomes:
Restore
|
v
Build
|
v
Unit Tests
If the unit tests fail:
Build ❌
the pipeline should stop.
This is an important CI principle:
Code should not progress toward deployment if automated tests are failing.
17. Step 14 — Publish the .NET Application
Add another .NET task.
Configure:
Command:
publish
Project:
src/OrderService/OrderService.csproj
Configuration:
Release
Output:
$(Build.ArtifactStagingDirectory)/order-service
The flow becomes:
Build
|
v
Test
|
v
Publish
|
v
Build Artifact
18. Step 15 — Build the Docker Image
Now we move from application build to containerization.
Add a Docker task.
Select:
Docker
Command:
Build
Configure the Azure Container Registry connection.
Repository:
order-service
Dockerfile:
$(Build.SourcesDirectory)/src/OrderService/Dockerfile
Tag:
$(Build.BuildId)
The resulting image could be:
enterpriseacr.azurecr.io/order-service:1234
19. Step 16 — Push Docker Image to ACR
Add another Docker task.
Select:
Docker
Command:
Push
Repository:
order-service
Tag:
$(Build.BuildId)
The CI process is now:
Git Push
|
v
Restore
|
v
Build
|
v
Unit Test
|
v
Publish
|
v
Docker Build
|
v
Docker Push
|
v
Azure Container Registry
This is the core Continuous Integration workflow.
20. Step 17 — Save and Run the CI Pipeline
Save the pipeline with a name such as:
CI-OrderService
Then select:
Save & Queue
Run the pipeline.
You should see tasks such as:
✔ Restore
✔ Build
✔ Unit Test
✔ Publish
✔ Docker Build
✔ Docker Push
If everything succeeds:
BUILD SUCCESSFUL
21. Step 18 — Verify the Docker Image in ACR
Open:
Azure Portal
↓
Container Registry
↓
Repositories
You should see:
order-service
Inside the repository:
order-service
|
└── 1234
The tag 1234 represents the pipeline build number.
22. Step 19 — Create the CD / Release Pipeline
The next step is Continuous Delivery/Deployment.
In the Classic pipeline model, the release pipeline is separate from the build pipeline.
Navigate to:
Pipelines
↓
Releases
↓
New pipeline
Select:
Empty Job
Rename the first stage:
DEV
A typical enterprise release pipeline can contain:
DEV
|
v
QA
|
v
UAT
|
v
PRODUCTION
23. Step 20 — Configure the Release Artifact
Add the output from the CI/build process as the release artifact.
For containerized applications, the key deployment input is the Docker image and its version/tag stored in ACR.
Conceptually:
CI Pipeline
|
v
Docker Image
|
v
ACR
|
v
Release Pipeline
|
v
AKS
24. Step 21 — Configure DEV Deployment
Open the DEV stage.
Add a Kubernetes deployment task.
Configure the Azure connection:
Connection Type:
Azure Resource Manager
Select:
Azure Subscription:
Azure-Enterprise-Connection
Then select the AKS cluster:
EnterpriseAKS
Use a namespace such as:
dev
This allows the same AKS cluster to host workloads for multiple environments when that is appropriate for the organization's architecture.
25. Kubernetes Deployment
A simplified Kubernetes Deployment might look like:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: enterpriseacr.azurecr.io/order-service:1234
ports:
- containerPort: 8080
The important part is:
image: enterpriseacr.azurecr.io/order-service:1234
When a new version is released, the image tag changes.
For example:
Old:
order-service:1233
becomes:
New:
order-service:1234
Kubernetes then performs a rollout to replace the old application version with the new one.
26. DEV Deployment Flow
The deployment now looks like:
ACR
|
| order-service:1234
v
AKS
|
v
DEV Namespace
|
+---- Pod 1
|
+---- Pod 2
|
+---- Pod 3
Running multiple replicas provides application redundancy and enables Kubernetes to distribute traffic across available Pods.
27. Step 22 — Create QA Stage
Add another stage:
QA
Configure the deployment to target:
QA
and use the appropriate Kubernetes namespace:
qa
The pipeline becomes:
DEV
|
v
QA
Configure the QA stage to execute after successful DEV deployment.
28. Step 23 — Create UAT Stage
Create another stage:
UAT
The release flow becomes:
DEV
|
v
QA
|
v
UAT
UAT is generally used for business/user acceptance validation before production.
29. Step 24 — Create Production Stage
Create the final stage:
PRODUCTION
The complete release flow is now:
DEV
|
v
QA
|
v
UAT
|
v
PRODUCTION
30. Step 25 — Add Production Approval
Production deployments should normally have appropriate controls.
Instead of allowing:
Developer
|
v
DEV
|
v
QA
|
v
UAT
|
v
PRODUCTION
without any control, introduce an approval gate:
DEV
|
v
QA
|
v
UAT
|
v
+----------------------+
| Production Approval |
| |
| Lead / Manager |
+----------+-----------+
|
v
PRODUCTION
In the Classic Release pipeline, configure:
PRODUCTION
↓
Pre-deployment conditions
↓
Pre-deployment approvals
Add the authorized reviewers according to your organization's release-management process.
31. Complete CI/CD Architecture
The complete architecture now looks like this:
DEVELOPER
|
| Git Push
v
+--------------+
| Azure Repos |
+------+-------+
|
v
+----------------------+
| CI / BUILD |
| |
| Restore |
| Build |
| Unit Test |
| Publish |
| Docker Build |
| Docker Push |
+----------+-----------+
|
v
+----------------------+
| Azure Container |
| Registry (ACR) |
+----------+-----------+
|
v
+----------------------+
| CD / RELEASE |
+----------+-----------+
|
v
+---------+
| DEV |
+----+----+
|
v
+---------+
| QA |
+----+----+
|
v
+---------+
| UAT |
+----+----+
|
Approval
|
v
+---------------+
| PRODUCTION |
+-------+-------+
|
v
AKS
|
+---------------+---------------+
| | |
v v v
Customer API Order API Product API
32. Where Does Ingress Fit?
Ingress is part of the Kubernetes application architecture. It is not a replacement for Azure DevOps.
A typical AKS architecture can look like:
Internet
|
v
+--------------+
| Ingress |
| / Gateway |
+------+-------+
|
+-------------+-------------+
| | |
v v v
Customer API Order API Product API
| | |
v v v
Pods Pods Pods
| | |
+-------------+-------------+
|
+--------+--------+
| |
v v
Azure SQL Azure Service Bus
The important distinction is:
Azure DevOps automates application delivery.
Kubernetes/AKS runs the application.
Ingress/Gateway manages incoming application traffic.
33. Where Do Azure SQL and Azure Service Bus Fit?
Azure SQL and Azure Service Bus are generally infrastructure dependencies rather than resources that should be recreated every time the application pipeline runs.
A typical environment contains:
Azure Infrastructure
|
+── AKS
|
+── ACR
|
+── Azure SQL
|
+── Azure Service Bus
|
+── Key Vault
|
+── Monitoring
The CI/CD pipeline then deploys the application:
CI/CD
|
v
AKS
The application can communicate with:
Order API
|
+---- Azure SQL
|
+---- Azure Service Bus
34. Managing Secrets
Sensitive information should never be hardcoded into:
Source code
Dockerfiles
Kubernetes manifests
Pipeline scripts
Git repositories
Examples include:
SQL connection strings
Service Bus credentials
JWT secrets
API keys
Third-party credentials
A recommended architecture is:
Azure Key Vault
|
|
Managed Identity
|
v
AKS
|
v
Microservices
Azure DevOps variable/secret mechanisms can also be used where appropriate, but secrets should be managed according to your organization's security architecture.
35. What Happens When a Developer Commits Code?
Suppose a developer changes the OrderService validation logic.
The developer executes:
git add .
Then:
git commit -m "Update order validation"
Then:
git push origin main
The automated process becomes:
Git Push
|
v
CI Trigger
|
v
Restore
|
v
Build
|
v
Unit Tests
|
v
Docker Build
|
v
Docker Image
|
v
ACR
|
v
Release Pipeline
|
v
DEV
|
v
QA
|
v
UAT
|
v
Approval
|
v
PRODUCTION
This is the essence of CI/CD.
36. Continuous Integration vs Continuous Delivery
Understanding the difference between CI and CD is important.
Continuous Integration
Continuous Integration focuses on validating the application whenever code changes.
Code
|
v
Restore
|
v
Build
|
v
Test
|
v
Package
|
v
Docker Image
|
v
ACR
The primary question is:
"Is my code buildable, testable and packageable?"
Continuous Delivery / Deployment
Continuous Delivery/Deployment focuses on moving the validated application version through environments.
ACR
|
v
DEV
|
v
QA
|
v
UAT
|
v
Approval
|
v
PRODUCTION
The primary question is:
"Can this version be safely deployed to the required environments?"
Therefore:
CI = Build, test and package the application
CD = Deliver/deploy the application through environments
37. Classic Pipeline vs YAML Pipeline
The Classic UI is an excellent way to learn Azure DevOps because you can visually see:
Agent
Tasks
Stages
Artifacts
Approvals
Deployment
However, for modern enterprise projects, YAML pipelines are often preferred because the pipeline definition can be stored alongside the source code.
A modern architecture can look like:
Azure DevOps
|
v
YAML Pipeline
|
+---- CI
|
+---- Build
|
+---- Unit Tests
|
+---- Docker
|
+---- Security
|
+---- ACR
|
+---- CD
|
+---- DEV
|
+---- QA
|
+---- UAT
|
+---- PROD
The key advantage is Pipeline as Code.
The pipeline itself becomes version-controlled, reviewable, and reproducible.
38. Recommended Enterprise Improvements
The basic pipeline described above can be extended significantly for production environments.
Security
Add:
Azure Key Vault
Managed Identity
Microsoft Entra ID
Container image scanning
Dependency scanning
Secret scanning
Least-privilege service connections
Quality
Add:
Unit tests
Integration tests
Code coverage
SonarQube/SonarCloud
API testing
Performance testing
Deployment
Add:
Rolling deployments
Health probes
Readiness probes
Liveness probes
Deployment strategies
Automatic rollback
Environment approvals
Observability
Add:
Application Insights
Azure Monitor
Log Analytics
Kubernetes monitoring
Alerts
Dashboards
39. Production-Ready Microservices Flow
A mature enterprise architecture could eventually look like:
Developer
|
v
Azure Repos
|
v
Azure DevOps CI
|
+------------------+------------------+
| | |
v v v
Build/Test Security Scan Code Quality
| | |
+------------------+------------------+
|
v
Docker Build
|
v
ACR
|
v
Deployment Pipeline
|
+----------------+----------------+
| | |
v v v
DEV QA UAT
|
Approval
|
v
PROD
|
v
AKS
|
+-------------------+-------------------+
| | |
v v v
Customer API Order API Product API
| | |
+-------------------+-------------------+
|
+------------------------+----------------------+
| |
v v
Azure SQL Azure Service Bus
|
v
Application
Insights
|
v
Azure Monitor
40. Important Takeaways
The complete deployment journey can be remembered as:
Developer
↓
Git
↓
Azure DevOps
↓
CI
↓
Build
↓
Test
↓
Docker
↓
ACR
↓
CD
↓
DEV
↓
QA
↓
UAT
↓
Approval
↓
PRODUCTION
↓
AKS
The responsibilities of the major components are:
| Component | Responsibility |
|---|---|
| Azure Repos | Source-code management |
| Azure DevOps | CI/CD automation |
| Docker | Application containerization |
| ACR | Docker image storage |
| AKS | Container orchestration |
| Kubernetes | Application workload management |
| Ingress/Gateway | Incoming traffic routing |
| Azure SQL | Relational database |
| Azure Service Bus | Asynchronous messaging |
| Key Vault | Secret management |
| Application Insights | Application telemetry |
| Azure Monitor | Monitoring and alerting |
41. Final Conclusion
Implementing CI/CD for a .NET microservices application on Azure provides a repeatable and controlled way to move software from development to production.
The complete lifecycle is:
Code
↓
Commit
↓
Build
↓
Unit Test
↓
Docker Image
↓
Azure Container Registry
↓
AKS Deployment
↓
DEV
↓
QA
↓
UAT
↓
Production Approval
↓
PRODUCTION
For learning Azure DevOps, the Classic UI pipeline is a very useful starting point because every stage and task is visible through the portal.
For a new enterprise implementation, however, it is worth moving toward YAML-based pipelines, infrastructure as code, automated security scanning, managed identities, automated testing, deployment strategies, and comprehensive monitoring.
The ultimate goal is not simply to automate deployment.
The goal is to create a secure, repeatable, observable, and reliable software delivery process.
Quick Reference
CI
Git
↓
Restore
↓
Build
↓
Test
↓
Publish
↓
Docker Build
↓
Docker Push
↓
ACR
CD
ACR
↓
DEV
↓
QA
↓
UAT
↓
Approval
↓
PRODUCTION
↓
AKS
Application Architecture
Internet
↓
Ingress / Gateway
↓
Microservices
↓
Azure SQL
+
Azure Service Bus
+
Key Vault
+
Application Insights
This architecture provides a strong foundation for deploying modern .NET 9 microservices applications on Azure using Azure DevOps, Docker, ACR and AKS.

No comments:
Post a Comment