5

I'm trying to find all the builds and releases that are configured to use a specific agent pool, using the .NET Client Libraries.

Assuming agentPoolId, I can get all the build definitions like this:

// _connection is of type VssConnection
using (var buildClient = _connection.GetClient<BuildHttpClient>())
{
    List<BuildDefinitionReference> allBuilds = await buildClient.GetDefinitionsAsync(projectName, top: 1000, queryOrder: DefinitionQueryOrder.DefinitionNameAscending);
    List<BuildDefinitionReference> builds = allBuilds.Where(x => HasAgentPoolId(x, agentPoolId)).ToList();
}

private bool HasAgentPoolId(BuildDefinitionReference buildDefinition, int agentPoolId)
{
    TaskAgentPoolReference pool = buildDefinition?.Queue?.Pool;

    if (pool == null)
    {
        return false;
    }

    return pool.Id.Equals(agentPoolId);
}

But I couldn't find a way to find the release definitions that have one or more environments configured to use a particular agent. Any suggestion?

riQQ
  • 9,878
  • 7
  • 49
  • 66
Rui Jarimba
  • 11,166
  • 11
  • 56
  • 86

2 Answers2

2

I was manged to get all releases by Agent Pool ID via Rest Api and not via NET Client Libraries.Hope that helps.

C# Code snippet:

public class ReleaseResponse
{
     [JsonProperty("value")]
     public List<ReleaseItem> Value { get; set; }
}

public class ReleaseItem
{
     [JsonProperty("name")]
     public string Name { get; set; }

     [JsonProperty("Id")]
     public int Id { get; set; }
}

static void Main(string[] args)
{
     string tfsURL = "TFS URL";
     string releaseDefurl = $"{tfsURL}/_apis/release/definitions?$expand=artifacts&api-version=3.2-preview.3";
     const int agentPoolID = "AGENT Pool ID";
     List<string> relevantReleases = new List<string>(); 
     WebClient client = new WebClient();
     client.UseDefaultCredentials = true;
     client.Headers.Add("Content-Type", "application/json");
     var releaseList = client.DownloadString(releaseDefurl);
     var allReleases = JsonConvert.DeserializeObject<ReleaseResponse>(releaseList).Value;
     foreach (var release in allReleases)
     {
           string releaseInfoApi = $"{tfsURL}/_apis/Release/definitions/{release.Id}";
           var getReleseInfo = client.DownloadString(releaseInfoApi);
           var releaseInfo = JsonConvert.DeserializeObject<TFSLogic.RootObject>(getReleseInfo);
           var deploymentAgents = releaseInfo.environments.ToList().Where(e => e.deployPhases.FirstOrDefault().deploymentInput.queueId == agentPoolID).Count();
           if (deploymentAgents > 0)
           {
               relevantReleases.Add(release.Name);
           }

     }

}

Find TFSLogic here : https://codebeautify.org/online-json-editor/cb7aa0d9

Powershell Code snippet:

$tfsUrl = "TFS URL"
$releaseDefurl = $tfsUrl + '/_apis/release/definitions?$expand=artifacts&api-version=3.2-preview.3'
$agentPoolID = "Agent Pool ID"
$relevantReleases = @();
$allReleasesID = (Invoke-RestMethod -Uri ($releaseDefurl) -Method Get -UseDefaultCredentials).value.id

function getReleaseByAgentPoolID($releaseID,$agentPoolID)
{
    $ReleaseInfo = Invoke-RestMethod -Uri "$tfsUrl/_apis/Release/definitions/$releaseID" -Method Get -UseDefaultCredentials

    $deploymentAgents = $ReleaseInfo.environments | % {$_.deployPhases.deploymentInput.queueId} | where {$_ -eq $agentPoolID}

    if($deploymentAgents.Count -gt 0)
    {
        return $ReleaseInfo.name
    }
}


foreach ($releaseID in $allReleasesID)
{
    $relevantReleases += getReleaseByAgentPoolID -releaseID $releaseID -agentPoolID $agentPoolID   
}

UPDATE :

It took me some time,But i was able to achieve that with azure-devops-dotnet-samples I hope this example is finally what you are looking for.

using Microsoft.VisualStudio.Services.WebApi;
using System;
using System.Linq;
using Microsoft.VisualStudio.Services.ReleaseManagement.WebApi.Clients;
using Microsoft.VisualStudio.Services.ReleaseManagement.WebApi.Contracts;
using Microsoft.VisualStudio.Services.Common;
using System.Collections.Generic;

namespace FindReleaseByAgentPoolID
{
    class Program
    {
        const int agentPoolID = 999;
        static void Main(string[] args)
        {
            var relevantReleases = new List<string>();
            VssCredentials c = new VssCredentials(new WindowsCredential(System.Net.CredentialCache.DefaultNetworkCredentials));
            var tfsURL = new Uri("TFS URL");
            var teamProjectName = "PROJECT";

            using (var connection = new VssConnection(tfsURL, c))
            using (var rmClient = connection.GetClient<ReleaseHttpClient2>())
            {
                var releases = rmClient
                    .GetReleaseDefinitionsAsync(teamProjectName, string.Empty, ReleaseDefinitionExpands.Environments)
                    .Result.ToArray();

                foreach (var release in releases)
                {
                    var r = rmClient.GetReleaseDefinitionAsync(teamProjectName, release.Id);
                    var deploymentAgents = r.Result.Environments.SelectMany(e =>
                     e.DeployPhases.Select(dp =>
                     dp.GetDeploymentInput()).Cast<DeploymentInput>()).Where(di =>
                     di.QueueId == agentPoolID).Count();

                    if (deploymentAgents > 0)
                    {
                        relevantReleases.Add(release.Name);
                    }
                }
            }
        }
    }
}
Amit Baranes
  • 7,398
  • 2
  • 31
  • 53
  • Thanks @amit-baranes but I'm actually trying to avoid the REST API, I'd rather use the .NET client libraries so I don't have to handle the serialization/deserialization. BTW I want to get the release definitions, not the releases. – Rui Jarimba Apr 06 '19 at 17:28
  • Many thanks for your help @amit-baranes. I've done some changes to your code sample, see below. – Rui Jarimba Apr 08 '19 at 08:04
  • 1
    I'm glad i could help you solve your question. Nice to learn new things every single day ! – Amit Baranes Apr 08 '19 at 08:29
2

Found a solution, many thanks to @amit-baranes for pointing me in the right direction.

I've changed his code sample to use the await keyword instead of using .Result, and use .OfType<DeploymentInput>() instead of .Cast<DeploymentInput>() (it was throwing some exceptions).

Oh, and the most important thing I've learned: agent pool ID and queue ID are different things!!! If you intend to use the agent pool ID to get the release definitions you'll need to get the correspondent agent queue.

Code sample:

// set agent pool Id and project name
int agentPoolId = 123456; 
string teamProjectName = ".....";

// _connection is of type VssConnection
using (var taskAgentClient = _connection.GetClient<TaskAgentHttpClient>())
using (var releaseClient = _connection.GetClient<ReleaseHttpClient2>())
{
    // please note: agent pool Id != queue Id
    // agent pool id is used to get the build definitions
    // queue Id is used to get the release definitions
    TaskAgentPool agentPool = await taskAgentClient.GetAgentPoolAsync(agentPoolId);
    List<TaskAgentQueue> queues = await taskAgentClient.GetAgentQueuesByNamesAsync(teamProjectName, queueNames: new[] { agentPool.Name });
    TaskAgentQueue queue = queues.FirstOrDefault();

    List<ReleaseDefinition> definitions = await releaseClient.GetReleaseDefinitionsAsync(teamProjectName, string.Empty, ReleaseDefinitionExpands.Environments);

    foreach (ReleaseDefinition definition in definitions)
    {
        var fullDefinition = await releaseClient.GetReleaseDefinitionAsync(teamProjectName, definition.Id);

        bool hasReleasesWithPool = fullDefinition.Environments.SelectMany(GetDeploymentInputs)
                                                              .Any(di => di.QueueId == queue.Id);

        if (hasReleasesWithPool)
        {
            Debug.WriteLine($"{definition.Name}");
        }
    }
}

private IEnumerable<DeploymentInput> GetDeploymentInputs(ReleaseDefinitionEnvironment environment)
{
    return environment.DeployPhases.Select(dp => dp.GetDeploymentInput())
                                   .OfType<DeploymentInput>();
}
Rui Jarimba
  • 11,166
  • 11
  • 56
  • 86