I have been using Azure Devops for a while. Like most of the cloud products out there this is one which gets constant refresh. My plan is to document the steps for building, testing and deploying an app to Azure Kubernetes Service using Azure Devops. So let's start. Prerequisties Github or Bitbucket Azure Kubernetes Service Azure Container Registry Soap UI Pro (you need Pro edition for CICD). Azure Devops Azure Devops Agent hosted on your Windows VM. (needed for SoapUI Pro) Flow Diagram 1) My code & Dockerfile What I got is a simple .NET web api project. And below is my Dockerfile. 2) My Github link 3) Azure Container Registry This is where we push our Docker image to. Below is a screenshot of Azure Container Registry from Azure Portal. Azure Kubernetes Service. This is the Azure managed Kubernetes Service. This is where we will deploy the containers into. Azure Devops Now that we have talked about the prerequisite required we will get ritht to it. We will walk through how we configure Azure Devops to build and push Docker image to registry and then deploying that image to AKS and running integration tests against it using SoapUI Pro So we will create 2 pipelines in Azure Devops. (Details below). The first one is build pipeline and the second pipeline is release pipeline. At the end of build pipeline the expected output is to have a new Docker Image in Azure Contaoner registry. This will be the artifact we will deploying to AKS as part of release pipeline. Below is my yaml file. # Dotnet # Unit the dotnet project. xUnit and NSubtitute # Docker # Build and push an image to Azure Container Registry # https: # Publsih # Now we get the tag the published id and update the k8 Deployment yaml image # https: trigger: - master resources: - repo: self variables: # Container registry service connection established during pipeline creation dockerRegistryServiceConnection: containerRegistry: imageRepository: dockerfilePath: tag: # Agent VM image name vmImageName: stages: - stage: UnitTestBuildAndPublish displayName: Unit Test then Build and Push Docket to Register then Publish release pipeline jobs: - job: UnitTest displayName: Running Unit tests the Hospital Microservice pool: vmImage: $(vmImageName) steps: - task: DotNetCoreCLI@ inputs: command: - job: Build dependsOn: UnitTest displayName: Build and push to container registry pool: vmImage: $(vmImageName) steps: - task: Docker@ displayName: Build and push an image to container registry inputs: command: buildAndPush repository: dockerfile: $(dockerfilePath) containerRegistry: $(containerRegistry) tags: | $(tag) - job: PreReleasePrepForhospitalMicroservice dependsOn: Build displayName: Pre Release Preparation (Bash build id and Publish Release pipeline) pool: vmImage: $(vmImageName) steps: - task: Bash@ inputs: targetType: script: | # Write your commands here cat value= value=${value echo > value1= echo mkdir echo - task: PublishPipelineArtifact@ inputs: targetPath: artifact: publishLocation: //docs.microsoft.com/azure/devops/pipelines/languages/docker of //docs.microsoft.com/azure/devops/pipelines/languages/docker '7c46ccde-aa97-4bd0-be94-abcd31bbe20b' 'dockerstore' 'hospital' '**/Dockerfile' '$(Build.BuildId)-$(Build.SourceVersion)' 'ubuntu-latest' of for 2 'test' 2 'hospital' for 3 'inline' '$(Build.SourcesDirectory)/hospital.yaml' `cat '$(Build.SourcesDirectory)/hospital.yaml'` //##BUILD_ID##/$(tag)} "$value" '$(Build.SourcesDirectory)/hospital_build.yaml' `cat '$(Build.SourcesDirectory)/hospital_build.yaml'` "$value1" '$(Pipeline.Workspace)/hospital' 'after creation of hospital' 1 '$(Pipeline.Workspace)' 'hospital' 'pipeline' Before talking about what is happening in above pipeliene; it maybe better to look at Azure Pipeline hierachy first. stages: - stage: A jobs: - job: A1 timeoutInMinutes: pool: vmImage: steps: - bash: echo - job: A2 steps: - bash: echo - stage: B jobs: - job: B1 steps: - bash: echo - job: B2 steps: - bash: echo 10 'ubuntu-16.04' "Hello world" "A" "B" "A" So the hierarchy is like above. You can have a list of stages which is the top level. Underneath it you can have a list of jobs which can further be broken down into steps and then steps into tasks. Also you can assign the kind of Build agent you want at job level. So you can have one job using Windows 10 agent another using Ubuntu. OK now that the pipeline hierarchy is clear; let's go back to the original build pipeline I have above. In that we have only 1 Stage called UnitTestBuildAndPublish. There are 3 jobs within it. Job1 called UnitTest Job2 called Build There is a task within this Job called Docker@2 which is for Build and pushing to Azure Container Registry. Job3 called PreReleasePrepForhospitalMicroservice. There are two tasks withing this. In the first task I use the bash command to get hold of the K8 yaml file and replace the placeholder with the imagetag. Then in the second task I use the PublishPipelineArtifact. This is kind of pushing the artifact so i can get hold of this in the release pipeline. There I need info on the Imagetag that was pushed to Azure Container Registry.( ) On whether the jobs runs in sequence or in parallel. By default they run parallel. But in above yaml file you may notice the "dependsOn" tag under each job. You will note that Build job "dependsOn" Unit test project to complete. And Prepublish job "dependsOn" Build Job. SO in essense they are executed in an sequential manner. https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/publish-pipeline-artifact?view=azure-devops UnitTest > Build(&Push Docker Image to Registry > Prepublish Sequence: Variables in above build pipeline. dockerRegistryServiceConnection: This is the Azure Container Registry connection string. This is preconfigured. containerRegistry: 'dockerstore' is the ACR service name, imageRepository: This is image repositor (or microservice name. dockerfilePath: Relative path to the Dockerfile in Github tag: Docker Image tag. We are using the BuildId and the Github Commitid for traceablity from Docker Image to Github code. Release Pipeline Below is the screenshot of my release pipeline tasks page. As you can see I am using two Agents. Hosted Agent running Ubuntu 18.04 Custom Agent on Windows 10 VM SoapUI Pro requires a Windows 10 OS to run it's tests. Download Pipeline Artifact (running on Ubuntu agent) So first step is to Download the Pipeline Artifact. We need this for K8 yaml file. In the Build pipleine we have updated the ##BuildID## placeholder with the real tag of the image that was pushed to ACR. Kubectl Apply (running on Ubuntu agent) We will use this task to apply the above yaml file against the "NM" namespace of AKS. The namespace is a way to logically have multpile environments in Kubernetes. The above yaml shows that the pods and services will be deployed to "NM" namespace. The above command confirms that the services and pods are deployed in that namespace. Bash Script Task (running on Ubuntu agent) This is more of a hack I put in there. This task just sleeps and holds the pipeline for few seconds. I wanted to ensure that the Pods are given enough time to be up and running. This is required because if the Pods are not Up SoapUI Pro will fail all the tests. Which is not the right thing. Azure SQL Dacpac Task (running on Ubuntu agent) This is for database deployment. I am not doing anything currently for this project. That is why it is in disabled state. But this is the task you would use to run the DDL and DML statements against your Azure SQL. API Management - Create/Update API (running on Windows 10 agent) This is task, I got from the market lace which helps me deploying API definitions to Azure API Management. It accepts OAS 3.0 defintion that I build using Swagger editor. Below is my API definition. openapi: # Added by API Auto Mocking Plugin servers: - description: SwaggerHub API Auto Mocking url: https: info: description: Demo API on Hospital. version: title: Hospital contact: email: m.naseem@outlook.com license: name: Apache url: tags: - name: Hospital description: Hospital related matters paths: {hospitalId}: get: tags: - Hospital summary: Finds hospital by id operationId: GetHospitalById description: | By passing the valid id, you can search the hospial details the database parameters: - : path name: hospitalId description: pass the hospitalId looking up the database required: schema: type: integer example: responses: : description: search result matching criteria content: application/json: schema: $ref: : description: bad input parameter : tags: - Hospital summary: deletes a hospital list operationId: DeleteHospial description: Deletes a hospital list parameters: - name: hospitalId : path description: Hospital id to required: schema: type: string responses: : description: : description: /hospital: post: tags: - Hospital summary: adds a hospital to the list operationId: AddHospital description: Adds a hospital to the list parameters: - name: Authorization : header required: schema: type: string responses: : description: hospital added : description: invalid input, object invalid : description: hospital already exists requestBody: content: application/json: schema: $ref: description: Add Hospital to list patch: tags: - Hospital summary: Updates a hospital the list operationId: UpdateHospital description: Updates a hospital to the list responses: : description: hospital updated : description: invalid input, object invalid : description: hospital does not exists requestBody: content: application/json: schema: $ref: description: Updates Hospital the list components: schemas: Hospital: type: object required: - Id - Name - Address - City - Pincode properties: id: type: integer example: name: type: string example: Epidemic Diseases Hospial Address: type: string example: MAJESTIC City: type: string example: Bangalore Pincode: type: integer example: 3.0 .0 //virtserver.swaggerhub.com/BRB/Hospital/1.0.0 "1.0.0" 2.0 'http://www.apache.org/licenses/LICENSE-2.0.html' /hospital/ in for in in for true 415 '200' '#/components/schemas/Hospital' '400' delete from from in delete true '400' "Invalid ID supplied" '404' "Hospital not found" in true '201' '400' '409' '#/components/schemas/Hospital' in '200' '400' '404' '#/components/schemas/Hospital' in 56 562110 SoapUI Pro for Azure DevOps (running on Win10 agent) After all of the above steps are completed; it is time to do the integration tests. And make sure everything is complying. SLA will be met. We didn't break anything. Once the tests are completed SoapUI exports some of the reports to Azure Devops. Below is one such report. Once all the test passes; we can promote the deployments to higher environments. This too can be automated nicely in Azure Devops. Each environment is called stages and we can add Manual Approver too as part of environment promotion. Below is a screenshot of the stages graphic that Azure Devops provide. So that's it. This is my github page: https://github.com/mohammednaseem/aksistio-hospital if you run into trouble with Azure Devops let me know.