VMware Horizon – Powershell scripting (Connection servers)



In the previous post “ViewAPI” we installed the necessary modules and learned how to (dis-)connect to our Horizon Connection server. In this part, we’ll get information from that ViewAPI about the connection servers in our environment.

Connection server information

Let’s start by getting some information about our Connection servers. Therefore we use the property ConnectionServerHealth. We’ll retrieve an array containing all our Connection servers with their basic information like name, status, version, connections, …

$HVServer = "connectionserver.horizon.cloud"
Import-Module VMware.VimAutomation.Core
Import-Module VMware.VimAutomation.HorizonView

$conn = Connect-HVServer -Server $HVServer  
$viewAPI = $conn.ExtensionData.ConnectionServerHealth.ConnectionserverHealth_List()

foreach($cs in $viewAPI)
{
    Write-host "Name: $($cs.Name)"
    write-host "Status: $($cs.Status)"
    write-host "Version: $($cs.Version)"
    Write-host ""
}

# disconnect from our connection server
$con | Disconnect-HVServer -Force -Confirm:$false

So, for each of our connection servers in our Horizon environment we get their name, status and version. In my environment you can see I have 2 connection servers, running Horizon 7.9.0:

Aside from this basic information, there’s another interesting property that we can look at: connectionData:


So we can extend our script to get the connection information too:

foreach($cs in $viewAPI)
    {
        Write-host "Name: $($cs.Name)"
        write-host "Status: $($cs.Status)"
        write-host "Version: $($cs.Version)"
        write-host "Current Connections: $($cs.ConnectionData.NumConnections)"
        write-host "Max. Connections: $($cs.ConnectionData.NumConnectionsHigh)"
        write-host "Current Composer Connections: $($cs.ConnectionData.NumViewComposerConnections)"
        write-host "Max. Composer Connections: $($cs.ConnectionData.NumViewComposerConnectionsHigh)"
        write-host "Tunneled Connections: $($cs.ConnectionData.NumTunneledSessions)"
        write-host "PCoIP Secure Gateway Connections: $($cs.ConnectionData.NumPSGSessions)"
        Write-host ""
    }

Which gives us the following output:

So, to get all that information into our PRTG monitoring solution I came up with the following script:

#
# Retrieve status of VMware Horizon Connection Servers
#

# set some parameters with default values
param (
    [string]$HVServer="connectionserver.horizon.cloud",
    [string]$HVUser,
    [string]$HVPass,
    [string]$HVFilter="*"
)

# if no $HVServer specified, don't do anything
if ($HVServer -ne "")
{
    #import necessary modules
    Import-Module VMware.VimAutomation.Core
    Import-Module VMware.VimAutomation.HorizonView
    
    # if no user/pass specified via parameters, prompt for user/pass, else use the parameters
    if ($HVUser -eq "" -or $HVPass -eq "") { $con = Connect-HVServer -Server $HVServer  }
    else { $con = Connect-HVServer -Server $HVServer -User $HVUser -Password $HVPass }
    
    # connect to the Horizon View API
    $viewAPI = $con.ExtensionData.ConnectionServerHealth.ConnectionserverHealth_List()
    
    # Start XML output for PRTG
    write-host "<prtg>"
    # loop through all connection servers
    foreach($cs in $viewAPI)
    {
        # check if Name matches filter specified
        if ($cs.Name -like $HVFilter)
        {
            # Get Connection Server status
            write-host "  <result>"
            write-host "    <channel>$($cs.Name) STATUS</channel>"
            if ($cs.Status -eq "OK") {write-host "    <value>1</value>"} else { write-host "    <value>2</value>"}
            write-host "    <unit>Custom</unit>"
            write-host "    <ValueLookup>prtg.standardlookups.yesno.stateyesok</ValueLookup>"
            write-host "  </result>"

            # Get Connection Server current connections
            write-host "  <result>"
            write-host "    <channel>$($cs.Name) # Connections</channel>"
            write-host "    <value>$($cs.ConnectionData.NumConnections)</value>"
            write-host "    <unit>Count</unit>"
            write-host "  </result>"

            # Get Connection Server current Composer connections
            write-host "  <result>"
            write-host "    <channel>$($cs.Name) # Composer Connections</channel>"
            write-host "    <value>$($cs.ConnectionData.NumViewComposerConnections)</value>"
            write-host "    <unit>Count</unit>"
            write-host "  </result>"

            # Get Connection Server tunneled connections
            write-host "  <result>"
            write-host "    <channel>$($cs.Name) # Tunneled Connections</channel>"
            write-host "    <value>$($cs.ConnectionData.NumTunneledSessions)</value>"
            write-host "    <unit>Count</unit>"
            write-host "  </result>"

            # Get Connection Server PCoIP Secure Gateway connections
            write-host "  <result>"
            write-host "    <channel>$($cs.Name) # PSG Connections</channel>"
            write-host "    <value>$($cs.ConnectionData.NumPSGSessions)</value>"
            write-host "    <unit>Count</unit>"
            write-host "  </result>"
        }
    }
    # close XML for PRTG output
    write-host "</prtg>"
    
    # disconnect from our connection server
    $con | Disconnect-HVServer -Force -Confirm:$false
}

I won’t be explaining the correct XML structure PRTG requires here. You can find all that information on the following page: PRTG Manual: Custom Sensors.

First thing I do is set some parameters with some default values. This way, I’m able to run the script from PRTG, but I can also run the same script interactively (with or without parameters) to test if the output is correct. Also in case of any errors, this is very usefull to do troubleshooting, because PRTG will only say: “XML: Junk after document element </prtg> — JSON: The returned JSON does not match the expected structure (Invalid JSON.). (code: PE231)“. But if you run the same script interactively you’ll see any error messages that might happen during the process.

Next I check if the parameter $HVServer is not empty. Normally it can’t be, because we specify a default value for that parameter, but it’s just to prevent trying to connect to nothing.

Then I start connecting to our Horizon Connection server. If no user/pass have been supplied via the parameters, it connects without credentials. so that a authentication prompt will appear to login to the Connection Server (remember I can run the script interactively?). If they are supplied, then I use them to connect to the Horizon Connection server and no interactive prompts are requested.

Once our connection is made, I loop through all the connection servers returned by the ViewAPI. One of the parameters was a filter parameter. It defaults to “*”, but you could also put a name or part of the name in here to filter only specific servers to be checked (e.g. you have 2 connection servers for internal users (int-conn001 & int-conn002) and one for external users (ext-conn001) and you only want to see information about the external server, so you could set $HVFilter to “ext-conn*“).

The next part is just outputing our values in the correct XML structure. For the status channel, I’m using a ValueLookup to set the status to 1 if the $cs.Status equals “OK” and 2 if it equals anything else (this can be either “NOT_RESPONDING” or “UNKNOWN”). The ValueLookup type “prtg.standardlookups.yesno.stateyesok” then knows that 1 = Yes and 2 = No and that Yes is a green status.

The other values are just numbers stating the number of different connection types.

Finally we close our XML structure and disconnect from our server.

This gives us the following output:

PRTG

Next I setup a new Custom Sensor in PRTG, using the “EXE/Script Advanced” sensor and specify the following parameters:

The parameters I specify here are the ones I setup in my script. I use the %host variable to get the hostname from the device where the sensor is created on (our primary connection server) and the credentials as specified on the device where the sensor is created.

This gives me a very nice overview of the status of my connection servers:

As you can see, I’m putting all the information of all my connection servers into a single sensor, in separate channels. I could also have used the HVFilter parameter to create 2 separate sensors and set it to “CONN002” for the first sensor and to “CONN003” for the second sensor. This way I would have the information of each connection server into a separate sensor. Especially in larger environments this might be easier to monitor, cause a single channel error puts the whole sensor in error.

This concludes our first PRTG script to monitor our VMware Horizon environment. In the next part we’ll have look at retrieving the number of “Problem vCenter VM’s” from our VMware Horizon environment.



Leave a Reply

Your email address will not be published. Required fields are marked *