> For the complete documentation index, see [llms.txt](https://developer.harness.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.harness.io/sast-and-sca/prezero/static-analysis-sast/workflows/azure-devops.md).

# Azure DevOps

This article shows you how to integrate Qwiet AI by Harness into your Azure DevOps workflow to provide automated code analysis. We provide two sets of instructions based on how you define your Azure pipelines: using YAML syntax or the Classic interface.

{% tabs %}
{% tab title="YAML" %}

## For Azure Pipelines defined using YAML syntax <a href="#for-azure-pipelines-defined-using-yaml-syntax" id="for-azure-pipelines-defined-using-yaml-syntax"></a>

This tutorial assumes that you have an existing YAML-base [Azure Pipeline](https://docs.microsoft.com/en-us/azure/devops/pipelines/get-started/pipelines-get-started?view=azure-devops) defined. You will be adding the tasks required to integrate Qwiet AI by Harness to this file.

## Step 1: Create your secret variables <a href="#step-1-create-your-secret-variables" id="step-1-create-your-secret-variables"></a>

You will need to create secret variables to store authentication information for Qwiet.

We recommend creating your secret variables using [variable groups](https://docs.microsoft.com/en-us/azure/devops/pipelines/library/variable-groups?view=azure-devops\&tabs=yaml#use-a-variable-group) since this method balances security with ease of deployment across multiple repositories.

![Add variable group](/files/ffrhnjFN64qzpafJwG5U)

However, you could also create your secret variables using:

* The [settings UI for a Pipeline](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops\&tabs=yaml%2Cbatch#secret-variables)
* The [Azure Key Vault](https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/deploy/azure-key-vault?view=azure-devops)

When creating a variable group, we recommend calling it something like `shiftleft-token`. You can then provide your access token as `SHIFTLEFT_ACCESS_TOKEN`.

When running in a production environment, we recommend that you use a CI config token as the access token. You can [create your CI config token](/sast-and-sca/prezero/integrations/tokens.md) in the Qwiet Dashboard.

![Add Qwiet tokens](/files/NRO6cA3AWWsSmvNYN0pj)

At this point, you can refer directly to this group in the pipeline's YAML configuration file using the `group` property under the `variables` section.

```
variables:
- group: shiftleft-token
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

## Step 2: Add Qwiet to your pipeline <a href="#step-2-add-qwiet-to-your-pipeline" id="step-2-add-qwiet-to-your-pipeline"></a>

You will need to include instructions in your Pipeline to download the Qwiet CLI so that the Pipeline can run Qwiet AI by Harness.

If you're running Windows, you can do so using a PowerShell task:

```
- task: PowerShell@2
  displayName: Download Qwiet cli
  inputs:
    targetType: 'inline'
    script: |
      Invoke-WebRequest -Uri 'https://cdn.shiftleft.io/download/sl-latest-windows-x64.zip' -OutFile $(Agent.HomeDirectory)\sl.zip
      Expand-Archive -Path $(Agent.HomeDirectory)\sl.zip -DestinationPath $(Agent.HomeDirectory) -Force\
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

If you're running Linux or macOS, you can use a script task:

```
- task: CmdLine@2
  displayName: Download Qwiet cli
  inputs:
    targetType: 'inline'
    script: |
      curl https://cdn.shiftleft.io/download/sl > $(Agent.HomeDirectory)/sl && chmod a+rx $(Agent.HomeDirectory)/sl
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

## Step 3: Invoke Qwiet AI by Harness for code analysis <a href="#step-3-invoke-qwiet-ai-by-harness-for-code-analysis" id="step-3-invoke-qwiet-ai-by-harness-for-code-analysis"></a>

The following sections will show you how to analyze your Java or C# applications.

When invoking Qwiet AI by Harness, you need to refer to the `SHIFTLEFT_ACCESS_TOKEN` variable. As suggested earlier in this article, if you create a variable group, these variables will be available automatically to all of your Pipelines.

### Analyzing a Java application <a href="#analyzing-a-java-application" id="analyzing-a-java-application"></a>

The following examples show how you can build your Java application (which is required before Qwiet AI by Harness can analyze your code), then use the Qwiet CLI to invoke Qwiet AI by Harness for code analysis.

Please note that the Qwiet CLI requires Java 8 to create the Code Property Graph (CPG) representation of your source code. This is necessary before code analysis unless you are analyzing a project written in C#. If so, you can skip this step.

To set up Java 8, use the [Java Tool Installer](https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/tool/java-tool-installer?view=azure-devops).

```
- task: JavaToolInstaller@0
  inputs:
    versionSpec: '8'
    jdkArchitectureOption: 'x64'
    jdkSourceOption: 'PreInstalled'
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

On Windows:

```
- task: Maven@3
  inputs:
    mavenPomFile: 'pom.xml'
    mavenOptions: '-Xmx3072m'
    javaHomeOption: 'JDKVersion'
    jdkVersionOption: '1.8'
    jdkArchitectureOption: 'x64'
    publishJUnitResults: false
    goals: 'package'

- task: CmdLine@2
  displayName: Analyze with Qwiet AI by Harness
  inputs:
    script: |
      $(Agent.HomeDirectory)\sl.exe analyze --app ShiftLeftJavaAzWin --tag branch=$(Build.SourceBranchName) --java target/hello-shiftleft-0.0.1.jar
    workingDirectory: '$(Build.SourcesDirectory)'
  env:
    SHIFTLEFT_ACCESS_TOKEN: $(SHIFTLEFT_ACCESS_TOKEN)
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

On Linux/macOS:

```
- task: CmdLine@2
  displayName: Analyze with Qwiet AI by Harness
  inputs:
    script: |
      $(Agent.HomeDirectory)/sl analyze --app ShiftLeftJava --tag branch=$(Build.SourceBranchName) --java target/hello-shiftleft-0.0.1.jar
    workingDirectory: '$(Build.SourcesDirectory)'
  env:
    SHIFTLEFT_ACCESS_TOKEN: $(SHIFTLEFT_ACCESS_TOKEN)
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

### Analyzing a C# application <a href="#analyzing-a-c-application" id="analyzing-a-c-application"></a>

The following examples show you how to build your .NET Core application, then use the Qwiet CLI to invoke Qwiet AI by Harness for code analysis.

```
- task: DotNetCoreCLI@2
  displayName: Build console app
  inputs:
    command: 'build'
    projects: '$(Build.SourcesDirectory)\netcoreConsole'

- task: CmdLine@2
  displayName: Analyze with Qwiet AI by Harness
  inputs:
    script: |
      $(Agent.HomeDirectory)\sl.exe analyze --app netcoreConsole --tag branch=$(Build.SourceBranchName) --csharp netcoreConsole/netcoreConsole.csproj
    workingDirectory: '$(Build.SourcesDirectory)'
  env:
    SHIFTLEFT_ACCESS_TOKEN: $(SHIFTLEFT_ACCESS_TOKEN)
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

For .NET applications, pass in the **.sln** file instead of the **.csproj** file:

```
- task: CmdLine@2
  displayName: Analyze with Qwiet AI by Harness
  inputs:
    script: |
      $(Agent.HomeDirectory)\sl.exe analyze --app netfwWebapi --tag branch=$(Build.SourceBranchName) --csharp netfwWebapi/netfwWebapi.sln
    workingDirectory: '$(Build.SourcesDirectory)'
  env:
    SHIFTLEFT_ACCESS_TOKEN: $(SHIFTLEFT_ACCESS_TOKEN)
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

To pass additional information to Azure, you can use [predefined variables](https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops\&tabs=yaml) that you then reference in the `env` block of your script.

### Recursively finding and scanning your solution/project files <a href="#recursively-finding-and-scanning-your-solutionproject-files" id="recursively-finding-and-scanning-your-solutionproject-files"></a>

The following example shows you how to modify the `sl analyze` invocation to recursively find all `.sln` files and scan them with Qwiet:

```
# Recursively find all .sln files and scan them with Qwiet AI by Harness
# Be sure to change the app.group value from "test-appgroup" to your preferred name
Get-ChildItem -Path . -Filter *.sln -Recurse -ErrorAction SilentlyContinue -Force | ForEach-Object {
    sl.exe analyze --csharp --oss-project-dir $($_.Directory) --tag app.group=test-appgroup --app $($_.Name -replace '.sln', '') $($_.FullName)
}
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

Alternatively, you can recursively find and scan all .csproj files if `.sln`-based scans are taking too long:

```
# Recursively find all .csproj files and scan them with Qwiet AI by Harness
# Use this when .sln based scans are taking too long
# Be sure to change the app.group value from ß"test-appgroup" to your preferred name
Get-ChildItem -Path . -Filter *.csproj -Recurse -ErrorAction SilentlyContinue -Force | ForEach-Object {
  sl.exe analyze --csharp --oss-project-dir $($_.Directory) --tag app.group=test-appgroup --app $($_.Name -replace '.csproj', '') $($_.FullName)
}
```

![](/files/CRrxFX9wuboXxDrdB2dv)

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)
{% endtab %}

{% tab title="Classic" %}

## For Azure Pipelines defined using the classic interface <a href="#for-azure-pipelines-defined-using-the-classic-interface" id="for-azure-pipelines-defined-using-the-classic-interface"></a>

If you haven't already, sign in to your Azure DevOps organization. Then, open up the project where you would like to configure your pipeline and navigate to the details page for your pipeline.

You need to create a series of tasks to execute each part of the code analysis workflow.

### Step 1: Download Qwiet AI by Harness <a href="#step-1-download-qwiet-ai-by-harness" id="step-1-download-qwiet-ai-by-harness"></a>

First, create a task to download Qwiet. This will be an **inline** task, and under **Script**, provide:

```
Invoke-WebRequest -Uri 'https://cdn.shiftleft.io/download/sl-latest-windows-x64.zip' -OutFile $(Agent.HomeDirectory)\sl.zip

Expand-Archive -Path $(Agent.HomeDirectory)\sl.zip -DestinationPath $(Agent.HomeDirectory) -Force\
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

![Download Qwiet](/files/jDEqi7GksPeOAD1JbY4n)

### Step 2: Run Qwiet AI by Harness <a href="#step-2-run-qwiet-ai-by-harness" id="step-2-run-qwiet-ai-by-harness"></a>

Next, create another inline task to run Qwiet and analyze your code. The required **script** is:

```
$(Agent.HomeDirectory)\sl.exe analyze --app netcoreConsole --tag branch=$(Build.SourceBranchName) --csharp --dotnet netcoreConsole/netcoreConsole/netcoreConsole.csproj
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

![Set code analysis to run](/files/8PYCr0rSPyQsVVnnLmxF)

### Step 3: Display findings <a href="#step-3-display-findings" id="step-3-display-findings"></a>

Finally, create your third inline task to display your results:

```
$findings = Invoke-RestMethod -Uri "https://app.shiftleft.io/api/v4/orgs/$(SHIFTLEFT_ORG_ID)/apps/netcoreConsole/findings" -Headers @{ 'Authorization' = "Bearer $(SHIFTLEFT_ACCESS_TOKEN)"  }

$findings.response.findings | Format-Table id, severity, title

Write-Host "Visit https://app.shiftleft.io/findingsSummary/netcoreConsole?apps=netcoreConsole&isApp=1 for details"
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

Be sure to update the `SHIFTLEFT_ACCESS_TOKEN` placeholder with your Qwiet access token, which is available in the Qwiet [Dashboard](https://app.shiftleft.io/user/profile) under **Access Token**.

![Display the results of your findings](/files/1IIdcNlDFeilSchZBat4)

### Recursively finding and scanning your solution/project files <a href="#recursively-finding-and-scanning-your-solutionproject-files-1" id="recursively-finding-and-scanning-your-solutionproject-files-1"></a>

The following example shows you how to modify the `sl analyze` invocation to recursively find all `.sln` files and scan them with Qwiet:

```
# Recursively find all .sln files and scan them with Qwiet AI by Harness
# Be sure to change the app.group value from "test-appgroup" to your preferred name
Get-ChildItem -Path . -Filter *.sln -Recurse -ErrorAction SilentlyContinue -Force | ForEach-Object {
  sl.exe analyze --csharp --oss-project-dir $($_.Directory) --tag app.group=test-appgroup --app $($_.Name -replace '.sln', '') $($_.FullName)
}
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)

Alternatively, you can recursively find and scan all .csproj files if `.sln`-based scans are taking too long:

```
# Recursively find all .csproj files and scan them with Qwiet AI by Harness
# Use this when .sln based scans are taking too long
# Be sure to change the app.group value from ß"test-appgroup" to your preferred name
Get-ChildItem -Path . -Filter *.csproj -Recurse -ErrorAction SilentlyContinue -Force | ForEach-Object {
  sl.exe analyze --csharp --oss-project-dir $($_.Directory) --tag app.group=test-appgroup --app $($_.Name -replace '.csproj', '') $($_.FullName)
}
```

![](/files/DfHDmRpYn5HJOmhRtTva) ![](/files/P5vj8JOtHNJWVEvAqKym)
{% endtab %}
{% endtabs %}

## Running `sl check-analysis` in Azure DevOps <a href="#running-sl-check-analysis-in-azure-devops" id="running-sl-check-analysis-in-azure-devops"></a>

Qwiet's [`check-analysis` feature](https://app.gitbook.com/s/4t03gua31tHpZwtcczPO/reference/check-analysis) allows you to compare your analysis results against a set of [build rules](/sast-and-sca/prezero/customization/build-rules-v2.md) you've defined. To use `check-analysis` in your Azure DevOps workflow, you must first [enable build validation](https://docs.microsoft.com/en-us/azure/devops/repos/git/branch-policies?view=azure-devops\&tabs=browser#build-validation) on your branch.
