{"id":77394,"date":"2019-06-11T09:00:11","date_gmt":"2019-06-11T16:00:11","guid":{"rendered":""},"modified":"2025-06-27T06:39:11","modified_gmt":"2025-06-27T13:39:11","slug":"five-steps-add-automated-performance-quality-gates-azure-devops-pipelines","status":"publish","type":"post","link":"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/","title":{"rendered":"Five steps to add\u00a0automated performance quality gates to Azure DevOps pipelines"},"content":{"rendered":"\n<p>In our&nbsp;<a href=\"https:\/\/cloudblogs.microsoft.com\/opensource\/2019\/04\/25\/adding-automated-performance-quality-gates-using-keptn-pitometer\/\">last post<\/a>, Daniel&nbsp;Semedo&nbsp;and I provided an overview of how to add automated performance quality gates using&nbsp;a&nbsp;performance specification file, as&nbsp;defined in the open source project&nbsp;<a href=\"https:\/\/www.dynatrace.com\/news\/blog\/automated-deployment-and-architectural-validation-with-pitometer-and-keptn\/\">Keptn Pitometer<\/a>.<\/p>\n\n\n\n<p>In this post, I\u2019ll explain the steps required to add a performance quality gate to&nbsp;your&nbsp;<a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/devops\/\">Azure&nbsp;DevOps&nbsp;<\/a>pipelines for both DevOps \u201cMulti-Stage\u201d and \u201cClassic\u201d&nbsp;<a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/devops\/pipelines\/\">pipelines<\/a>&nbsp;using Keptn Pitometer.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-1-setup-keptn-pitometer\">Step 1: Setup Keptn Pitometer<\/h2>\n\n\n\n<p>As a quick refresher, Keptn Pitometer is not an application \u2013 it\u2019s a set of open source Node.js modules used to create your own Pitometer client. Pitometer provides the processing of a performance specification (PerfSpec). This PerfSpec file defines the metrics, evaluation thresholds, and scoring objectives that constitute the resulting pass, warning or fail status calculated by Pitometer.<\/p>\n\n\n\n<p>The quickest way to try Pitometer is to use the&nbsp;<a href=\"https:\/\/github.com\/dt-demos\/pitometer-web-service\">Pitometer webservice<\/a>.&nbsp;In Azure, this is as simple as starting up an&nbsp;<a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/container-instances\/\">Azure container instance<\/a>&nbsp;that uses the Pitometer webservice Docker image. This way any pipeline can add a quality gate by just calling webservice with their PerfSpec file. Follow&nbsp;<a href=\"https:\/\/github.com\/dt-demos\/pitometer-web-service#use-azure-container-instance-to-host-the-pitometer-web-service\">these instructions<\/a>&nbsp;to create a new container instance through the Azure portal or Azure CLI.<\/p>\n\n\n\n<p>After the container instance is running, go to the overview page (shown below) and copy the IP address. Use this IP within the Pitometer webservice URL: http:\/\/&lt;Your IP&gt;:8080\/api\/pitometer.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-1-1024x219.png\" alt=\"overview page image\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-2-create-your-perfspec-file\">Step 2: Create your PerfSpec file<\/h2>\n\n\n\n<p>The PerfSpec file is tailored to your quality objectives and application. You\u2019ll need to specify the indicator metrics to capture, and the scoring for the \u201cPitometer\u201d service to evaluate. See example PerfSpec file&nbsp;<a href=\"https:\/\/github.com\/dt-demos\/azure-devops-ubp\/blob\/master\/perfspec\/perfspec.json\">here.<\/a><\/p>\n\n\n\n<p>Once you have your own PerfSpec file, check it into&nbsp;your Azure DevOps or GitHub repository. This allows you to manage artifacts the same way and keep them in-sync with code changes.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-3-create-a-script-to-call-pitometer\">Step 3: Create a script to call Pitometer<\/h2>\n\n\n\n<p>A PowerShell script is an easy way to call and parse results from the Pitometer webservice. The example below expects the following parameters and will first read the \u201cPerfSpec\u201d file contents and construct the request to the Pitometer webservice.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Start and stop times in UTC milliseconds for gathering metrics<\/li>\n\n\n\n<li>Pitometer webservice URL<\/li>\n\n\n\n<li>PerfSpec file location within the pipeline working directory<\/li>\n<\/ol>\n\n\n\n<p>After the Pitometer webservice is called, the script will parse the response and pull out the status attribute. The status attribute will have a value of \u201cpass,\u201d \u201cwarning,\u201d \u201cfail,\u201d or \u201cerror.\u201d&nbsp;&nbsp; If there is a failure or an \u201cerror,\u201d the script will return \u201cexit 1\u201d thus stopping the pipeline.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n# input variables \n$START_TIME=$env:startTime\n$END_TIME=$env:endTime\n$PITOMETER_WEBSERVICE_URL=$env:pitometerUrl\nSet-Variable -Name \"PERFSPEC_FILE\" -Value \"$(System.DefaultWorkingDirectory)\/_Demo-App-Build\/app\/perfspec\/perfspec.json\"\n\n# read perfspec file contents and compose the Pitometer service POST body content \nSet-Variable -Name \"PERFSPEC_DIR\" -Value \"$($PERFSPEC_FILE)\" \n$PERFSPEC_CONTENT = Get-Content -Path $PERFSPEC_DIR \n$PERFSPEC_REQUEST_BODY=\"{\"\"timeStart\"\": $START_TIME,\"\"timeEnd\"\": $END_TIME,\"\"perfSpec\"\": $($PERFSPEC_CONTENT)}\" \n \n# some debug output\nWrite-Host \"===============================================================\"\nWrite-Host \"startTime: \"$START_TIME\nWrite-Host \"endTime: \"$END_TIME\nWrite-Host \"url: \"$PITOMETER_WEBSERVICE_URL\nWrite-Host \"request body: \"$PERFSPEC_REQUEST_BODY\nWrite-Host \"===============================================================\"\n\n# calling Pitometer Service \n$PERFSPEC_RESULT_BODY = Invoke-RestMethod -Uri $PITOMETER_WEBSERVICE_URL -Method Post -Body $PERFSPEC_REQUEST_BODY -ContentType \"application\/json\"  \n\n# more debug output\nWrite-Host \"response body: \"$PERFSPEC_RESULT_BODY\nWrite-Host \"===============================================================\"\n \n# save the Pitometer service response and pull out the result status \n$PERFSPEC_JSON = $PERFSPEC_RESULT_BODY | ConvertTo-Json -Depth 5 \n$PERFSPEC_RESULT = $PERFSPEC_RESULT_BODY.result \n \n# evaluate the result and pass or fail the pipeline \nif (\"$PERFSPEC_RESULT\" -eq \"fail\" -Or \"$PERFSPEC_RESULT\" -eq \"error\") { \n    Write-Host \"Failed the quality gate\"  \n    exit 1 \n} else { \n    Write-Host \"Passed the quality gate\"  \n} \n<\/pre><\/div>\n\n\n<p>Here is how to setup up the PowerShell script inputs.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The timeframe is set within two environment variables called \u201c$env.startTime\u201d and \u201c$env.endTime\u201d. One can set these variables before and after the performance task using PowerShell script such as this:<\/li>\n<\/ul>\n\n\n\n<p>$startTime = Get-Date ((get-date).toUniversalTime()) -UFormat +%s Write-Host (&#8220;##vso[task.setvariable variable=startTime]$startTime&#8221;)<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The Pitometer URL is set within an environment variable called $env.pitometerURL. Set the pipeline variables as shown below:<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-2-1024x211.png\" alt=\"Description of pipeline variables\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-4-add-a-performance-quality-gate-task-in-your-pipeline-that-calls-pitometer\">Step 4: Add a performance quality gate task in your pipeline that calls Pitometer<\/h2>\n\n\n\n<p>Below is a representative build and release pipeline that&nbsp;includes&nbsp;typical&nbsp;tasks along with the addition of performance quality gate task described in step 6.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-3-1024x399.png\" alt=\"representative build and release pipeline\" \/><\/figure>\n\n\n\n<ol class=\"wp-block-list\">\n<li>PerfSpec file\u00a0is\u00a0checked\u00a0into your source code repository.<\/li>\n\n\n\n<li>Task\u00a0within the Build stage\u00a0adds\u00a0the PerfSpec as a built artifact.<\/li>\n\n\n\n<li>Code is deployed to the application under test.<\/li>\n\n\n\n<li>Performance test runs\u00a0and\u00a0monitoring metrics are collected.<\/li>\n\n\n\n<li>Deployment event is pushed to\u00a0a\u00a0monitoring tool to provide\u00a0build\u00a0context.<\/li>\n\n\n\n<li>Pitometer service is called with the PerfSpec file content. Result will be pass, warn, or fail. A fail will stop the pipeline.<\/li>\n<\/ol>\n\n\n\n<p>Azure&nbsp;DevOps&nbsp;offers both \u201cClassic\u201d&nbsp;pipelines and the new \u201cMulti-Stage\u201d pipelines.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"what-are-classic-build-pipelines\">What are \u201cClassic\u201d Build pipelines?<\/h2>\n\n\n\n<p>Azure \u201cClassic\u201d has two distinct pipeline types; build and release. Build pipelines can be created using a visual editor or through YAML declarative files, as opposed to release pipelines, which can only be created visually.<\/p>\n\n\n\n<p>A Build pipeline is meant to build, unit test, and publish a reusable build artifact. Since we checked in our \u201cPerfSpec\u201d file with our code, we can include it in the build artifact and reference it through the Release pipeline.<\/p>\n\n\n\n<p>The example Build pipeline, named \u201dDemo-App-Build,\u201d will make an artifact named \u201capp\u201d that includes the whole repo, including the PerfSpec sub-folder, which contains the perfspec.json file. The ZIP artifact will be saved with the pipeline release.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-4-1024x404.png\" alt=\"example Build pipeline\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"what-are-classic-release-pipelines\">What are \u201cClassic\u201d Release pipelines?<\/h2>\n\n\n\n<p>A Release pipeline takes input artifacts and uses their files for task scripts such as deploying code. Tasks are grouped into stages. Below is an example release pipeline named \u201dDemo-App-Deploy\u201d with two stages named \u201cStaging\u201d and \u201cProduction.\u201d<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-5-1024x346.png\" alt=\"A Release pipeline\" \/><\/figure>\n\n\n\n<p>Below are the tasks within the \u201dStaging\u201d stage. The last one is the PowerShell \u201cQuality gate call to Pitometer\u201d task, which determines if the pipeline stops or continues to the \u201cProduction\u201d stage.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-6-911x1024.png\" alt=\"Tasks within the \u201dStaging\u2019 stage.\" \/><\/figure>\n\n\n\n<p>The \u201cQuality gate call to Pitometer\u201d task simply contains the PowerShell script we reviewed earlier as an in-line script (shown below). Recall that the script will \u201dexit 1\u201d and stop the pipeline when the \u201cPerfSpec\u201d fails.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-7-1024x639.png\" alt=\"PowerShell screenshot\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"multi-stage-pipelines\">\u201cMulti-Stage\u201d pipelines<\/h2>\n\n\n\n<p>Azure DevOps&nbsp;<a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/devops\/pipelines\/get-started\/pipelines-get-started?view=azure-devops&amp;tabs=yaml\">\u201cMulti-Stage\u201d pipelines<\/a>&nbsp;allow all the build and release tasks to be defined in a YAML file. To enable this preview feature, open the menu found within your Azure DevOps account profile as shown below.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-8.png\" alt=\"Multi-Stage pipelines screenshot\" \/><\/figure>\n\n\n\n<p>We need to capture the test start and stop times, run a performance test so that&nbsp;monitoring metrics are collected, and run the quality gate step. Similar to Classic pipelines, PowerShell tasks can be run as either in-line scripts or script files.<\/p>\n\n\n\n<p>Below is a YAML pipeline code snippet for PowerShell tasks calling scripts files.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n- task: PowerShell@2 \ndisplayName: 'Capture Load Test Start Time' \nfailOnStderr: true \ninputs: \n    targetType: filePath \n    filePath: $(System.DefaultWorkingDirectory)\/extract\/pipeline\/captureStartTime.ps1 \n \n- task: Bash@3 \ndisplayName: 'Load Test' \ninputs: \n    targetType: filePath \n    filePath: $(System.DefaultWorkingDirectory)\/extract\/pipeline\/loadtest.sh \n    arguments: 'Staging $(azure-resource-prefix)-ubp-demo-app-staging.azurewebsites.net $(loadtest-duration-seconds)' \n \n- task: PowerShell@2 \ndisplayName: 'Capture Load Test End Time' \nfailOnStderr: true \ninputs: \n    targetType: filePath \n    filePath: $(System.DefaultWorkingDirectory)\/extract\/pipeline\/captureEndTime.ps1 \n\n- task: PowerShell@2 \ndisplayName: 'Quality Gate call to Pitometer' \ninputs: \n    targetType: filePath \n    failOnStderr: true \n    filePath: $(System.DefaultWorkingDirectory)\/extract\/pipeline\/qualitygate.ps1 \n    arguments: '$(startTime) $(endTime) $(pitometer-url) \/home\/vsts\/work\/1\/s\/extract\/$(perfspecFilePath)' \n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"step-5-run-your-pipeline\">Step 5: Run your pipeline<\/h2>\n\n\n\n<p>Both \u201cClassic\u201d and \u201cMulti-stage\u201d pipelines show a visualization as the pipeline runs and the logs from each task can be reviewed. Below is how the visualization looks for Multi-stage\u201d pipelines and examples for the failed quality gate.<\/p>\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-9.webp\" alt=\"Passing quality gate in &ldquo;Multi-stage&rdquo; Pipeline.\" class=\"wp-image-77421 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-9.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-9-300x79.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-9-768x203.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-9-330x87.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-9-800x212.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-9-400x106.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-9.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-9.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-9-300x79.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-9-768x203.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-9-330x87.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-9-800x212.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-9-400x106.png 400w\"><\/figure>\n\n\n\n<p>Passing quality gate in \u201cMulti-stage\u201d Pipeline.<\/p>\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-10.webp\" alt=\"Failing quality gate in &ldquo;Multi-stage&rdquo; Pipeline.\" class=\"wp-image-77424 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-10.webp 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-10-300x124.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-10-768x318.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-10-330x137.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-10-800x331.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-10-400x166.webp 400w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-10.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-10.png 1024w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-10-300x124.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-10-768x318.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-10-330x137.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-10-800x331.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-10-400x166.png 400w\"><figcaption class=\"wp-element-caption\">Failing quality gate in &ldquo;Multi-stage&rdquo; Pipeline.<\/figcaption><\/figure>\n\n\n\n<p id=\"caption-attachment-77424\">Failing quality gate in \u201cMulti-stage\u201d Pipeline.<\/p>\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-11.webp\" alt=\"Task details from failing quality gate task\" class=\"wp-image-77427 webp-format\" srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-11.webp 802w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-11-300x170.webp 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-11-768x435.webp 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-11-330x187.webp 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-11-800x453.webp 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-11-400x226.webp 400w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-11-235x132.webp 235w\" data-orig-src=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-11.png\" data-orig-srcset=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-11.png 802w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-11-300x170.png 300w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-11-768x435.png 768w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-11-330x187.png 330w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-11-800x453.png 800w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-11-400x226.png 400w, https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/Keptn-Pitometer_image-11-235x132.png 235w\"><figcaption class=\"wp-element-caption\">Task details from failing quality gate task<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"recap\">Recap<\/h2>\n\n\n\n<p>As with most things within software, there are often several ways to achieve a given goal. I hope these five steps will provide a starting point for implementing automated performance quality gates using Keptn Pitometer within Azure DevOps pipelines.<\/p>\n\n\n\n<p>Questions or feedback? Let me know in the comments below.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In our&nbsp;last post, Daniel&nbsp;Semedo&nbsp;and I provided an overview of how to add automated performance quality gates using&nbsp;a&nbsp;performance specification file, as&nbsp;defined in the open source project&nbsp;Keptn Pitometer. In this post, I\u2019ll explain the steps required to add a performance quality gate to&nbsp;your&nbsp;Azure&nbsp;DevOps&nbsp;pipelines for both DevOps \u201cMulti-Stage\u201d and \u201cClassic\u201d&nbsp;pipelines&nbsp;using Keptn Pitometer.<\/p>\n","protected":false},"author":5562,"featured_media":77445,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"msxcm_post_with_no_image":false,"ep_exclude_from_search":false,"_classifai_error":"","_classifai_text_to_speech_error":"","footnotes":""},"post_tag":[2271],"content-type":[340],"topic":[2241,2244],"programming-languages":[],"coauthors":[576],"class_list":["post-77394","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","tag-community-partners","content-type-tutorials-and-demos","topic-cloud","topic-devops","review-flag-1593580428-734","review-flag-1593580419-521","review-flag-1593580771-946","review-flag-1-1593580432-963","review-flag-2-1593580437-411","review-flag-3-1593580442-169","review-flag-4-1593580448-609","review-flag-5-1593580453-725","review-flag-6-1593580457-852","review-flag-new-1593580248-669"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.2 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Five steps to add\u00a0automated performance quality gates to Azure DevOps pipelines<\/title>\n<meta name=\"description\" content=\"This post explains how to add a performance quality gate to\u00a0your Azure\u00a0DevOps\u00a0pipelines using Keptn Pitometer, which is a set of open source Node.js modules used to create your own Pitometer client.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Five steps to add\u00a0automated performance quality gates to Azure DevOps pipelines\" \/>\n<meta property=\"og:description\" content=\"This post explains how to add a performance quality gate to\u00a0your Azure\u00a0DevOps\u00a0pipelines using Keptn Pitometer, which is a set of open source Node.js modules used to create your own Pitometer client.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/\" \/>\n<meta property=\"og:site_name\" content=\"Microsoft Open Source Blog\" \/>\n<meta property=\"article:published_time\" content=\"2019-06-11T16:00:11+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-27T13:39:11+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/keptn_logo.png\" \/>\n\t<meta property=\"og:image:width\" content=\"700\" \/>\n\t<meta property=\"og:image:height\" content=\"200\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Rob Jahn\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@OpenAtMicrosoft\" \/>\n<meta name=\"twitter:site\" content=\"@OpenAtMicrosoft\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Rob Jahn\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 min read\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/\"},\"author\":[{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/author\/rob-jahn\/\",\"@type\":\"Person\",\"@name\":\"Rob Jahn\"}],\"headline\":\"Five steps to add\u00a0automated performance quality gates to Azure DevOps pipelines\",\"datePublished\":\"2019-06-11T16:00:11+00:00\",\"dateModified\":\"2025-06-27T13:39:11+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/\"},\"wordCount\":1082,\"commentCount\":1,\"publisher\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/keptn_logo.png\",\"keywords\":[\"Community\/partners\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/\",\"url\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/\",\"name\":\"Five steps to add\u00a0automated performance quality gates to Azure DevOps pipelines\",\"isPartOf\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/keptn_logo.png\",\"datePublished\":\"2019-06-11T16:00:11+00:00\",\"dateModified\":\"2025-06-27T13:39:11+00:00\",\"description\":\"This post explains how to add a performance quality gate to\u00a0your Azure\u00a0DevOps\u00a0pipelines using Keptn Pitometer, which is a set of open source Node.js modules used to create your own Pitometer client.\",\"breadcrumb\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/#primaryimage\",\"url\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/keptn_logo.png\",\"contentUrl\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/keptn_logo.png\",\"width\":700,\"height\":200,\"caption\":\"Keptn Logo\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/opensource.microsoft.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Five steps to add\u00a0automated performance quality gates to Azure DevOps pipelines\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#website\",\"url\":\"https:\/\/opensource.microsoft.com\/blog\/\",\"name\":\"Microsoft Open Source Blog\",\"description\":\"Open dialogue about openness at Microsoft \u2013 open source, standards, interoperability\",\"publisher\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/opensource.microsoft.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#organization\",\"name\":\"Microsoft Open Source Blog\",\"url\":\"https:\/\/opensource.microsoft.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Microsoft-Logo.png\",\"contentUrl\":\"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Microsoft-Logo.png\",\"width\":259,\"height\":194,\"caption\":\"Microsoft Open Source Blog\"},\"image\":{\"@id\":\"https:\/\/opensource.microsoft.com\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/OpenAtMicrosoft\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Five steps to add\u00a0automated performance quality gates to Azure DevOps pipelines","description":"This post explains how to add a performance quality gate to\u00a0your Azure\u00a0DevOps\u00a0pipelines using Keptn Pitometer, which is a set of open source Node.js modules used to create your own Pitometer client.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/","og_locale":"en_US","og_type":"article","og_title":"Five steps to add\u00a0automated performance quality gates to Azure DevOps pipelines","og_description":"This post explains how to add a performance quality gate to\u00a0your Azure\u00a0DevOps\u00a0pipelines using Keptn Pitometer, which is a set of open source Node.js modules used to create your own Pitometer client.","og_url":"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/","og_site_name":"Microsoft Open Source Blog","article_published_time":"2019-06-11T16:00:11+00:00","article_modified_time":"2025-06-27T13:39:11+00:00","og_image":[{"width":700,"height":200,"url":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/keptn_logo.png","type":"image\/png"}],"author":"Rob Jahn","twitter_card":"summary_large_image","twitter_creator":"@OpenAtMicrosoft","twitter_site":"@OpenAtMicrosoft","twitter_misc":{"Written by":"Rob Jahn","Est. reading time":"5 min read"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/#article","isPartOf":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/"},"author":[{"@id":"https:\/\/opensource.microsoft.com\/blog\/author\/rob-jahn\/","@type":"Person","@name":"Rob Jahn"}],"headline":"Five steps to add\u00a0automated performance quality gates to Azure DevOps pipelines","datePublished":"2019-06-11T16:00:11+00:00","dateModified":"2025-06-27T13:39:11+00:00","mainEntityOfPage":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/"},"wordCount":1082,"commentCount":1,"publisher":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#organization"},"image":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/#primaryimage"},"thumbnailUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/keptn_logo.png","keywords":["Community\/partners"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/","url":"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/","name":"Five steps to add\u00a0automated performance quality gates to Azure DevOps pipelines","isPartOf":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/#primaryimage"},"image":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/#primaryimage"},"thumbnailUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/keptn_logo.png","datePublished":"2019-06-11T16:00:11+00:00","dateModified":"2025-06-27T13:39:11+00:00","description":"This post explains how to add a performance quality gate to\u00a0your Azure\u00a0DevOps\u00a0pipelines using Keptn Pitometer, which is a set of open source Node.js modules used to create your own Pitometer client.","breadcrumb":{"@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/#primaryimage","url":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/keptn_logo.png","contentUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/06\/keptn_logo.png","width":700,"height":200,"caption":"Keptn Logo"},{"@type":"BreadcrumbList","@id":"https:\/\/opensource.microsoft.com\/blog\/2019\/06\/11\/five-steps-add-automated-performance-quality-gates-azure-devops-pipelines\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/opensource.microsoft.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Five steps to add\u00a0automated performance quality gates to Azure DevOps pipelines"}]},{"@type":"WebSite","@id":"https:\/\/opensource.microsoft.com\/blog\/#website","url":"https:\/\/opensource.microsoft.com\/blog\/","name":"Microsoft Open Source Blog","description":"Open dialogue about openness at Microsoft \u2013 open source, standards, interoperability","publisher":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/opensource.microsoft.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/opensource.microsoft.com\/blog\/#organization","name":"Microsoft Open Source Blog","url":"https:\/\/opensource.microsoft.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/opensource.microsoft.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Microsoft-Logo.png","contentUrl":"https:\/\/opensource.microsoft.com\/blog\/wp-content\/uploads\/2019\/08\/Microsoft-Logo.png","width":259,"height":194,"caption":"Microsoft Open Source Blog"},"image":{"@id":"https:\/\/opensource.microsoft.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/OpenAtMicrosoft"]}]}},"msxcm_display_generated_audio":false,"msxcm_animated_featured_image":null,"distributor_meta":false,"distributor_terms":false,"distributor_media":false,"distributor_original_site_name":"Microsoft Open Source Blog","distributor_original_site_url":"https:\/\/opensource.microsoft.com\/blog","push-errors":false,"_links":{"self":[{"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts\/77394","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/users\/5562"}],"replies":[{"embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/comments?post=77394"}],"version-history":[{"count":1,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts\/77394\/revisions"}],"predecessor-version":[{"id":97745,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/posts\/77394\/revisions\/97745"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/media\/77445"}],"wp:attachment":[{"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/media?parent=77394"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/post_tag?post=77394"},{"taxonomy":"content-type","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/content-type?post=77394"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/topic?post=77394"},{"taxonomy":"programming-languages","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/programming-languages?post=77394"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/opensource.microsoft.com\/blog\/wp-json\/wp\/v2\/coauthors?post=77394"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}