Skip to content

azure devops

Purplemet CI edited this page Jun 9, 2026 · 2 revisions

Azure DevOps Integration

This guide covers all aspects of integrating Purplemet security analyses into Azure DevOps pipelines.

Table of Contents

Prerequisites

1. Purplemet API Token

Create a token at cloud.purplemet.com.

The token must have the Operator role. The Administrator role is discouraged for CI/CD usage — the CLI will display a warning if an Administrator token is detected.

2. Install the Extension

Install the Purplemet extension from the Azure DevOps Marketplace.

For Azure DevOps Server (on-premises), the extension must be uploaded manually by an administrator.

3. Secret Pipeline Variable

Add PURPLEMET_API_TOKEN as a secret pipeline variable:

Option A — Pipeline variable:

  1. Edit your pipeline
  2. Click Variables
  3. Click New variable
  4. Name: PURPLEMET_API_TOKEN
  5. Value: your API token
  6. Check Keep this value secret

Option B — Variable group (recommended for multi-pipeline use):

  1. Go to PipelinesLibrary
  2. Create a new Variable group
  3. Add PURPLEMET_API_TOKEN with the lock icon (secret)
  4. Link the variable group to your pipelines

4. Network Access

Azure-hosted agents can reach the Purplemet API by default. For self-hosted agents, ensure outbound HTTPS access to api.purplemet.com.

Quick Start

Add to your azure-pipelines.yml:

trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

steps:
  - task: PurplemetAnalyze@1
    inputs:
      apiToken: $(PURPLEMET_API_TOKEN)
      targetUrl: 'https://your-app.example.com'
      failSeverity: 'high'

Installation Methods

Method 1: Marketplace Extension (recommended)

Uses the official Purplemet task from the Azure DevOps Marketplace.

steps:
  - task: PurplemetAnalyze@1
    inputs:
      apiToken: $(PURPLEMET_API_TOKEN)
      targetUrl: 'https://your-app.example.com'
      failSeverity: 'high'

Method 2: Docker Image

Uses the official Docker image directly. No extension needed.

steps:
  - script: |
      docker run --rm \
        -e PURPLEMET_API_TOKEN=$(PURPLEMET_API_TOKEN) \
        ppmsupport/purplemet-cli analyze https://your-app.com \
        --json --fail-on-severity high \
        | tee $(Build.ArtifactStagingDirectory)/purplemet-report.json
    displayName: 'Purplemet Security Analysis'
  - publish: $(Build.ArtifactStagingDirectory)/purplemet-report.json
    artifact: purplemet-report
    condition: always()

Method 3: Binary Installation

Downloads and installs the CLI binary.

steps:
  - script: |
      curl -sSL https://raw.githubusercontent.com/purplemet/cli/main/scripts/install.sh | sh
      purplemet-cli analyze https://your-app.com \
        --json --fail-on-severity high \
        | tee $(Build.ArtifactStagingDirectory)/purplemet-report.json
    displayName: 'Purplemet Security Analysis'
    env:
      PURPLEMET_API_TOKEN: $(PURPLEMET_API_TOKEN)
  - publish: $(Build.ArtifactStagingDirectory)/purplemet-report.json
    artifact: purplemet-report
    condition: always()

Parameters

Task Inputs (PurplemetAnalyze@1)

When using the Marketplace extension (Method 1), all gates and options are exposed as task inputs:.

Required

Input Default Description
apiToken API token (use secret variable $(PURPLEMET_API_TOKEN))
targetUrl URL of the web application to analyze

General

Input Default Description
failSeverity high Severity threshold: critical, high, medium, low, info
timeout 1800000 Polling timeout in milliseconds (30 min, 0 = unlimited)
version latest CLI version to use (e.g. v1.2.0)
format json Output format: json, human, sarif, html
baseUrl API base URL override
basicUser HTTP Basic Auth user (dev API only)
basicPass HTTP Basic Auth password (dev API only, use secret variable)
noCreate false Do not auto-create site if URL not found

Security Gates

Input Default Description
failRating Fail if rating is at or below this grade (AF)
failCvss 0 Fail if any CVE has CVSS score ≥ this value (e.g. 9.0)
failOnEol false Fail on end-of-life components
failOnSsl false Fail on SSL/TLS protocol issues
failOnCert false Fail on certificate issues
failOnHeaders false Fail on HTTP security header issues (CSP, HSTS, X-Frame-Options)
failOnCookies false Fail on insecure cookies (HttpOnly, Secure, SameSite)
failOnUnsafe false Fail on unsafe component issues
failOnKev false Fail on CISA Known Exploited Vulnerabilities
failOnEpss 0 Fail if any issue has EPSS score ≥ this value (0.0–1.0)
failOnActiveExploits false Fail on actively exploited vulnerabilities
failOnOssfScore 0 Fail if any technology has OpenSSF Scorecard score below this value (0–10)
failOnCertExpiry 0 Fail if certificate expires within N days
failOnIssueCount 0 Fail if total issue count ≥ this value
requireWaf false Fail if no WAF is detected
failOnSensitiveServices false Fail if sensitive services are exposed on the site IP
excludeTech Fail if specified technologies are detected (comma-separated)

YAML types: booleans are unquoted (failOnKev: true), numeric thresholds are quoted strings (failCvss: '9.0').

Environment Variables (Docker & Binary methods only)

Methods 2 and 3 don't use task inputs — they run analyze.sh which reads PURPLEMET_* environment variables. These are not needed when using the extension.

Variable Default Description
PURPLEMET_API_TOKEN API authentication token (required)
PURPLEMET_TARGET_URL URL to analyze (required)
PURPLEMET_FAIL_SEVERITY Severity threshold
PURPLEMET_FAIL_RATING Rating threshold: AF
PURPLEMET_FAIL_CVSS 0 CVSS score threshold
PURPLEMET_FAIL_ON_EOL false Block on end-of-life components
PURPLEMET_FAIL_ON_SSL false Block on SSL/TLS issues
PURPLEMET_FAIL_ON_CERT false Block on certificate issues
PURPLEMET_FAIL_ON_HEADERS false Block on HTTP security header issues
PURPLEMET_FAIL_ON_COOKIES false Block on insecure cookies
PURPLEMET_FAIL_ON_UNSAFE false Block on unsafe components
PURPLEMET_FAIL_ON_KEV false Block on CISA Known Exploited Vulnerabilities
PURPLEMET_FAIL_ON_EPSS 0 EPSS score threshold
PURPLEMET_FAIL_ON_ACTIVE_EXPLOITS false Block on actively exploited vulnerabilities
PURPLEMET_FAIL_ON_OSSF_SCORE 0 Min OpenSSF Scorecard score
PURPLEMET_FAIL_ON_CERT_EXPIRY 0 Block if certificate expires within N days
PURPLEMET_FAIL_ON_ISSUE_COUNT 0 Block if total issues ≥ threshold
PURPLEMET_REQUIRE_WAF false Block if no WAF detected
PURPLEMET_FAIL_ON_SENSITIVE_SERVICES false Block if sensitive services exposed
PURPLEMET_EXCLUDE_TECH Block if specified technologies detected
PURPLEMET_WAIT_TIMEOUT 1800000 Polling timeout (ms)
PURPLEMET_FORMAT json Output format: json, human, sarif, html
PURPLEMET_BASE_URL API base URL override

Security Gates

Multiple gates can be combined — the analysis fails (exit code 1) if any gate triggers.

Example: Strict Policy

steps:
  - script: |
      curl -sSL https://raw.githubusercontent.com/purplemet/cli/main/scripts/install.sh | sh
      purplemet-cli analyze https://your-app.com \
        --json \
        --fail-on-severity high \
        --fail-on-eol \
        --fail-on-kev \
        --fail-on-ssl \
        --require-waf \
        --fail-on-cert-expiry 30
    displayName: 'Security Analysis (Strict)'
    env:
      PURPLEMET_API_TOKEN: $(PURPLEMET_API_TOKEN)

Output Variables

The PurplemetAnalyze@1 task exposes output variables for use in subsequent steps:

Variable Description Example
PurplemetExitCode Exit code of the analysis 0
PurplemetRating Security rating B
PurplemetIssues Total number of issues 12

Using Output Variables

steps:
  - task: PurplemetAnalyze@1
    name: analysis
    inputs:
      apiToken: $(PURPLEMET_API_TOKEN)
      targetUrl: 'https://your-app.example.com'
    continueOnError: true

  - script: |
      echo "Rating: $(analysis.PurplemetRating)"
      echo "Issues: $(analysis.PurplemetIssues)"
      echo "Exit code: $(analysis.PurplemetExitCode)"
    displayName: 'Check Results'

Complete Pipeline Examples

Basic: Analysis on Push to Main

trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

steps:
  - task: PurplemetAnalyze@1
    inputs:
      apiToken: $(PURPLEMET_API_TOKEN)
      targetUrl: 'https://your-app.example.com'
      failSeverity: 'high'

Build → Deploy → Analysis Pipeline

trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

stages:
  - stage: Build
    jobs:
      - job: Build
        steps:
          - script: make build
          - script: make test

  - stage: Deploy
    dependsOn: Build
    jobs:
      - deployment: DeployStaging
        environment: staging
        strategy:
          runOnce:
            deploy:
              steps:
                - script: ./deploy.sh staging

  - stage: Security
    dependsOn: Deploy
    jobs:
      - job: Analysis
        steps:
          - task: PurplemetAnalyze@1
            inputs:
              apiToken: $(PURPLEMET_API_TOKEN)
              targetUrl: 'https://staging.example.com'
              failSeverity: 'high'
              timeout: '600000'

  - stage: Production
    dependsOn: Security
    condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
    jobs:
      - deployment: DeployProd
        environment: production
        strategy:
          runOnce:
            deploy:
              steps:
                - script: ./deploy.sh production

Scheduled Weekly Analysis

trigger: none

schedules:
  - cron: '0 6 * * 1'  # Every Monday at 6:00 UTC
    displayName: 'Weekly Security Analysis'
    branches:
      include:
        - main

pool:
  vmImage: 'ubuntu-latest'

steps:
  - task: PurplemetAnalyze@1
    inputs:
      apiToken: $(PURPLEMET_API_TOKEN)
      targetUrl: 'https://production.example.com'
      failSeverity: 'medium'
      timeout: '600000'

Multi-Site Analysis (Matrix)

trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

strategy:
  matrix:
    app1:
      targetUrl: 'https://app1.example.com'
    app2:
      targetUrl: 'https://app2.example.com'
    api:
      targetUrl: 'https://api.example.com'

steps:
  - task: PurplemetAnalyze@1
    inputs:
      apiToken: $(PURPLEMET_API_TOKEN)
      targetUrl: $(targetUrl)
      failSeverity: 'high'

Warning Mode (Non-blocking)

steps:
  - task: PurplemetAnalyze@1
    name: analysis
    inputs:
      apiToken: $(PURPLEMET_API_TOKEN)
      targetUrl: 'https://your-app.example.com'
      failSeverity: 'high'
    continueOnError: true

  - script: |
      if [ "$(analysis.PurplemetExitCode)" = "1" ]; then
        echo "##vso[task.logissue type=warning]Security analysis found $(analysis.PurplemetIssues) issue(s) — rating: $(analysis.PurplemetRating)"
      fi
    displayName: 'Report Results'
    condition: always()

Classic Pipeline

  1. Add the Purplemet Security Analysis task from the task catalog
  2. Configure:
    • API Token: $(PURPLEMET_API_TOKEN) (link a secret variable)
    • Target URL: your application URL
    • Fail Severity: high
  3. Optionally check Continue on error for non-blocking mode

Full Pipeline with Strict Gates

trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

stages:
  - stage: Build
    jobs:
      - job: BuildAndTest
        steps:
          - script: make build && make test

  - stage: Deploy
    dependsOn: Build
    jobs:
      - deployment: Staging
        environment: staging
        strategy:
          runOnce:
            deploy:
              steps:
                - script: ./deploy.sh

  - stage: Security
    dependsOn: Deploy
    jobs:
      - job: StrictAnalysis
        steps:
          - script: |
              curl -sSL https://raw.githubusercontent.com/purplemet/cli/main/scripts/install.sh | sh
              purplemet-cli analyze https://staging.example.com \
                --json \
                --fail-on-severity high \
                --fail-on-eol \
                --fail-on-kev \
                --fail-on-ssl \
                --fail-on-cert-expiry 30 \
                --require-waf
            displayName: 'Security Analysis (Strict)'
            env:
              PURPLEMET_API_TOKEN: $(PURPLEMET_API_TOKEN)

  - stage: Production
    dependsOn: Security
    jobs:
      - deployment: Prod
        environment: production
        strategy:
          runOnce:
            deploy:
              steps:
                - script: ./deploy.sh production

Results and Exit Codes

Exit Codes

Code Meaning Task Result
0 No issues above threshold Succeeded
1 Issues found above threshold Succeeded with issues
2 Analysis error on Purplemet Failed
3 Timeout exceeded Failed
4 Network or API error Failed
5 Usage error (bad arguments) Failed
6 API contract error Failed

Security Rating and Severity Levels

Ratings (AF) and severity levels (CRITICAL/HIGH/MEDIUM/LOW/INFO) are computed and defined by the Purplemet platform. See the official Purplemet documentation for authoritative definitions.

Advanced Usage

Pin a Specific CLI Version

- task: PurplemetAnalyze@1
  inputs:
    apiToken: $(PURPLEMET_API_TOKEN)
    targetUrl: 'https://your-app.example.com'
    version: 'v1.2.0'

Viewing the Report

For json, sarif, and human formats, the PurplemetAnalyze@1 task automatically uploads the analysis output as a build artifact named purplemet-report. Open the pipeline run → Summary tab → Published section to download it. No extra step required.

For html format, the CLI writes purplemet-report.html directly to the pipeline workspace (not to stdout), so you must publish it explicitly:

steps:
  - task: PurplemetAnalyze@1
    inputs:
      apiToken: $(PURPLEMET_API_TOKEN)
      targetUrl: 'https://your-app.example.com'
      format: 'html'
      failSeverity: 'high'

  - task: PublishPipelineArtifact@1
    condition: always()   # publish even if the gate fails
    inputs:
      targetPath: '$(System.DefaultWorkingDirectory)/purplemet-report.html'
      artifact: 'purplemet-report-html'
      publishLocation: 'pipeline'

Download the artifact from SummaryPublished and open it locally. Azure DevOps does not render HTML inline — install the HTML Report Publisher extension if you want a dedicated tab inside the run.

Generate HTML Report (Without the Extension)

If you're using the binary directly (no extension):

steps:
  - script: |
      curl -sSL https://raw.githubusercontent.com/purplemet/cli/main/scripts/install.sh | sh
      purplemet-cli analyze https://your-app.com \
        --format html --output-file $(Build.ArtifactStagingDirectory)/report.html \
        --fail-on-severity high || true
    displayName: 'Generate HTML Analysis Report'
    env:
      PURPLEMET_API_TOKEN: $(PURPLEMET_API_TOKEN)

  - publish: $(Build.ArtifactStagingDirectory)/report.html
    artifact: security-report
    condition: always()

Publishing the Extension (Maintainers)

cd integrations/azure-devops
npm install -g tfx-cli
tfx extension create --manifest-globs vss-extension.json
tfx extension publish --token <PAT>

FAQ / Common Errors

PURPLEMET_API_TOKEN variable not found

The pipeline variable is missing.

Fix: Add PURPLEMET_API_TOKEN as a secret variable: Edit pipeline → Variables → New variable → check Keep this value secret.

Or use a Variable group: Pipelines → Library → New variable group.


"Access is not authorized without a valid session"

The API token is invalid or expired.

Fix:

  1. Verify: purplemet-cli auth check
  2. Create a new token at cloud.purplemet.com
  3. Update the pipeline variable

Analysis times out (exit code 3)

Fix: Increase the timeout:

- task: PurplemetAnalyze@1
  inputs:
    apiToken: $(PURPLEMET_API_TOKEN)
    targetUrl: 'https://your-app.example.com'
    timeout: '600000'  # 10 minutes

Task fails with exit code 1 but I want it to continue

Exit code 1 means vulnerabilities were found. The task reports Succeeded with issues by default.

To fully suppress the failure:

- task: PurplemetAnalyze@1
  inputs:
    apiToken: $(PURPLEMET_API_TOKEN)
    targetUrl: 'https://your-app.example.com'
  continueOnError: true

Extension not available in Azure DevOps Server

For on-premises installations, upload the extension manually:

  1. Download the .vsix from the Marketplace
  2. Go to Organization SettingsExtensionsBrowse local extensions → Upload

Network error (exit code 4)

Self-hosted agents may not have access to the Purplemet API.

Fix: Ensure outbound HTTPS access to api.purplemet.com. If behind a proxy, configure HTTP_PROXY / HTTPS_PROXY.


How do I analyze multiple sites?

Use a matrix strategy (see Multi-Site Analysis example).


Where can I see the results?

  1. Pipeline log: Summary in the task output
  2. Pipeline artifacts: Download purplemet-report.json
  3. Output variables: Access PurplemetRating, PurplemetIssues in subsequent steps
  4. Purplemet dashboard: cloud.purplemet.com for detailed results

Clone this wiki locally