Andriy
Andriy

Reputation: 23

How to allow tags in Azure DevOps environment branch control

We would like to limit our prod-level service connections to master branches as a protection so we only deploy code that has been reviewed. For now we setup a master branch control on the production environment in "Approvals and Checks" to be refs/heads/master.

That works, so if a pipeline references any template from another repository, this will fail if the ref property is something else than master.

Now we would like to also allow tags for custom pipeline templates versioning, so the pipeline could reference a template by tag. We can do it like this

 repositories:
    - repository: templates
      name: RepositoryWithTemplates
      type: git
      ref: refs/tags/0.0.1

However, we don't want to allow all the tags, but only the tags of commits, that are already on master.

We try to do it by using "Invoke Azure Function" in "Approvals and Checks" of the environment in order to do our custom branch control inside of an azure function. This function will need to check every repository reference and if a tag present, run a git command to check this tag is pointing to commit on master.

But we didn't find yet a way to get all the resources/repositories of the pipeline to pass as arguments to the function, or to query them from Azure DevOps API.

Does anybody know if it is possible to obtain from environment variables or from the API all the resources and repositories used in the pipeline? Somehow the standard "branch control" check does it for all the repositories, but is it even exposed for the custom control checks?

Upvotes: 1

Views: 1759

Answers (2)

Andriy
Andriy

Reputation: 23

Thank you, Jane, this solution finally worked for us! We didn't need to create any access token, we just use the one from system.AuthToken from azure devops. In case somebody needs the whole function to check the tags are on master here is the code:

# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)

function Output-Result([HttpStatusCode] $status, $text) {
    Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
        StatusCode = $status
        Body = $text
    })
}

function Call-Uri($uri, $authHeader) {
    try {
        $result = Invoke-RestMethod -Uri $uri -Method get -Headers $authHeader
    }
    catch {
        Output-Result -status ([HttpStatusCode]::InternalServerError) -text "Failed to call $($uri)"
        return $null    
    }
    return $result
}

# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."

$organization = "ate-test-83"

$token = $Request.headers.AuthToken

$buildId = $Request.Body.BuildId
$projectName = $Request.Body.ProjectName

$authHeader = @{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($token)")) }
$baseUri = "https://dev.azure.com/$($organization)/$($projectName)/"

$result = Call-Uri -uri ($baseUri + "_build/results?buildId=$($buildId)&__rt=fps&__ver=2") -authHeader $authHeader
if ($result -eq $null) {
    return
} 

$resources = $result.fps.dataProviders.data."ms.vss-build-web.run-details-data-provider".repositoryResources;
if ($resources.count -eq 0) {
    Output-Result -status ([HttpStatusCode]::BadRequest) -text "No repository resources found. Check function parameters and headers are correct"
    return
}

foreach ($resource in $resources) {
    if ($resource.refName -eq "refs/heads/master") {
        continue
    }
    elseif ($resource.refName -match "^refs/tags/") {
        $result = Call-Uri -uri ($baseUri + "_apis/git/repositories/$($resource.id)/commits?searchCriteria.itemVersion.version=master&searchCriteria.toCommitId=$($resource.version)&searchCriteria.fromCommitId=$($resource.version)&api-version=6.1-preview.1") -authHeader $authHeader
        if ($result -eq $null) {
            return
        } 

        if ($result.count -ne 1) {
            Output-Result -status ([HttpStatusCode]::BadRequest) -text "The ref $($resource.refName) to the repository $($resource.name) is not on master branch"
            return
        }
    }
    else {
        Output-Result -status ([HttpStatusCode]::BadRequest) -text "The ref $($resource.refName) to the repository $($resource.name) is not allowed. Only master or tags from master are allowed}"
        return
    }
}

Output-Result -status ([HttpStatusCode]::Ok) -text "Ok"

Upvotes: 1

Jane Ma-MSFT
Jane Ma-MSFT

Reputation: 5222

How to obtain all the repositories used in the pipeine from REST API:

You can use the following REST API:

GET https://dev.azure.com/{Organization}/{Project}/_build/results?buildId={Build ID}&__rt=fps&__ver=2

Its response body is quite massive, the information about multiple repositories is in:

fps -> dataProviders -> data -> ms.vss-build-web.run-details-data-provider -> repositoryResources

It shows all source repositories including their names, ids, versions, and so on.

This REST API is not documented and I get it from Developer Console.

Upvotes: 2

Related Questions