Azure DevOps Container Scan

The QualysContainerScan@1 task scans Docker/OCI container images for vulnerabilities, secrets, and malware. It supports local images, registry images, and integrates with Azure DevOps Advanced Security for SARIF publishing.

Task Name: QualysContainerScan@1

Task Inputs

Service Connection

Input Required Default Description
qualysConnection Yes - Qualys API service connection name

Scan Target

Input Required Default Description
imageId Yes - Container image to scan (name:tag or digest)
imageTar No - Path to tar archive of the image
platform No linux/amd64 Target platform for multi-arch images

Scan Options

Input Required Default Description
scanSecrets No false Enable secrets detection in container layers
scanMalware No false Enable malware detection
offlineMode No false Scan without uploading to Qualys platform

Threshold Configuration

Input Required Default Description
maxCritical No -1 Maximum critical vulnerabilities allowed (-1 = unlimited)
maxHigh No -1 Maximum high vulnerabilities allowed (-1 = unlimited)
maxMedium No -1 Maximum medium vulnerabilities allowed (-1 = unlimited)
maxLow No -1 Maximum low vulnerabilities allowed (-1 = unlimited)

Policy Configuration

Input Required Default Description
usePolicyEvaluation No false Enable Qualys cloud policy evaluation
failOnAudit No false Fail pipeline when policy result is AUDIT

Output Options

Input Required Default Description
publishResults No false Publish SARIF to Azure DevOps Advanced Security

Work Item Creation

Input Required Default Description
createWorkItems No false Create Azure Boards work items for vulnerabilities
workItemSeverities No 4,5 Severity levels to create work items for (comma-separated)
workItemType No Bug Work item type to create
workItemAreaPath No - Area path for created work items
workItemIterationPath No - Iteration path for created work items

Complete Example

trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

steps:
  - task: Docker@2
    displayName: 'Build Container Image'
    inputs:
      command: build
      Dockerfile: Dockerfile
      tags: $(Build.BuildId)

  - task: QualysContainerScan@1
    displayName: 'Qualys Container Scan'
    inputs:
      qualysConnection: 'QualysConnection'
      imageId: 'myapp:$(Build.BuildId)'
      platform: 'linux/amd64'
      scanSecrets: true
      scanMalware: true
      maxCritical: 0
      maxHigh: 5
      usePolicyEvaluation: false
      publishResults: true
      createWorkItems: true
      workItemSeverities: '4,5'
      workItemAreaPath: 'MyProject\Security'
    env:
      SYSTEM_ACCESSTOKEN: $(System.AccessToken)

  - script: |
      echo "Vulnerabilities: $(qualysContainerScan.vulnerabilityCount)"
      echo "Critical: $(qualysContainerScan.criticalCount)"
      echo "Scan passed: $(qualysContainerScan.scanPassed)"
    displayName: 'Display Scan Results'

Scanning Remote Images

To scan images from private registries, authenticate before running the scan:

steps:
  - task: Docker@2
    displayName: 'Login to ACR'
    inputs:
      command: login
      containerRegistry: 'myAcrConnection'

  - task: QualysContainerScan@1
    displayName: 'Scan ACR Image'
    inputs:
      qualysConnection: 'QualysConnection'
      imageId: 'myacr.azurecr.io/myapp:$(Build.BuildId)'
      publishResults: true

Scanning Tar Archives

steps:
  - script: |
      docker save myapp:$(Build.BuildId) -o $(Build.ArtifactStagingDirectory)/image.tar
    displayName: 'Export Image'

  - task: QualysContainerScan@1
    displayName: 'Scan Image Tar'
    inputs:
      qualysConnection: 'QualysConnection'
      imageTar: '$(Build.ArtifactStagingDirectory)/image.tar'
      publishResults: true

Policy-Based Scanning

steps:
  - task: QualysContainerScan@1
    displayName: 'Scan with Policy'
    inputs:
      qualysConnection: 'QualysConnection'
      imageId: 'myapp:$(Build.BuildId)'
      usePolicyEvaluation: true
      failOnAudit: true

Output Variables

Access scan results in subsequent tasks using these output variables:

Variable Description
$(taskName.vulnerabilityCount) Total number of vulnerabilities found
$(taskName.criticalCount) Number of critical vulnerabilities
$(taskName.highCount) Number of high vulnerabilities
$(taskName.mediumCount) Number of medium vulnerabilities
$(taskName.lowCount) Number of low vulnerabilities
$(taskName.secretsCount) Number of secrets detected
$(taskName.malwareCount) Number of malware detections
$(taskName.policyResult) Policy result: ALLOW, DENY, AUDIT, or NONE
$(taskName.scanPassed) true/false based on thresholds or policy
$(taskName.sarifPath) Path to SARIF report file
$(taskName.jsonPath) Path to JSON report file
$(taskName.workItemsCreated) Number of work items created

Using Output Variables

steps:
  - task: QualysContainerScan@1
    name: containerScan
    displayName: 'Scan Container'
    inputs:
      qualysConnection: 'QualysConnection'
      imageId: 'myapp:$(Build.BuildId)'

  - script: |
      echo "Total vulnerabilities: $(containerScan.vulnerabilityCount)"
      echo "Critical: $(containerScan.criticalCount)"
      echo "High: $(containerScan.highCount)"
      echo "Policy result: $(containerScan.policyResult)"
      echo "Scan passed: $(containerScan.scanPassed)"
    displayName: 'Show Results'

  - task: PublishBuildArtifacts@1
    inputs:
      pathToPublish: '$(containerScan.sarifPath)'
      artifactName: 'security-reports'
    displayName: 'Publish SARIF Report'

Work Item Creation

To create Azure Boards work items, enable OAuth token access:

Option 1: Pipeline Setting

Enable "Allow scripts to access OAuth token" in pipeline settings.

Option 2: Environment Variable

- task: QualysContainerScan@1
  displayName: 'Scan and Create Work Items'
  inputs:
    qualysConnection: 'QualysConnection'
    imageId: 'myapp:$(Build.BuildId)'
    createWorkItems: true
    workItemSeverities: '4,5'
    workItemType: 'Bug'
    workItemAreaPath: 'MyProject\Security'
  env:
    SYSTEM_ACCESSTOKEN: $(System.AccessToken)

Multi-Stage Pipeline

trigger:
  - main

stages:
  - stage: Build
    jobs:
      - job: BuildImage
        pool:
          vmImage: 'ubuntu-latest'
        steps:
          - task: Docker@2
            inputs:
              command: build
              tags: $(Build.BuildId)
          - task: Docker@2
            inputs:
              command: push
              containerRegistry: 'myAcrConnection'

  - stage: Security
    dependsOn: Build
    jobs:
      - job: ContainerScan
        pool:
          vmImage: 'ubuntu-latest'
        steps:
          - task: QualysContainerScan@1
            inputs:
              qualysConnection: 'QualysConnection'
              imageId: 'myacr.azurecr.io/myapp:$(Build.BuildId)'
              maxCritical: 0
              publishResults: true

  - stage: Deploy
    dependsOn: Security
    condition: succeeded()
    jobs:
      - deployment: DeployToProduction
        environment: 'production'

Next Steps

  • Code Scan - Scan source code for vulnerable dependencies
  • Configuration - Complete configuration reference
  • Overview - Return to Azure DevOps overview