> For the complete documentation index, see [llms.txt](https://docs.veedna.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.veedna.com/unifai/setting-up/scm-integrations/azure-pipelines-in-azure-devops.md).

# Azure Pipelines in Azure DevOps

## Using Azure Pipelines in Azure DevOps

### Choose an MCP Server configuration

Choose the MCP Server configuration for this pipeline.

#### Lineaje-hosted MCP Server

Use the existing Lineaje SaaS MCP endpoint configuration below.

```
https://mcp-code.v2.prod.veedna.com/mcp
```

#### Customer-hosted Hybrid MCP Server

Configure the pipeline to use your Hybrid UnifAI MCP Server endpoint:

```
https://<CUSTOMER_VM_OR_FQDN>/mcp
```

Before configuring this option:

* The Hybrid UnifAI MCP Server must already be deployed.
* The MCP Server health check must succeed.
* The Azure Pipelines agent must be able to reach the configured MCP endpoint.

Deploy and verify the server first in [Deploy UnifAI on Azure](/unifai/setting-up/deploy-unifai-on-azure.md).

### Lineaje-hosted MCP Server

Use these steps if your repositories are on Azure Repos Git in Azure DevOps Services. The scan runs on a Microsoft-hosted agent and connects to the Lineaje SaaS MCP endpoint.

#### Before You Start

* An Azure DevOps organization with an Azure Repos Git repository, and permission to create pipelines and pipeline variables in the project.
* A Lineaje PAT (Personal Access Token).

{% stepper %}
{% step %}

### Generate a Lineaje PAT

A Lineaje PAT authenticates the Azure Pipeline to Lineaje.

Follow these steps to generate a Lineaje PAT from your Lineaje account. The token grants the pipeline access to the Lineaje scan service:

1. Log in at app.veedna.com and navigate to **SBOM360** → **Integrations** → **Deploy Private Cloud** → **Personal Access Token**.
2. Click the link in the box.
3. Click **Generate Token**. A Lineaje login webpage opens.
4. Authenticate and click **Yes**. You are returned to the **Integrations** page.
5. Copy the **PAT** displayed on the Integrations page.
   {% endstep %}

{% step %}

### Add the Lineaje PAT to Your Azure Pipeline

Store the Lineaje PAT as a secret pipeline variable.

1. **For a single pipeline** — after creating the pipeline (step 5), open it, choose **Edit → Variables → New variable**:
   * Name: `LINEAJE_PAT_TOKEN`
   * Value: your Lineaje PAT
   * Tick **Keep this value secret**, then **OK → Save**.<br>
2. **For several repositories** — use a variable group instead, which is the Azure equivalent of a GitHub organization secret:
   * **Pipelines → Library → + Variable group**, name it e.g. `lineaje`.
   * Add `LINEAJE_PAT_TOKEN`, click the padlock to mark it secret, and **Save**.
   * Reference it from each pipeline by adding to `.azuredevops/lineaje-unifai-scan.yml`:

     ```yaml
     variables:
       - group: lineaje
       - name: createFixPrFlag
       # ...the existing entries stay as they are
     ```
   * On the group's **Pipeline permissions** tab, grant access to each pipeline that needs it.
     {% endstep %}

{% step %}

### Use default System.AccessToken or generate custom token

By default, the pipeline authenticates to Azure DevOps with `$(System.AccessToken)`, the build identity's own OAuth token. This is limited to only scans and cannot be used for remediation (ie. creating pull requests).

To enable write access using the `SystemToken`, go to **Project Settings → Repositories →&#x20;*****your repo*****&#x20;→ Security**, find the identity named **`<Project Name>` Build Service (`<Organization Name>`)**, and set `Contribute`, `Create branch`, and `Contribute to pull requests` to "Allow".

To use an Azure DevOps PAT if the build service identity can't be granted write access, or if the repository lives in a different project or organization from the pipeline:

1. **User settings → Personal access tokens → New Token**, scope **Code (Read & Write)**.
2. Add it as a secret pipeline variable named `AZURE_DEVOPS_PAT`.
3. Map it into the scan step alongside the existing `env:` entries:

   ```yaml
       env:
         LINEAJE_PAT_TOKEN: $(LINEAJE_PAT_TOKEN)
         AZURE_DEVOPS_PAT: $(AZURE_DEVOPS_PAT)
   ```

   \
   The scanner prefers `SYSTEM_ACCESSTOKEN` when both are present, so drop the `SYSTEM_ACCESSTOKEN` line if you plan to use `AZURE_DEVOPS_PAT`.
   {% endstep %}

{% step %}

### Copy script to .azuredevops folder

Download the following script and place it in the same repository under the `.azuredevops` folder.

{% file src="/files/ZviAQOY2L6EIWzgd74oN" %}
{% endstep %}

{% step %}

### Copy the Azure Pipelines YAML file to the central repository

Copy the below script into a new file in the central repository at `.azuredevops/lineaje-unifai-scan.yml`.

The `trigger:` or `pr:` configurations can trigger new commits or pull requests. The following pipeline runs manually.

```yaml
name: Lineaje UnifAI Scan

trigger: none
pr: none

parameters:
  - name: createFixPr
    displayName: Open a remediation PR with the suggested fixes
    type: boolean
    default: true
  - name: mcpServerUrl
    displayName: UnifAI MCP Server URL
    type: string
    default: https://mcp.v2.prod.veedna.com/mcp
  - name: skipTlsVerify
    displayName: Skip TLS verification for a self-signed Hybrid MCP certificate
    type: boolean
    default: false

variables:
  - name: createFixPrFlag
    ${{ if eq(parameters.createFixPr, true) }}:
      value: --create-fix-pr
    ${{ else }}:
      value: ''
  - name: tlsSkipVerify
    ${{ if eq(parameters.skipTlsVerify, true) }}:
      value: '1'
    ${{ else }}:
      value: '0'
  - name: reportPath
    value: $(Build.ArtifactStagingDirectory)/unifai-report.md
  - name: scannerPath
    value: $(Agent.TempDirectory)/scm_unifai_scan_azure.py

pool:
  vmImage: ubuntu-latest

steps:
  - checkout: self
    displayName: Checkout repository
    fetchDepth: 1

  - task: UsePythonVersion@0
    displayName: Set up Python
    inputs:
      versionSpec: '3.11'

  - script: pip install mcp==1.27.1 httpx
    displayName: Install dependencies

  - script: |
      set -euo pipefail
      cp .azuredevops/scm_unifai_scan_azure.py "$(scannerPath)"
      
      # Optional: Exclude test or other folders to scan only what you want UnifAI to analyze
      # rm -rf tests/ temp/
      
      mkdir -p "$(Build.ArtifactStagingDirectory)"
    displayName: Prepare scanner

  - script: |
      set -euo pipefail
      python "$(scannerPath)" \
        --source-path . \
        --exclude .git \
        --exclude .azuredevops \
        --mcp-server-url "${{ parameters.mcpServerUrl }}" \
        $(createFixPrFlag) | tee "$(reportPath)"
    displayName: Run Lineaje AI policy scan
    env:
      LINEAJE_PAT_TOKEN: $(LINEAJE_PAT_TOKEN)
      SYSTEM_ACCESSTOKEN: $(System.AccessToken)
      MCP_TLS_INSECURE_SKIP_VERIFY: $(tlsSkipVerify)
      # Optional alternative to the Build Service token:
      # AZURE_DEVOPS_PAT: $(AZURE_DEVOPS_PAT)

  - script: |
      set -euo pipefail
      if [ -s "$(reportPath)" ]; then
        echo "##vso[task.uploadsummary]$(reportPath)"
      else
        echo "No report produced - nothing to attach."
      fi
    displayName: Attach report to run summary
    condition: always()

  - publish: $(reportPath)
    artifact: unifai-report
    displayName: Publish report artifact
    condition: always()
    continueOnError: true

```

{% endstep %}

{% step %}

### Create the pipeline

1. **Pipelines → New pipeline**
2. **Azure Repos Git** → select your repository
3. **Existing Azure Pipelines YAML file**
4. Branch: your default branch. Path: `/.azuredevops/lineaje-unifai-scan.yml`
5. **Continue → Save** (use the dropdown next to *Run* to save without running)

Rename it from the default (the repo name) to something like `lineaje-unifai-scan` under **Pipeline settings → Rename/move**.
{% endstep %}

{% step %}

### Run the Azure Pipeline

To trigger: **Pipelines →&#x20;*****your pipeline*****&#x20;→ Run pipeline.**

The **Open a remediation PR with the suggested fixes** checkbox maps to the scanner's `--create-fix-pr` flag and is on by default.
{% endstep %}
{% endstepper %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.veedna.com/unifai/setting-up/scm-integrations/azure-pipelines-in-azure-devops.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
