Available Options
There are multiple authentication methods available. The first fork in the decision tree is whether you have the Observes extension installed.
- Observes Scanner - Extension Installed
- Standalone Scanner - Extension Not Installed
For a deep dive on configuring Managed Identity or Workload Identity Federation, follow this guide.
Observes Scanner - Extension Installed
The Observes task supports two service connection types, selected via the --auth-mode input. Each supports multiple credential kinds.
- Option 1: Observes Service Connection
- Option 2: ARM Service Connection
Option 1: Observes Service Connection
An Observes service connection is created under Project Settings > Service connections > New service connection > Observes Azure DevOps Scanner. It supports three credential kinds:
1a. PAT (Personal Access Token)
Best for quick setup. Least recommended for production due to manual token rotation.
Create the service connection:
- Go to Project Settings > Service connections > New service connection > Observes Azure DevOps Scanner
- Select Basic Authentication as the Authentication method
- Enter the PAT token in the input field
- Name the Service Connection and save (for example:
service-connection-observes-pat)
Pipeline:
- task: observes@1
displayName: 'Observes Security Scan'
inputs:
authMethod: observes
observesServiceConnection: 'service-connection-observes-pat'
organization: 'my-ado-org'
1b. Service Principal - Client Secret
Create the service connection:
- Go to Project Settings > Service connections > New service connection > Observes Azure DevOps Scanner
- Select Token Based Authentication as the Authentication method
- Enter your Tenant ID, Client ID, and Client Secret
- Name the Service Connection and save (for example:
service-connection-observes-app-reg-secret)
Pipeline:
- task: observes@1
displayName: 'Observes Security Scan'
inputs:
authMethod: observes
observesServiceConnection: 'service-connection-observes-app-reg-secret'
organization: 'my-ado-org'
1c. Service Principal - Certificate
Create the service connection:
- Go to Project Settings > Service connections > New service connection > Observes Azure DevOps Scanner
- Select Certificate Based as the Authentication method
- Enter your Tenant ID, Client ID, and upload the PEM certificate
- Name the Service Connection and save (for example:
service-connection-observes-app-reg-cert)
Pipeline:
- task: observes@1
displayName: 'Observes Security Scan'
inputs:
authMethod: observes
observesServiceConnection: 'service-connection-observes-app-reg-cert'
organization: 'my-ado-org'
Option 2: ARM Service Connection
Uses a standard Azure Resource Manager service connection. If you already have an ARM service connection configured in your project, you can point the Observes task directly at it. Because an ARM connection does not embed an Azure DevOps organization name, the organization field is always required when using this method.
The task automatically maps the ARM connection's credential kind to the correct scanner auth mode:
2a. Workload Identity Federation (recommended)
Head over to this guide for more information about managed identity set ups.
Create the service connection:
- Go to Project Settings > Service connections > New service connection > Azure Resource Manager
- Select Workload Identity Federation (automatic) - ADO creates and configures the app registration for you
- Select your subscription and optionally scope to a resource group
- Name the Service Connection and save (for example:
service-connection-arm-app-reg-wif)
Pipeline:
- task: observes@1
displayName: 'Observes Security Scan'
inputs:
authMethod: arm
armServiceConnection: 'service-connection-arm-app-reg-wif'
organization: 'my-ado-org'
Note: System-Assigned Managed Identity connections only work on self-hosted agents running on Azure compute with a managed identity attached. For Microsoft-hosted runners, use Workload Identity Federation / User-Assigned Managed Identities attached to the pipeline instead.
2b. Service Principal - Client Secret
Create the service connection:
- Go to Project Settings > Service connections > New service connection > Azure Resource Manager
- Select Service Principal (manual)
- Enter your Tenant ID, Subscription ID, Client ID, and Client Secret
- Name the Service Connection and save (for example:
service-connection-arm-app-reg-secret)
Pipeline:
- task: observes@1
displayName: 'Observes Security Scan'
inputs:
authMethod: arm
armServiceConnection: 'service-connection-arm-app-reg-secret'
organization: 'my-ado-org'
2c. Service Principal - Certificate
Create the service connection:
1. Go to Project Settings > Service connections > New service connection > Azure Resource Manager
2. Select Service Principal (manual)
3. Enter your Tenant ID, Subscription ID, Client ID, and upload the certificate
4. Name the Service Connection and save (for example: service-connection-arm-app-reg-cert)
Pipeline:
- task: observes@1
displayName: 'Observes Security Scan'
inputs:
authMethod: arm
armServiceConnection: 'service-connection-arm-app-reg-cert'
organization: 'my-ado-org'
Standalone Scanner - Extension Not Installed
When running the scanner without the Observes extension, you can leverage the command line options to use the authentication method that best suits you:
| Mode | Flag | Description |
|---|---|---|
| Default | --auth-mode default |
Auto-detects credentials via DefaultAzureCredential. Tries env vars, workload identity, managed identity, and Azure CLI session in order. Best for pipelines using AzureCLI@3 with a service connection. |
| PAT | --auth-mode pat -p <TOKEN> |
Personal Access Token (Basic auth). PAT can also be set via AZURE_DEVOPS_PAT env var. Best for local/quick testing. |
| Service Principal | --auth-mode service-principal |
Microsoft Entra app registration. Requires --tenant-id, --client-id, and one of --client-secret or --client-certificate-path. |
| Managed Identity | --auth-mode managed-identity |
Azure-managed identity. Requires running on Azure compute. Requires --client-id if using user-assigned identities. |
Default (AzureCLI@3 with service connection)
The recommended approach for most pipelines. ADO injects credentials automatically via the service connection — no secrets to manage in the pipeline itself.
- task: AzureCLI@3
displayName: 'Observes Security Scan'
inputs:
azureSubscription: 'service-connection-arm-app-reg-wif'
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
python scan.py -o my-ado-org -j my_scan
PAT (Personal Access Token)
Store the PAT as a secret in a key vault, variable group, secure file or your agreed secret storage pattern. Then make it available to the script either by using environment variables or directly in the command line script.
# Using environment variables
- script: |
python scan.py -o my-ado-org -j my_scan --auth-mode pat
displayName: 'Observes Security Scan'
env:
AZURE_DEVOPS_PAT: $(OBSERVES_PAT)
# OR - Using the command line flag
- script: |
python scan.py -o my-ado-org -j my_scan --auth-mode pat -p $(OBSERVES_PAT)
displayName: 'Observes Security Scan'
Your secret may be exposed in the logs when using the command-line flag, hence it is not recommended.
Service Principal - Client Secret
Store AZURE_CLIENT_SECRET as a secret in a key vault, variable group, secure file or your agreed secret storage pattern. Then make it available to the script either by using environment variables or directly in the command line script.
# Using environment variables
- script: |
python scan.py -o my-ado-org -j my_scan \
--auth-mode service-principal \
--tenant-id $(AZURE_TENANT_ID) \
--client-id $(AZURE_CLIENT_ID)
displayName: 'Standalone - SP Secret'
env:
AZURE_CLIENT_SECRET: $(AZURE_CLIENT_SECRET)
# OR - Using the command line flag
- script: |
python scan.py -o my-ado-org -j my_scan \
--auth-mode service-principal \
--tenant-id $(AZURE_TENANT_ID) \
--client-id $(AZURE_CLIENT_ID) \
--client-secret $(AZURE_CLIENT_SECRET)
displayName: 'Observes Security Scan'
Your secret may be exposed in the logs when using the command-line flag, hence it is not recommended.
Service Principal - Certificate
The certificate file must be available on the agent at run time. Store it somewhere safe, like in a key vault and when running in the pipeline, download it to an accessible place.
- script: |
python scan.py -o my-ado-org -j my_scan \
--auth-mode service-principal \
--tenant-id $(AZURE_TENANT_ID) \
--client-id $(AZURE_CLIENT_ID) \
--client-certificate-path /path/to/certificate.pem
displayName: 'Observes Security Scan'
Managed Identity
Requires a self-hosted agent running on Azure compute with a managed identity attached.
- For system-assigned identities, omit
--client-id. - For user-assigned identities, provide
--client-idexplicitly. This is because a single workload may have multiple user-assigned identities, and you need to specify which one you want to use in the scan.
# System-assigned managed identity
- script: |
python scan.py -o my-ado-org -j my_scan --auth-mode managed-identity
displayName: 'Observes Security Scan'
# User-assigned managed identity
- script: |
python scan.py -o my-ado-org -j my_scan \
--auth-mode managed-identity \
--client-id $(AZURE_CLIENT_ID)
displayName: 'Observes Security Scan'
Default Behaviour
When you run python scan.py -o my_org -j my_scan without specifying --auth-mode, the scanner automatically tries to authenticate using DefaultAzureCredential, which checks the following sources in order:
- Environment variables (
AZURE_TENANT_ID+AZURE_CLIENT_ID+AZURE_CLIENT_SECRET/AZURE_CLIENT_CERTIFICATE_PATH) - Workload identity (example: AKS workload identity webhook)
- Managed identity (system- or user-assigned, when running on Azure compute)
- Azure CLI session (when running inside an
AzureCLI@3pipeline task or afteraz loginlocally)
Full Example - All methods in one pipeline
trigger: none
pool:
vmImage: ubuntu-latest
jobs:
# Extension - ARM - Workload Identity Federation
- job: RunObservesArmWif
steps:
- task: observes@1
displayName: 'ARM - WIF'
inputs:
authMethod: arm
armServiceConnection: 'service-connection-arm-app-reg-wif'
organization: 'my-ado-org'
# Extension - ARM - Service Principal (secret)
- job: RunObservesArmSecret
steps:
- task: observes@1
displayName: 'ARM - SP Secret'
inputs:
authMethod: arm
armServiceConnection: 'service-connection-arm-app-reg-secret'
organization: 'my-ado-org'
# Extension - ARM - Service Principal (certificate)
- job: RunObservesArmCert
steps:
- task: observes@1
displayName: 'ARM - SP Certificate'
inputs:
authMethod: arm
armServiceConnection: 'service-connection-arm-app-reg-cert'
organization: 'my-ado-org'
# Extension - Observes - PAT
- job: RunObservesObsPat
steps:
- task: observes@1
displayName: 'Observes - PAT'
inputs:
authMethod: observes
observesServiceConnection: 'service-connection-observes-pat'
organization: 'my-ado-org'
# Extension - Observes - Service Principal (secret)
- job: RunObservesObsSecret
steps:
- task: observes@1
displayName: 'Observes - SP Secret'
inputs:
authMethod: observes
observesServiceConnection: 'service-connection-observes-app-reg-secret'
organization: 'my-ado-org'
# Extension - Observes - Service Principal (certificate)
- job: RunObservesObsCert
steps:
- task: observes@1
displayName: 'Observes - SP Certificate'
inputs:
authMethod: observes
observesServiceConnection: 'service-connection-observes-app-reg-cert'
organization: 'my-ado-org'
# Standalone - Default (AzureCLI@3 with WIF service connection)
- job: RunStandaloneDefault
steps:
- task: AzureCLI@3
displayName: 'Standalone - Default (AzureCLI WIF)'
inputs:
azureSubscription: 'service-connection-arm-app-reg-wif'
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
python scan.py -o my-ado-org -j my_scan
# Standalone - PAT
- job: RunStandalonePat
steps:
- script: |
python scan.py -o my-ado-org -j my_scan --auth-mode pat
displayName: 'Standalone - PAT'
env:
AZURE_DEVOPS_PAT: $(OBSERVES_PAT)
# Standalone - Service Principal (secret)
- job: RunStandaloneSpSecret
steps:
- script: |
python scan.py -o my-ado-org -j my_scan \
--auth-mode service-principal \
--tenant-id $(AZURE_TENANT_ID) \
--client-id $(AZURE_CLIENT_ID)
displayName: 'Standalone - SP Secret'
env:
AZURE_CLIENT_SECRET: $(AZURE_CLIENT_SECRET)
# Standalone - Service Principal (certificate)
- job: RunStandaloneSpCert
steps:
- script: |
python scan.py -o my-ado-org -j my_scan \
--auth-mode service-principal \
--tenant-id $(AZURE_TENANT_ID) \
--client-id $(AZURE_CLIENT_ID) \
--client-certificate-path /path/to/certificate.pem
displayName: 'Standalone - SP Certificate'
# Standalone - Managed Identity (system-assigned)
- job: RunStandaloneMiSystem
pool:
name: my-self-hosted-pool # must run on Azure compute with managed identity attached
steps:
- script: |
python scan.py -o my-ado-org -j my_scan --auth-mode managed-identity
displayName: 'Standalone - Managed Identity (system-assigned)'
# Standalone - Managed Identity (user-assigned)
- job: RunStandaloneMiUser
pool:
name: my-self-hosted-pool # must run on Azure compute with managed identity attached
steps:
- script: |
python scan.py -o my-ado-org -j my_scan \
--auth-mode managed-identity \
--client-id $(AZURE_CLIENT_ID)
displayName: 'Standalone - Managed Identity (user-assigned)'
Final remarks / FAQ
- Service principal and managed identity auth require pip install azure-identity.
- No explicit credentials are needed when running inside an AzureCLI@3 pipeline task with a service connection - the scanner picks up the active session automatically.
- If using --auth-mode pat and no PAT token is provided (-p
), the tool will exit with an error. For PAT authentication (--auth-mode pat), also specify the PAT token. - As a CLI argument: -p
or --pat-token - As an environment variable: AZURE_DEVOPS_PAT
- If you have installed the Observes extension, the observes pipeline task handles authentication automatically - you do not need to pass --auth-mode or any credential flags manually.