VMware Horizon – Powershell scripting (Desktop Pools)



Today we’ll be monitoring our desktop pools. In our PRTG monitoring, we’d like to see the status of our desktop pools, the status of the provisioning and of course if there are enough desktops available.

Desktop Pools

A desktop pool can be enabled or disabled, so we’d like to see that in our PRTG monitoring system. Also provisioning can be enabled or disabled foor a VDI desktop pool, but not for a Remote Desktop Session Host pool, so we’ll have to catch this. To get the usage data of the desktop pool, we’ll retrieve the number of machines that are configured on the desktop pool and also the number of the current sessions (active or disconnected) of the desktop pool.

We will use the QueryService again, as we did in the previous post for the Problem VM’s. This time we’ll query the entity “DesktopSummaryView”. This will give us all the desktop pools and their data.

 ...
    $query_Service = New-Object "Vmware.Hv.QueryServiceService"
    $query = New-Object "Vmware.Hv.QueryDefinition"
    $query.QueryEntityType = 'DesktopSummaryView'
    $Pools = $query_Service.QueryService_Query($viewAPI,$query)

    foreach($dp in $Pools.Results)
    {
        if ($dp.DesktopSummaryData.Name -eq $HVPoolID)
        {
            if ($dp.DesktopSummaryData.Type -ne "RDS")
            # no usage calculation for RDS types
            {
                $usage = $dp.DesktopSummaryData.NumSessions / $dp.DesktopSummaryData.NumMachines * 100
            }
                        
            # Current sessions (active & disconnected)
            $dp.DesktopSummaryData.NumSessions

            # total number of VDI machines (for RDSH this is the total number of RDSH Servers)
            $dp.DesktopSummaryData.NumMachines

            # Status of the desktop pool
            $dp.DesktopSummaryData.Enabled

            if ($dp.DesktopSummaryData.Type -ne "RDS")
            # Provisioning is disabled for RDS type
            {
                $dp.DesktopSummaryData.ProvisioningEnabled
            }

            # Only RDSH pools
            if ($dp.DesktopSummaryData.Type -eq "RDS")
            {
                # match RDS Id with RDSServerSummaryView to get status of RDS servers in farm
                # used to detect if RDS server availability status
                $query_Service2 = New-Object "Vmware.Hv.QueryServiceService"
                $query2 = New-Object "Vmware.Hv.QueryDefinition"
                $query2.QueryEntityType = 'RDSServerSummaryView'
                $filter2 = new-object VMware.Hv.QueryFilterEquals -Property @{'memberName' = 'base.desktop'; 'value' = $dp.Id}
                $query2.Filter=$filter2
                
                $Farms = $query_Service2.QueryService_Query($viewAPI,$query2)
                
                # only retrieve first RDSH server from the result
                $Farms.Results[0].RuntimeData.Status
            }
        }
    }
...

Because we want to have a separate sensor in PRTG for each of our desktop pools, we’ll use an extra filter to call the script so we only return data for one specific desktop pool. Some of the data is not applicable to RDSH pools, like provisioning and total number of machines, so we’ll have to check this too.

For each desktop pool we’ll retrieve the data from the DesktopSummaryData property. In the DesktopSummaryData.Type we’ll see if it’s and RDSH server (value: “RDS”) or not (value: “AUTOMATED” or “MANUAL”).
In the DesktopSummaryData.numMachines & .numSessions we’ll find the total number of VDI machines and the number of active or disconnected sessions. We’ll use this to calculage the usage percentage.

As we don’t have provisioning status on the RDSH desktop pools, we’ll need to create a second QueryServiceService to retrieve the RDSServerSummaryView. In this object we can find the status of the RDS Server. I’m only checking the status for the value “AVAILABLE”. Any other status will show as “not available” in PRTG. Check the VMware {code} site for the other statuses.

If we wrap this all up and format the result in a correct PRTG XML format this becomes the complete script:

param (
    [string]$HVServer="connectionserver.horizon.test",
    [string]$HVUser,
    [string]$HVPass,
    [string]$HVPoolID = ""
)

if ($HVPoolID -ne "")
{
    Import-Module VMware.VimAutomation.Core
    Import-Module VMware.VimAutomation.HorizonView
    if ($HVUser -eq "" -or $HVPass -eq "") { $con = Connect-HVServer -Server $HVServer  }
    else { $con = Connect-HVServer -Server $HVServer -User $HVUser -Password $HVPass }
    $viewAPI = $con.ExtensionData
    $query_Service = New-Object "Vmware.Hv.QueryServiceService"
    $query = New-Object "Vmware.Hv.QueryDefinition"
    $query.QueryEntityType = 'DesktopSummaryView'
    $Pools = $query_Service.QueryService_Query($viewAPI,$query)
    write-host "<prtg>"
    foreach($dp in $Pools.Results)
    {
        if ($dp.DesktopSummaryData.Name -eq $HVPoolID)
        {
            if ($dp.DesktopSummaryData.Type -ne "RDS")
            # no usage calculation for RDS types
            {
                $usage = $dp.DesktopSummaryData.NumSessions / $dp.DesktopSummaryData.NumMachines * 100
                write-host "  <result>"
                write-host "    <channel>Usage</channel>"
                write-host "    <value>$usage</value>"
                write-host "    <unit>percent</unit>"
                write-host "    <float>1</float>"
                write-host "    <DecimalMode>Auto</DecimalMode>"
                write-host "    <limitMode>1</limitMode>"
                write-host "    <LimitMaxWarning>85</LimitMaxWarning>"
                write-host "    <LimitMaxError>95</LimitMaxError>"
                write-host "  </result>"
            }
                        
            write-host "  <result>"
            write-host "    <channel>$($dp.DesktopSummaryData.Name) Sessions</channel>"
            write-host "    <value>$($dp.DesktopSummaryData.NumSessions)</value>"
            write-host "    <unit>count</unit>"
            write-host "  </result>"

            write-host "  <result>"
            write-host "    <channel>$($dp.DesktopSummaryData.Name) Machines</channel>"
            write-host "    <value>$($dp.DesktopSummaryData.NumMachines)</value>"
            write-host "    <unit>count</unit>"
            write-host "  </result>"

            write-host "  <result>"
            write-host "    <channel>Desktop Enabled</channel>"
            if ($dp.DesktopSummaryData.Enabled) {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>"

            if ($dp.DesktopSummaryData.Type -ne "RDS")
            # Provisioning is disabled for RDS type
            {
                write-host "  <result>"
                write-host "    <channel>Provisioning Enabled</channel>"
                if ($dp.DesktopSummaryData.ProvisioningEnabled) {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>"
            }

            if ($dp.DesktopSummaryData.Type -eq "RDS")
            {
                # match RDS Id with RDSServerSummaryView to get status of RDS servers in farm
                # used to detect if RDS server agent unreachable status
                $query_Service2 = New-Object "Vmware.Hv.QueryServiceService"
                $query2 = New-Object "Vmware.Hv.QueryDefinition"
                $query2.QueryEntityType = 'RDSServerSummaryView'
                $filter2 = new-object VMware.Hv.QueryFilterEquals -Property @{'memberName' = 'base.desktop'; 'value' = $dp.Id}
                $query2.Filter=$filter2
                
                $Farms = $query_Service2.QueryService_Query($viewAPI,$query2)
                
                write-host "  <result>"
                write-host "    <channel>Status</channel>"
                if ($Farms.Results[0].RuntimeData.Status -eq "AVAILABLE") {write-host "    <value>1</value>"}
                else { write-host "    <value>0</value>"}
                write-host "    <unit>Custom</unit>"
                write-host "    <ValueLookup>prtg.standardlookups.offon.stateonok</ValueLookup>"
                write-host "  </result>"
            }
        }
    }
    write-host "</prtg>"

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

PRTG

When we create a new “EXE/Script advanced” sensor pointing to our script above (see the previous post on how to create the sensor), We’ll get the follwing information:

So we have a channel for the current sessions of a desktop pool, a total number of provisioned machines, a calculated value that gives us the usage percentage (e.g. 50% available desktops in use). We also have status indicators showing us if the desktop pool is enabled and if provisioning is enabled.

Optimization

This was one of my first scripts I wrote to monitor our VMware Horizon environment. While writing this blog post, I see there’s room for improvement in this script. For example in stead of looping through the results of all desktop pools, I could also create a filter on the initial query. Once I have some spare time to optimize this script, I’ll update it here.

This concludes our latest post about monitoring our VMware Horizon environment with PRTG. Stay tuned for more to come!



Leave a Reply

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