Search code examples
c#restpowershelltfs-2015build-agent

TFS 2015 API remove agent from pool with PowerShell


I'm working on removing an agent from a pool temporarily, install new software on the buildserver the agent is on, test that it works, and then add the agent to the pool again.

I would like to do that programmatically, either with PowerShell or if that isn't a possibility, then do it with C#.

The problem is that I can't find any documentation that can assist me on doing this, either through the TFS REST API or through the tools that come with Visual Studio.

So I'm specifically asking:

How do I remove a named agent from a build pool and how do I add a named agent back into the build pool?

I basically want the same functionality of going onto the web administration of TFS and unchecking/checking an agent in the pool.

When I try to enable/disable an agent with the information provided by starain-msft, I get the following error:

Invoke-RestMethod :
404 - File or directory not found.
Server Error

Later I removed much of the error, as I found out that issue lay in my company's proxy. Read here: Azure DevOps Services REST API Reference

But I got it to work with the help of starain-msft.

The final solution looks like this:

Function TFSwebRequest {
    param
    (
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string] $Uri,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory = $true)]
        [string] $Method,

        [ValidateNotNullOrEmpty()]
        [string] $ContentType,

        [ValidateNotNullOrEmpty()]
        [string] $ContentBody,

        [ValidateNotNullOrEmpty()]
        [System.Net.WebHeaderCollection] $Headers
    )

    # Creating Webrequest from 'Uri'
    $webRequest = [System.Net.HttpWebRequest]::CreateHttp($Uri)

    $webRequest.UseDefaultCredentials = $true
    $webRequest.Method = $Method
    if ($Headers.Count -ne 0) {
        $webRequest.Headers = $Headers
    }
    if (![string]::IsNullOrEmpty($ContentType)) {
        $webRequest.ContentType = $ContentType
    }
    if (![string]::IsNullOrEmpty($ContentBody)) {
        $Body = [byte[]][char[]]$ContentBody
        $Stream = $webRequest.GetRequestStream();
        $Stream.Write($Body, 0, $Body.Length);
    }

    # Get webresponse to a variable
    try {
        [System.Net.WebResponse]$webResponse = $webRequest.GetResponse()
    }
    catch {
        $ErrorMessage = $_.Exception.Message
        Write-Host "TFSwebRequest Failed = " $ErrorMessage -ForegroundColor Red
    }

    # Stream webresponse to a string
    $webResponseStream = $webResponse.GetResponseStream()
    $streamReader = New-Object System.IO.StreamReader $webResponseStream
    $result = $streamReader.ReadToEnd() | ConvertFrom-Json

    return ,$result
}

$agentUri = "http://teamfoundation:8080/tfs/Main/_apis/distributedtask/pools/$($poolID)/agents/$($agentID)?api-version=2.3-preview.1"
$contentBody = @"
{
    "maxParallelism": 1,
    "id": INSERTID,
    "enabled": true #Or false
}
"@

$headers = New-Object System.Net.WebHeaderCollection
$headers.Add("X-HTTP-Method-Override", "PATCH")

TFSwebRequest -Uri $agentUri -Method "POST" -Headers $headers -ContentType "application/json" -ContentBody $contentBody

Solution

  • REST API of the agent pool and agent:

    Get agent pools (request method: GET):

    http://[TFS URL]/_apis/distributedtask/pools?api-version=2.3-preview.1
    

    Get agents of an agent pool (Request method: GET):

    http://[TFS URL]/_apis/distributedtask/pools/[pool id]/agents?api-version=2.3-preview.1
    

    Disable/enable build agent (Request method: PATCH)

    http://[TFS URL]/_apis/distributedtask/pools/[pool id]/agents/[agent id]?api-version=2.3-preview.1
    

    Body (Content-Type: application/json)

    {
        "enabled": false,
        "id": [agent id],
        "maxParallelism": 1
    }
    

    Delete an agent from an agent pool (request method: DELETE):

    http://[Tfs URL]/_apis/distributedtask/pools/[pool id]/agents/[agent id]?api-version=2.3-preview.1
    

    Simple sample to call REST API (PowerShell):

    Param(
       [string]$vstsAccount = "<VSTS-ACCOUNT-NAME>",
       [string]$projectName = "<PROJECT-NAME>",
       [string]$buildNumber = "<BUILD-NUMBER>",
       [string]$keepForever = "true",
       [string]$user = "",
       [string]$token = "<PERSONAL-ACCESS-TOKEN>"
    )
    
    # Base64-encodes the Personal Access Token (PAT) appropriately
    $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token)))
    
    $uri = "https://$($vstsAccount).visualstudio.com/DefaultCollection/$($projectName)/_apis/build/builds?api-version=2.0&buildNumber=$($buildNumber)"
    $result = Invoke-RestMethod -Uri $uri -Method Get -ContentType "application/json" -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)}
    

    For details: Calling VSTS APIs with PowerShell

    C# code to call REST API:

    String MyURI = "REST API URL";
    WebRequest WReq = WebRequest.Create(MyURI);
    WReq.Credentials =
        new NetworkCredential("[user name]", "[password]", "[domain]");
    
    WebResponse response = WReq.GetResponse();
    Console.WriteLine(((HttpWebResponse)response).StatusDescription);
    Stream dataStream = response.GetResponseStream();
    
    StreamReader reader = new StreamReader(dataStream);
    
    string responseFromServer = reader.ReadToEnd();
    
    Console.WriteLine(responseFromServer);
    

    On the other hand, you need to restart the build agent after you install new software to the agent machine in order to recognize them.