XDPing PowerShell Function

by Jeremy Saunders on May 16, 2019

I wanted to write valid PowerShell function to do an XDPing the same way Citrix do with their Health Assistant tool. I was struggling a little to get the PowerShell code working as expected, so in the end I used JetBrains decompiler to decompile the VDAAssistant.Backend.dll, which is a component of the Citrix Health Assistant Tool. This allowed me to reverse engineer and understand how Citrix does it in C# .NET, which is then easy for me to convert to PowerShell. Yeah…okay, so I cheated at little 😉 I also released a C# (csharp) function I wrote back in 2020.

To test if the Broker service is reachable, listening and processing requests on its configured port, you can issue blank HTTP POST requests at the Broker’s Registrar service, which is located at /Citrix/CdsController/IRegistrar. If the first line displayed/returned is “HTTP/1.1 100 Continue“, then the Broker service responded and is deemed to be healthy.

Function XDPing {
  # To work out the best way to write this function I decompiled the VDAAssistant.Backend.dll from the
  # Citrix Health Assistant tool using JetBrains decompiler.
  # To test if the Broker service is reachable, listening and processing requests on its configured port,
  # you can issue blank HTTP POST requests at the Broker's Registrar service, which is located at
  # /Citrix/CdsController/IRegistrar. If the first line displayed/returned is "HTTP/1.1 100 Continue",
  # then the Broker service responded and is deemed to be healthy.

  param(
    [Parameter(Mandatory=$True)][String]$ComputerName, 
    [Parameter(Mandatory=$True)][Int32]$Port,
    [String]$ProxyServer="", 
    [Int32]$ProxyPort
  )

  $URI = "http://$ComputerName/Citrix/CdsController/IRegistrar"
  $InitialDataToSend = "POST $URI HTTP/1.1`r`nContent-Type: application/soap+xml; charset=utf-8`r`nHost: ${ComputerName}:${Port}`r`nContent-Length: 1`r`nExpect: 100-continue`r`nConnection: Close`r`n`r`n"
  $FinalDataToSend = "X"
  $IsServiceListening = $False

  If ($ProxyServer -eq "" -OR $ProxyServer -eq $NULL) {
    $ConnectToHost = $ComputerName
    [int]$ConnectOnPort = $Port
  } Else {
    $ConnectToHost = $ProxyServer
    [int]$ConnectOnPort = $ProxyPort
    #write-verbose "Connecting via a proxy" -verbose
  }

  $Saddrf   = [System.Net.Sockets.AddressFamily]::InterNetwork 
  $Stype    = [System.Net.Sockets.SocketType]::Stream 
  $Ptype    = [System.Net.Sockets.ProtocolType]::TCP
  $socket    = New-Object System.Net.Sockets.Socket $saddrf, $stype, $ptype 

  Try {
    $socket.Connect($ConnectToHost,$ConnectOnPort)
    #$socket.Connected
    $initialbytes = [System.Text.Encoding]::ASCII.GetBytes($InitialDataToSend)
    #write-verbose "Sending initial data..." -verbose
    #$InitialDataToSend
    $NumBytesSent = $socket.Send($initialbytes, $initialbytes.length,[net.sockets.socketflags]::None)
    #write-verbose "Sent $NumBytesSent bytes" -verbose
    $numArray = new-object byte[] 21
    $socket.ReceiveTimeout = 5000
    $NumBytesReceived = $socket.Receive($numArray)
    #write-verbose "Received $NumBytesReceived bytes" -verbose
    $output = [System.Text.Encoding]::ASCII.GetString($numArray)
    $finalBytes = [System.Text.Encoding]::ASCII.GetBytes($FinalDataToSend)
    #write-verbose "Sending final data..." -verbose
    #$FinalDataToSend
    $NumBytesSent = $socket.Send($finalBytes, $finalBytes.length,[net.sockets.socketflags]::None)
    #write-verbose "Sent $NumBytesSent bytes" -verbose
    $socket.Close() | out-null
    #write-verbose "Received data..." -verbose
    #$output
    if ($output -eq "HTTP/1.1 100 Continue") {
      $IsServiceListening = $True
    }
  }
  Catch {
    #
  }
  return $IsServiceListening
}

Then you can call the function from within your script. Just give it the FQDN of a Delivery Controller and the port it’s listening on. You will either receive a return value of True or False from the function.

$ComputerName = "cxdt01.jouseconsulting.com"
$Port = 80

XDPing -ComputerName $ComputerName -Port $Port

XDPing Output

You’ll notice that you can also pass proxy parameters to the function. I added this as I was using Fiddler to test the function and make sure the raw data sent was correctly formatted. Therefore they are not mandatory parameters. I just left them there for test and development.

Enjoy!

Jeremy Saunders

Jeremy Saunders

Technical Architect | DevOps Evangelist | Software Developer | Microsoft, NVIDIA, Citrix and Desktop Virtualisation (VDI) Specialist/Expert | Rapper | Improvisor | Comedian | Property Investor | Kayaking enthusiast at J House Consulting
Jeremy Saunders is the Problem Terminator. He is a highly respected IT Professional with over 35 years’ experience in the industry. Using his exceptional design and problem solving skills with precise methodologies applied at both technical and business levels he is always focused on achieving the best business outcomes. He worked as an independent consultant until September 2017, when he took up a full time role at BHP, one of the largest and most innovative global mining companies. With a diverse skill set, high ethical standards, and attention to detail, coupled with a friendly nature and great sense of humour, Jeremy aligns to industry and vendor best practices, which puts him amongst the leaders of his field. He is intensely passionate about solving technology problems for his organisation, their customers and the tech community, to improve the user experience, reliability and operational support. Views and IP shared on this site belong to Jeremy.
Jeremy Saunders
Jeremy Saunders

Previous post:

Next post: