Monitoring TFS 2015 availability from F5 LTM

Introduction

If redundancy and performance are the thing you are looking for your TFS application tier setup, for sure you stumbled upon the term Network Load Balancing (NLB). Microsoft describes the benefits of such a setup and prerequisites in the document named How to: Create a Team Foundation server farm (high availability), thus I will not go in the details about these topics if you continue reading. However, in the documentation, Microsoft encourages you to setup the NLB feature that is integrated in the Windows Server operating system. In many situations that is not an option due to the network restrictions or company policies and the only choice is to use preexisting networking appliances. Reasons for using a hardware based NLB can also be a performance as it offloads the AppTier machines from this task that, for how minor it can be on today’s machines, it adds some load.

Monitoring

In case of using the Windows NLB feature, nodes participating in the pool of the machines used for the load distribution are monitored directly by the system itself, meanwhile for the hardware based solutions we need to setup a health monitor. This is essential as the load balancer needs to know if the node is available and in healthy state, otherwise it is excluded from the pool and the traffic is not sent towards that node.

Now, what is the best practice when it comes to the health status of TFS? Googling around you can’t find much, there are some pointers towards a SOAP method called GetServerStatus exposed, however it doesn’t bring the necessary information.
Luckily there is a non documented rest resource that is exposed on TFS 2015 and beyond and you can reach it at the URL

http(s)://your.tfs.address:port/tfs/_apis/health

It will return just a simple current time stamp by default using the JSON notation. Accessing this resource still requires the user to be authenticated.

When it comes to the F5 in particular, you need to create HTTPS Health Monitor (Local Traffic > Monitors > Create…)

f5-health-monitor

The most important fields to set are Send and Receive string. Here we will send a request towards TFS at the above mentioned address and expect a status code 200 in the response. We can ignore the time stamp in the response body.
The send string will be:

GET /tfs/_apis/health HTTP1.1\r\nHost: your.tfs.address:port\r\n

meanwhile the receive string should be set to:

HTTP/1.1 200 OK

A simple check that the request succeeded (we are not interested in the timestamp in this case).

Do not also forget to provide a username and password of the account that has sufficient rights to access this resource on your TFS server. Username needs to be provided in the form of DOMAIN\UserName. A bare minimum of access rights are necessary for accessing this resource and a View instance-level information permission on the server level is more than sufficient. You can set server-level permissions from the Team Foundation Administration Console or using the TFSSecurity command line tool. Now assign the newly created health monitor to your NLB pool and you are ready to go.

In case you are trying to do so from a script for some of your custom dashboards, I wrote a CmdLet that will return true or false based on the response received from the call to the above mentioned REST resource.

function Get-ServerStatus()
{
	[CmdletBinding()]
    [OutputType([bool])]
	param
	(
		[parameter(Mandatory = $true)]
		[Uri]$url,
        [System.Management.Automation.PSCredential]$credential
	)
	BEGIN
    {
        if ($url.AbsoluteUri)
        {
            $url = $url.AbsoluteUri.TrimEnd('/')
        }

        Add-Type -AssemblyName System.Net.Http
	}
	PROCESS
	{
		$httpClientHandler = New-Object System.Net.Http.HttpClientHandler
 
        if ($Credential)
        {
		    $networkCredential = New-Object System.Net.NetworkCredential @($Credential.UserName, $Credential.Password)
		    $httpClientHandler.Credentials = $networkCredential
        }
        else
        {
            $httpClientHandler.UseDefaultCredentials = $true
        }

        $httpClient = New-Object System.Net.Http.Httpclient $httpClientHandler

        try
        {
			$response = $httpClient.GetAsync("$url/_apis/health").Result
 
			if ($response.IsSuccessStatusCode)
			{
				return $true
			}
 
			return $false
        }
        catch [Exception]
        {
			$PSCmdlet.ThrowTerminatingError($_)
        }
        finally
        {
            if($null -ne $httpClient)
            {
                $httpClient.Dispose()
            }
 
            if($null -ne $response)
            {
                $response.Dispose()
            }
        }

		return $false
	}
	END { }
}

It is sufficient to invoke this cmdlet by passing in the URL of your TFS instance and eventually the credentials. If no credentials are provided, current process credentials will be used.

$uri = "http(s)://your.tfs.address:port/tfs"
$credential = Get-Credential

$state = Get-ServerStatus $uri $credential

A simple solution is now in place that will keep other tools informed about the availability of our TFS instance.

Good luck!