Skip to main content

Azure DevOps

Similar to the Source Control Management (SCM) integration plugin, the Azure DevOps integration plugin for Soundcheck provides out-of-box integration with Azure DevOps by leveraging Backstage's Azure DevOps integration to implement collection of facts from Azure DevOps repositories.

The purpose of the Azure DevOps integration plugin is to provide Azure DevOps-specific fact collection (like branch policies), while the SCM integration plugin provides the collection of facts based on repository content.

The Azure DevOps integration plugin supports the collection of the following facts:

Prerequisites

Configure Azure DevOps integration in Backstage

Integrations are configured at the root level of app-config.yaml, here's an example configuration for Azure DevOps:

integrations:
azure:
- host: dev.azure.com
credentials:
- personalAccessToken: ${PERSONAL_ACCESS_TOKEN}

Follow the instructions for full details on configuration.

Add the AzureDevOpsFactCollector to Soundcheck

First add the package:

yarn workspace backend add @spotify/backstage-plugin-soundcheck-backend-module-azure

Then add the following to your packages/backend/src/index.ts file:

packages/backend/src/index.ts
const backend = createBackend();

// ...
backend.add(import('@spotify/backstage-plugin-soundcheck-backend'));
backend.add(
import('@spotify/backstage-plugin-soundcheck-backend-module-azure'),
);
// ...

backend.start();

Consult the Soundcheck Backend documentation for additional details on setting up the Soundcheck backend.

Legacy Backend

warning

If you are still using the Legacy Backend you can follow these instructions but we highly recommend migrating to the New Backend System.

First add the package: yarn workspace backend add @spotify/backstage-plugin-soundcheck-backend-module-azure

Then in packages/backend/src/plugins/soundcheck.ts, add the AzureDevOpsFactCollector:

  import { SoundcheckBuilder } from '@spotify/backstage-plugin-soundcheck-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
+ import { AzureDevOpsFactCollector } from '@spotify/backstage-plugin-soundcheck-backend-module-azure';

export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
return SoundcheckBuilder.create({ ...env })
+ .addFactCollectors(
+ AzureDevOpsFactCollector.create(env.config, env.logger, env.cache),
+ )
.build();
}

Entity configuration

To be able to determine the repository to use the Azure DevOps integration will use the value from the backstage.io/source-location annotation. In many cases this will be set for you but if it is not you will need to add it to your catalog-info.yaml file, here's a simple example:

metadata:
annotations:
backstage.io/source-location: url:https://dev.azure.com/my-org/my-project/_git/my-repo/

Plugin Configuration

The collection of facts is driven by configuration. To learn more about the configuration, jump to the Defining Azure DevOps Fact Collectors.

  1. Create azure-facts-collectors.yaml in the root of your Backstage repository and fill in all your Azure DevOps fact collectors. A simple example Azure DevOps fact collector is listed below.

Note: this file will be loaded at runtime along with the rest of your Backstage configuration files, so make sure it's available in deployed environments in the same way as your app-config.yaml files.

---
frequency:
cron: '0 * * * *'
collects:
- type: branch_policies
branch: main
- type: branch_status
branch: main
- factName: stale_branches
type: number_of_branches
lastCommitMoreThanXDaysAgo: 15
- factName: stale_pull_requests
type: number_of_active_pull_requests
branch: main
createdMoreThanXDaysAgo: 15
- type: repository_details
  1. Add a soundcheck collectors field to app-config.yaml and reference the newly created azure-facts-collectors.yaml
# app-config.yaml
soundcheck:
collectors:
azure:
$include: ./azure-facts-collectors.yaml

Rate Limiting (Optional)

This fact collector can be rate limited in Soundcheck using the following configuration:

soundcheck:
job:
workers:
azure:
limiter:
max: 190
duration: 300000

Azure DevOps API has a limit of 200 times the consumption of a typical user within a sliding five-minute window. We recommend setting your rate limit to something below this, i.e. in the example above, we set the rate limit to 190 executions every 5 minutes.

This fact collector handles rate limit errors per the recommendation from Azure DevOps. Soundcheck will automatically wait and retry requests that are rate limited.

Defining Azure DevOps Fact Collectors

This section describes the data shape and semantics of Azure DevOps Fact Collectors.

Overall Shape Of Azure DevOps Fact Collector

The following is an example of a descriptor file for Azure DevOps Fact Collector:

---
frequency:
cron: '0 * * * *'
filter:
kind: 'Component'
cache:
duration:
hours: 2
collects:
- type: branch_policies
branch: main
filter:
- spec.lifecycle: 'production'
spec.type: 'website'
cache: false
- factName: stale_branches
type: number_of_branches
lastCommitMoreThanXDaysAgo: 15
cache: true
exclude:
- spec.type: 'documentation'

See below for details about these fields.

frequency [optional]

The frequency at which the collector should be executed. Possible values are either a cron expression { cron: ... } or HumanDuration. This is the default frequency for each collector.

initialDelay [optional]

The amount of time that should pass before the first invocation happens. Possible values are either a cron expression { cron: ... } or HumanDuration.

Example:

initialDelay:
seconds: 30

batchSize [optional]

The number of entities to collect facts for at once. Optional, the default value is 1.

Note: Fact collection for a batch of entities is still considered as one hit towards the rate limits by the Soundcheck Rate Limiting engine, while the actual number of hits will be equal to the batchSize.

Example:

batchSize: 100

filter [optional]

A filter specifying which entities to collect the specified facts for. Matches the filter format used by the Catalog API. This is the default filter for each collector.

exclude [optional]

Entities matching this filter will be skipped during the fact collection process. Can be used in combination with filter. Matches the filter format used by the Catalog API.

filter:
- kind: component
exclude:
- spec.type: documentation

cache [optional]

If the collected facts should be cached, and if so for how long. Possible values are either true or false or a nested { duration: HumanDuration } field. This is the default cache config for each collector.

collects [required]

An array describing which facts to collect and how to collect them. See below for details about the overall shape of a fact collector.

Overall Shape Of A Fact Collector

Each collector supports the fields described below.

factName [optional]

The name of the fact to be collected (must be unique within Azure DevOps collector).

  • Minimum length of 1
  • Maximum length of 100
  • Alphanumeric with single separator instances of periods, dashes, underscores, or forward slashes

If not provided it defaults to the type value (below).

type [required]

The type of the collector (e.g. branch_policies, repository_details).

frequency [optional]

The frequency at which the fact collection should be executed. Possible values are either a cron expression { cron: ... } or HumanDuration. If provided it overrides the default frequency provided at the top level. If not provided it defaults to the frequency provided at the top level. If neither collector's frequency nor default frequency is provided the fact will only be collected on demand. Example:

frequency:
minutes: 10

batchSize [optional]

The number of entities to collect facts for at once. Optional, the default value is 1. If provided it overrides the default batchSize provided at the top level. If not provided it defaults to the batchSize provided at the top level. If neither collector's batchSize nor default batchSize is provided the fact will be collected for one entity at a time.

Note: Fact collection for a batch of entities is still considered as one hit towards the rate limits by the Soundcheck Rate Limiting engine, while the actual number of hits will be equal to the batchSize.

Example:

batchSize: 100

branch [optional]

The branch to collect the fact from. If not provided, defaults to the repository's default branch.

filter [optional]

A filter specifying which entities to collect the specified facts for. Matches the filter format used by the Catalog API. If provided it overrides the default filter provided at the top level. If not provided it defaults to the filter provided at the top level. If neither collector's filter nor default filter is provided the fact will be collected for all entities.

exclude [optional]

Entities matching this filter will be skipped during the fact collection process. Can be used in combination with filter. Matches the filter format used by the Catalog API.

filter:
- kind: component
exclude:
- spec.type: documentation

cache [optional]

If the collected facts should be cached, and if so for how long. Possible values are either true or false or a nested { duration: HumanDuration } field. If provided it overrides the default cache config provided at the top level. If not provided it defaults to the cache config provided at the top level. If neither collector's cache nor default cache config is provided the fact will not be cached. Example:

cache:
duration:
hours: 24

Collecting Branch Policies Fact

Branch Policies fact contains information about configured branch policies for a given branch in Azure DevOps repository.

Shape of A Branch Policies Fact Collector

The shape of a Branch Policies Fact Collector matches the Overall Shape Of Azure DevOps Fact Collector (restriction: type: branch_policies).

The following is an example of the Branch Policies Fact Collector config:

collects:
- type: branch_policies
frequency:
cron: '0 * * * *'
filter:
- spec.lifecycle: 'production'
spec.type: 'website'
cache: false

Shape of A Branch Policies Fact

The shape of a Branch Policies Fact is based on the Fact Schema.

For a description of the data collected regarding branch policies, refer to the Azure DevOps API documentation.

The following is an example of the collected Branch Policies Fact:

factRef: azure:default/branch_policies
entityRef: component:default/queue-proxy
scope: default
timestamp: 2024-04-01T15:50+00Z
data:
- createdBy:
displayName: 'Normal Paulk'
url: 'https://vssps.dev.azure.com/fabrikam/_apis/Identities/ac5aaba6-a66a-4e1d-b508-b060ec624fa9'
_links:
avatar:
href: 'https://dev.azure.com/fabrikam/_apis/GraphProfile/MemberAvatars/aad.YmFjMGYyZDctNDA3ZC03OGRhLTlhMjUtNmJhZjUwMWFjY2U5'
id: 'ac5aaba6-a66a-4e1d-b508-b060ec624fa9'
uniqueName: 'dev@mailserver.com'
imageUrl: 'https://dev.azure.com/fabrikam/_api/_common/identityImage?id=ac5aaba6-a66a-4e1d-b508-b060ec624fa9'
descriptor: 'aad.YmFjMGYyZDctNDA3ZC03OGRhLTlhMjUtNmJhZjUwMWFjY2U5'
createdDate: '2024-02-28T19:19:36.127673Z'
isEnabled: true
isBlocking: true
isDeleted: false
settings:
authorEmailPatterns: '*'
scope:
- repositoryId: '2f3d611a-f012-4b39-b157-8db63f380226'
isEnterpriseManaged: false
_links:
self:
href: 'https://dev.azure.com/fabrikam/2f3d611a-f012-4b39-b157-8db63f380226/_apis/policy/configurations/4'
policyType:
href: 'https://dev.azure.com/fabrikam/2f3d611a-f012-4b39-b157-8db63f380226/_apis/policy/types/77ed4bd3-b063-4689-934a-175e4d0a78d7'
revision: 1
id: 4
url: 'https://dev.azure.com/fabrikam/2f3d611a-f012-4b39-b157-8db63f380226/_apis/policy/configurations/4'
type:
id: '77ed4bd3-b063-4689-934a-175e4d0a78d7'
url: 'https://dev.azure.com/fabrikam/2f3d611a-f012-4b39-b157-8db63f380226/_apis/policy/types/77ed4bd3-b063-4689-934a-175e4d0a78d7'
displayName: 'Commit author email validation'

Shape of A Branch Policies Fact Check

The shape of a Branch Policies Fact Check matches the Shape of a Fact Check.

The following is an example of the Branch Policies fact checks:

soundcheck:
checks:
- id: requires_author_email_validation
rule:
factRef: azure:default/branch_policies
path: $[?(@.type.displayName === 'Commit author email validation')].isEnabled
operator: contains
value: true

Collecting Branch Status Fact

Branch Status fact contains information about a status associated with the latest Git commit for a given branch in Azure DevOps repository.

Shape of A Branch Status Fact Collector

The shape of a Branch Status Fact Collector matches the Overall Shape Of Azure DevOps Fact Collector (restriction: type: branch_status).

The following is an example of the Branch Status Fact Collector config:

collects:
- type: branch_status
frequency:
cron: '0 * * * *'
filter:
- spec.lifecycle: 'production'
spec.type: 'website'
cache: false

Shape of A Branch Status Fact

The shape of a Branch Status Fact is based on the Fact Schema.

For a description of the data collected regarding branch Status, refer to the Azure DevOps API documentation.

The following is an example of the collected Branch Status Fact:

factRef: azure:default/branch_status
entityRef: component:default/queue-proxy
scope: default
timestamp: 2024-04-01T15:50+00Z
data:
name: 'refs/heads/main'
objectId: '917131a709996c5cfe188c3b57e9a6ad90e8b85c'
creator:
displayName: 'Normal Paulk'
url: 'https://vssps.dev.azure.com/fabrikam/_apis/Identities/ac5aaba6-a66a-4e1d-b508-b060ec624fa9'
_links:
avatar:
href: 'https://dev.azure.com/fabrikam/_apis/GraphProfile/MemberAvatars/aad.YmFjMGYyZDctNDA3ZC03OGRhLTlhMjUtNmJhZjUwMWFjY2U5'
id: 'ac5aaba6-a66a-4e1d-b508-b060ec624fa9'
uniqueName: 'dev@mailserver.com'
imageUrl: 'https://dev.azure.com/fabrikam/_api/_common/identityImage?id=ac5aaba6-a66a-4e1d-b508-b060ec624fa9'
descriptor: 'aad.YmFjMGYyZDctNDA3ZC03OGRhLTlhMjUtNmJhZjUwMWFjY2U5'
url: 'https://dev.azure.com/fabrikam/7484f783-66a3-4f27-b7cd-6b08b0b077ed/_apis/git/repositories/d3d1760b-311c-4175-a726-20dfc6a7f885/refs?filter=heads%2Fmain'
statuses:
- id: 203802
state: 'succeeded'
description: 'MyProject-.NET Desktop-CI build succeeded'
context:
name: 'build/MyProject-.NET Desktop-CI'
genre: 'continuous-integration'
creationDate: '2018-07-10T12:45:26.35Z'
createdBy:
displayName: 'Microsoft.VisualStudio.Services.TFS'
url: 'https://vssps.dev.azure.com/fabrikam/_apis/Identities/00000002-0000-8888-8000-000000000000'
_links:
avatar:
href: 'https://dev.azure.com/fabrikam/_apis/GraphProfile/MemberAvatars/s2s.MDAwMDAwMDItMDAwMC04ODg4LTgwMDAtMDAwMDAwMDAwMDAwQDJjODk1OTA4LTA0ZTAtNDk1Mi04OWZkLTU0YjAwNDZkNjI4OA'
id: '00000002-0000-8888-8000-000000000000'
uniqueName: '00000002-0000-8888-8000-000000000000@2c895908-04e0-4952-89fd-54b0046d6288'
imageUrl: 'https://dev.azure.com/fabrikam/_api/_common/identityImage?id=00000002-0000-8888-8000-000000000000'
descriptor: 's2s.MDAwMDAwMDItMDAwMC04ODg4LTgwMDAtMDAwMDAwMDAwMDAwQDJjODk1OTA4LTA0ZTAtNDk1Mi04OWZkLTU0YjAwNDZkNjI4OA'
targetUrl: 'vstfs:///Build/Build/2'

Shape of A Branch Status Fact Check

The shape of a Branch Status Fact Check matches the Shape of a Fact Check.

The following is an example of the Branch Status fact checks:

soundcheck:
checks:
- id: build_succeeded
rule:
factRef: azure:default/branch_status
path: $.statuses[0].state
operator: equal
value: succeeded

Collecting Number of Branches Fact

Number of Branches fact contains a total number of branches in Azure DevOps repository or a number of branches with the last commit more than X days ago.

Shape of A Number of Branches Fact Collector

The shape of a Number of Branches Fact Collector extends the Overall Shape Of Azure DevOps Fact Collector (restriction: type: number_of_branches).

Additional fields:

lastCommitMoreThanXDaysAgo [Optional]

A number of days to filter the branches by. If provided, the number of branches with the last commit older than this number of days will be collected. If not provided, a total number of branches within a given repository will be collected.

The following is an example of the Number of Branches Fact Collector config:

collects:
- factName: number_of_stale_branches
type: number_of_branches
lastCommitMoreThanXDaysAgo: 15
frequency:
cron: '0 * * * *'
filter:
- spec.lifecycle: 'production'
spec.type: 'website'
cache: false

Shape of A Number of Branches Fact

The shape of a Number of Branches Fact is based on the Fact Schema.

The following is an example of the collected Number of Branches Fact:

factRef: azure:default/number_of_stale_branches
entityRef: component:default/queue-proxy
scope: default
timestamp: 2024-04-01T15:50+00Z
data:
count: 7

Shape of A Number of Branches Fact Check

The shape of a Number of Branches Fact Check matches the Shape of a Fact Check.

The following is an example of the Number of Branches fact checks:

soundcheck:
checks:
- id: no_stale_branches
rule:
factRef: azure:default/number_of_stale_branches
path: $.count
operator: equal
value: 0

Collecting Number of Active Pull Requests Fact

Number of Active Pull Requests fact contains a total number of active pull requests for a given target branch in Azure DevOps repository or a number of active pull requests created more than X days ago.

Shape of A Number of Active Pull Requests Fact Collector

The shape of a Number of Active Pull Requests Fact Collector extends the Overall Shape Of Azure DevOps Fact Collector (restriction: type: number_of_active_pull_requests).

Additional fields:

createdMoreThanXDaysAgo [Optional]

A number of days to filter the pull requests by. If provided, the number of active pull requests created more than this number of days ago will be collected. If not provided, a total number of active pull requests for a given target branch will be collected.

The following is an example of the Number of Active Pull Requests Fact Collector config:

collects:
- factName: number_of_stale_pull_requests
type: number_of_active_pull_requests
createdMoreThanXDaysAgo: 15
frequency:
cron: '0 * * * *'
filter:
- spec.lifecycle: 'production'
spec.type: 'website'
cache: false

Shape of A Number of Active Pull Requests Fact

The shape of a Number of Active Pull Requests Fact is based on the Fact Schema.

The following is an example of the collected Number of Active Pull Requests Fact:

factRef: azure:default/number_of_stale_pull_requests
entityRef: component:default/queue-proxy
scope: default
timestamp: 2024-04-01T15:50+00Z
data:
count: 5

Shape of A Number of Active Pull Requests Fact Check

The shape of a Number of Active Pull Requests Fact Check matches the Shape of a Fact Check.

The following is an example of the Number of Active Pull Requests fact checks:

soundcheck:
checks:
- id: no_stale_pull_requests
rule:
factRef: azure:default/number_of_stale_pull_requests
path: $.count
operator: equal
value: 0

Collecting Repository Details Fact

Repository Details fact contains information about Azure DevOps repository.

Shape of A Repository Details Fact Collector

The shape of a Repository Details Fact Collector matches the Overall Shape Of Azure DevOps Fact Collector (restriction: type: repository_details).

The following is an example of the Repository Details Fact Collector config:

collects:
- type: repository_details
frequency:
cron: '0 * * * *'
filter:
- spec.lifecycle: 'production'
cache: true

Shape of A Repository Details Fact

The shape of a Repository Details Fact is based on the Fact Schema.

For a description of the data collected about repository, refer to the Azure DevOps API documentation.

The following is an example of the collected Repository Details Fact:

factRef: azure:default/repository_details
entityRef: component:default/queue-proxy
scope: default
timestamp: 2024-04-01T15:50+00Z
data:
id: '5febef5a-833d-4e14-b9c0-14cb638f91e6'
name: 'AnotherRepository'
url: 'https://dev.azure.com/fabrikam/_apis/git/repositories/5febef5a-833d-4e14-b9c0-14cb638f91e6'
project:
id: '6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c'
name: 'Fabrikam-Fiber-Git'
url: 'https://dev.azure.com/fabrikam/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c'
state: 'wellFormed'
revision: 293012730
defaultBranch: 'refs/heads/master'
remoteUrl: 'https://dev.azure.com/fabrikam/Fabrikam-Fiber-Git/_git/AnotherRepository'
_links:
self:
href: 'https://dev.azure.com/fabrikam/_apis/git/repositories/5febef5a-833d-4e14-b9c0-14cb638f91e6'
project:
href: 'vstfs:///Classification/TeamProject/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c'
web:
href: 'https://dev.azure.com/fabrikam/Fabrikam-Fiber-Git/_git/AnotherRepository'
commits:
href: 'https://dev.azure.com/fabrikam/_apis/git/repositories/5febef5a-833d-4e14-b9c0-14cb638f91e6/commits'
refs:
href: 'https://dev.azure.com/fabrikam/_apis/git/repositories/5febef5a-833d-4e14-b9c0-14cb638f91e6/refs'
pullRequests:
href: 'https://dev.azure.com/fabrikam/_apis/git/repositories/5febef5a-833d-4e14-b9c0-14cb638f91e6/pullRequests'
items:
href: 'https://dev.azure.com/fabrikam/_apis/git/repositories/5febef5a-833d-4e14-b9c0-14cb638f91e6/items'
pushes:
href: 'https://dev.azure.com/fabrikam/_apis/git/repositories/5febef5a-833d-4e14-b9c0-14cb638f91e6/pushes'
isDisabled: false
isFork: false
isInMaintenance: false

Shape of A Repository Details Fact Check

The shape of a Repository Details Fact Check matches the Shape of a Fact Check.

The following is an example of the Repository Details fact checks:

soundcheck:
checks:
- id: default_branch_is_master
rule:
factRef: azure:default/repository_details
path: $.defaultBranch
operator: equal
value: refs/heads/master

Collecting Number of Work Items Fact

Number of Work Items fact contains a total number of work items for a given project in Azure DevOps or a number of work items filtered by WIQL queries.

Shape of A Number of Work Items Fact Collector

The shape of a Number of Work Items Fact Collector extends the Overall Shape Of Azure DevOps Fact Collector (restriction: type: number_of_work_items).

Additional fields:

data [Optional]

Defines the data to collect for this fact. This is an array consisting of two pairs of name and query:

  • name: An identifier for the data element.
  • query: The WIQL syntax query to filter work items by.

If data is not provided, the total number of work items will be collected as count: <X>.

The following is an example of the Number of Work Items Fact Collector config:

collects:
- factName: number_of_tasks
type: number_of_work_items
data:
- name: 'all_tasks',
query: `Select [System.Id] From WorkItems Where [System.WorkItemType] = 'Task'`,
- name: 'open_tasks',
query: `Select [System.Id] From WorkItems Where [System.WorkItemType] = 'Task' AND [State] <> 'Closed' AND [State] <> 'Removed'`,
frequency:
cron: '0 * * * *'
filter:
- spec.lifecycle: 'production'
spec.type: 'website'
cache: false

Shape of A Number of Work Items Fact

The shape of a Number of Work Items Fact is based on the Fact Schema.

The following is an example of the collected Number of Work Items Fact:

factRef: azure:default/number_of_tasks
entityRef: component:default/queue-proxy
scope: default
timestamp: 2024-04-01T15:50+00Z
data:
all_tasks: 156
open_tasks: 20

Shape of A Number of Work Items Fact Check

The shape of a Number of Work Items Fact Check matches the Shape of a Fact Check.

The following is an example of the Number of Work Items fact checks:

soundcheck:
checks:
- id: has_less_than_ten_open_tasks
rule:
factRef: azure:default/number_of_tasks
path: $.open_tasks
operator: lessThan
value: 10