End-to-End CI/CD Pipeline with Azure DevOps: A Comprehensive Guide

sheak imransheak imran
4 min read

Introduction

In today's fast-paced software development world, implementing a robust CI/CD pipeline is essential for delivering high-quality applications quickly and reliably. In this article, I'll walk you through a comprehensive Azure DevOps pipeline that covers everything from code analysis to production deployment, including security scanning and infrastructure management.

Pipeline Overview

The pipeline I've created is designed for a Java Spring Boot application and incorporates modern DevOps practices including:

  1. Infrastructure as Code (Terraform)

  2. Static Application Security Testing (SAST)

  3. Dynamic Application Security Testing (DAST)

  4. Containerization and container security scanning

  5. Multi-cloud deployment (Azure and AWS)

  6. Environment-specific deployments

Pipeline Structure Breakdown

1. Triggers and Agent Configuration

trigger:
  - development
  - uat
  - production

pool:
  name: LinuxAgentPool
  demands:
    - JDK -equals Yes
    - Terraform -equals Yes
    - Agent.Name -equals ProdADO

The pipeline triggers on changes to three branches: development, uat, and production. It requires a Linux agent with specific capabilities (JDK, Terraform) to ensure all pipeline tasks can execute properly.

2. Variables Section

variables:
  global_version: "1.0.0"
  global_email: "sheakdev@gmail.com"
  isDev: $[eq(variables['Build.SourceBranch'], 'refs/heads/development')]
  isProd: $[eq(variables['Build.SourceBranch'], 'refs/heads/production')]

Variables are defined at different scopes (global, stage, job) allowing for flexible configuration. The conditional variables (isDev, isProd) enable branch-specific behavior in the pipeline.

Key Stages Explained

Stage 1: CheckingTheAgent

This initial validation stage ensures our agent has all required tools installed:

- stage: CheckingTheAgent
  condition: and(succeeded(), eq(variables.isDev, true))
  jobs:
    - job: CheckingTerraformAndPacker
      steps:
        - script: terraform version && packer version
        - script: docker version && docker ps && docker images && docker ps -a
        - script: pwd && ls -al

It verifies versions of critical tools (Terraform, Packer, Docker) and checks the working directory structure.

Stage 2: SAST With SonarQube

- stage: SASTWithSonarQube
  jobs:
    - job: RunningSASTWithSonarqube
      steps:
        - task: SonarQubePrepare@7
        - task: Maven@4
          inputs:
            sonarQubeRunAnalysis: true
        - task: sonar-buildbreaker@8

This stage performs static code analysis using SonarQube to identify code quality issues and security vulnerabilities early in the development cycle.

Stage 3: Building Java Code with Maven

- stage: BuildingJavaCodeWithMavenCopyToJFrog
  jobs:
    - job: BuildingJavaCodeJob
      steps:
        - script: mvn versions:set -DnewVersion=Dev-2.0.$(Build.BuildId)
        - script: mvn clean package install
        - script: mvn deploy
        - task: PublishBuildArtifacts@1

The build stage compiles the Java code, runs tests, and publishes artifacts. Key features:

  • Dynamic versioning based on build ID

  • Artifact publishing for downstream stages

  • JFrog artifact repository integration

Stage 4: Multi-Cloud Artifact Distribution

- stage: CopyingArtifactsToAzureAndAws
  jobs:
    - job: CopyFilesToAzureBlob
      steps:
        - task: AzureCLI@2
          inputs:
            inlineScript: az storage blob upload-batch...
    - job: CopyFilesToAWSS3Bucket
      steps:
        - task: S3Upload@1

This stage demonstrates hybrid cloud deployment by copying artifacts to both Azure Blob Storage and AWS S3, ensuring availability across platforms.

Stage 5: Container Security with Trivy

- stage: DockerBuildAndTrivyScan
  jobs:
    - job: BuildingContainerImageAndSecurityScanning
      steps:
        - script: docker build -t sheakimran21/myapp:$(Build.BuildId) .
        - script: trivy image --exit-code 0 --severity LOW,MEDIUM...
        - script: trivy image --exit-code 0 --severity HIGH,CRITICAL...
        - task: PublishTestResults@2

Container security scanning with Trivy checks for vulnerabilities at different severity levels, publishing results in JUnit format for visibility.

Stage 6: Container Registry Push

- stage: BuildDockerImagePushToAzureACRAndDockerHub
  jobs:
    - job: PushToAzureACR
      steps:
        - script: docker login...
        - script: docker tag...
        - script: docker push...
    - job: PushToDockerHub
      steps:
        - task: Docker@2

The built container image is pushed to both Azure Container Registry and Docker Hub, demonstrating multi-registry support.

Stage 7: Deployment to Staging

- stage: DeployingToStagingEnvironment
  jobs:
    - deployment: DeployJARtoStagingServer
      environment: STAGING
      strategy:
        runOnce:
          deploy:
            steps:
              - script: sudo kill -9 $PROC
              - script: sudo java -jar /home/ubuntu/azagent/_work/1/ROOT$(Build.BuildId).jar/ROOT$(Build.BuildId).jar &

The staging deployment ensures the application works in a production-like environment before promoting to production.

Stage 8: DAST Testing with OWASP ZAP

- stage: ZAPOWASPTestingStagingEnvironment
  jobs:
    - job: ZapTestingStaging
      steps:
        - script: docker run ghcr.io/zaproxy/zaproxy:stable zap-baseline.py...
        - task: PublishTestResults@2

Dynamic Application Security Testing (DAST) with OWASP ZAP identifies runtime vulnerabilities in the staging environment.

Stage 9: Production Deployment

- stage: DeployingToProdEnvironment
  condition: or(eq(variables.isDev, true), eq(variables.isProd, true))
  jobs:
    - deployment: DeployJARtoProdServer
      environment: PROD
      strategy:
        runOnce:
          deploy:
            steps:
              - task: DownloadBuildArtifacts@0
              - script: sudo kill -9 $PROC
              - script: sudo java -jar /home/ubuntu/azagent/_work/1/ROOT$(Build.BuildId).jar/ROOT$(Build.BuildId).jar &

The production deployment is gated by branch conditions and follows the same pattern as staging but targets the production environment.

Key DevOps Practices Demonstrated

  1. Infrastructure as Code: Terraform is used for infrastructure management

  2. Shift-Left Security: SAST and DAST testing early in the pipeline

  3. Immutable Artifacts: Versioned builds ensure traceability

  4. Multi-Cloud Support: Deployment to both Azure and AWS

  5. Container Security: Trivy scanning for container vulnerabilities

  6. Environment Promotion: Clear staging → production progression

  7. Conditional Execution: Branch-specific behavior

project github URL : https://github.com/saikiranpi/Mastering-DevSecOps/tree/Master/Day%2028%20SAST-AzureDevOps-Part-1

Conclusion

This Azure DevOps pipeline showcases a comprehensive approach to modern application delivery, incorporating security scanning, multi-cloud deployment, and environment promotion. By implementing such a pipeline, teams can achieve:

  • Faster feedback cycles with early testing

  • Improved security with SAST/DAST scanning

  • Consistent deployments across environments

  • Flexibility to deploy to multiple cloud providers

  • Traceability through versioned artifacts

The pipeline can be further enhanced with additional steps like performance testing, blue-green deployments, or canary releases based on specific organizational needs.

credit of this project : Pinapathruni Saikiran

0
Subscribe to my newsletter

Read articles from sheak imran directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

sheak imran
sheak imran

System Administrator FM Associates BD | Network Specialist | RHCE, RHCSA, RHCVA certified.