In my last post we talked about getting started with using powercli to get information from Horizon. We even talked about some of the limitations that exist when using the default objects that Horizon returns as output. I wanted to take a minute and explain how to get the information you want from Horizon using powershell/powercli.

One of the things I noticed when using the powercli cmdlets for Horizon is that there is great way to select only the data you want from an object. Here is a single object from Get-HVMachineSummary

We see that is has most of the information I want, but I want to display something different in the table. We see those properties aren’t available, or at least they aren’t easily available.

I can still get to some of the data in the objects base property

But it is definitely missing some items, not to mention now we can’t get a readable assigned user from this object.

Creating an object using api

Looking through the api provided by VMware I found an object that should contain all of the info I need. It is called the MachineDetailsView.

Unfortunately the way to get at those objects it not through a simple cmdlet. We have to use the query service which is explained here.

Using the query service I created a function

Function Get-HVMachineDetail{

    $queryService = New-Object VMware.Hv.QueryServiceService
    $defn = New-Object VMware.Hv.QueryDefinition
    $defn.queryEntityType = 'MachineDetailsView'
    $defn.sortBy = 'data.name'
    $defn.sortDescending = $true
    $queryResults = $queryService.QueryService_Create($hvServices, $defn)

    # Handle results
    try {
    $vmresults= @()
    while ($queryResults.results -ne $null) {
        foreach ($result in $queryResults.Results) {
            [VMware.Hv.MachineDetailsView]$MachineDetailsView = $result
            $vmresults += $MachineDetailsView
            # Do work.
        }

        # Fetch next page
        if ($queryResults.id -eq $null) {
            break;
        }
        $queryResults = $queryService.QueryService_GetNext($hvServices, $queryResults.id)
    }
    } finally {
    if ($queryResults.id -ne $null) {
        $queryService.QueryService_Delete($hvServices, $queryResults.id)
    }
    }

return $vmresults

}

Most of it is copied from the search query link I posted earlier, but the important thing to note is line 6.

Here I am telling the search query that we want the MachineDetailsView, which I mentioned earlier.

Alright let’s run it and see what we get….

Alright, it isn’t pretty yet, and it does have all the info we want, but it is just hiding it.

Exploring the object

To find the information we need from this object we need to explore it. Let’s start just looking through one object. In case case I stored one of the objects in the variable $tempvm

Because I have looked through these objects already I know the information we want is going to be in the Data and the DesktopData so let’s look at those first.

Looks like basic information about the desktop, and we are able to see the assigned user here, which is one of the things I want.

This is simple information about the desktop pool, but still info we want.

Last but not least let’s look at the property ManagedMachineDetailsData.

This is data about the VM itself. Which host it is on, where is the data stored and more information can be found in this property.

Alright we found where all of the data we want is, so let’s look at how to create a custom object.

Creating a Custom Powershell Object

There are a number of ways to create objects in Powershell, in this particular instance I am going to use the straightforward way of New-Object with Add-Member. Although this way takes a little more setup(I think) than some of the others, it makes it very clear how the object is being created.

So here is a portion of the code and let’s look at it line by line

        $tempobject = New-Object -TypeName psobject
        $tempobject |Add-Member -MemberType NoteProperty -Name VM -value $vm.data.name
        $tempobject |Add-Member -MemberType NoteProperty -Name AssignedUser -value $vm.data.assignedusername
        $tempobject |Add-Member -MemberType NoteProperty -Name DesktopPool -value $vm.desktopdata.name
        $tempobject |Add-Member -MemberType NoteProperty -Name DesktopPool -value $vm.managedmachinedetailsdata.hostname

When done like this it is fairly easy to see how the object is being created. I first create the individual object $tempobject. We then use the Add-Member cmdlet to add properties to the object. With Add-Member we define what we want the object to be named and what we want that value to be. The value comes from the different properties we were exploring earlier.

Although this method does take a bit more typing to setup than some other methods of creating objects, it makes everything easy to understand.

Put it all together

Once we add the object creation to the earlier script we get something that looks like this:

Function Get-HVMachineDetail{

    $queryService = New-Object VMware.Hv.QueryServiceService
    $defn = New-Object VMware.Hv.QueryDefinition
    $defn.queryEntityType = 'MachineDetailsView'
    $defn.sortBy = 'data.name'
    $defn.sortDescending = $true
    $queryResults = $queryService.QueryService_Create($hvServices, $defn)

    # Handle results
    try {
    $vmresults= @()
    while ($queryResults.results -ne $null) {
        foreach ($result in $queryResults.Results) {
            [VMware.Hv.MachineDetailsView]$MachineDetailsView = $result
            $vmresults += $MachineDetailsView
            # Do work.
        }

        # Fetch next page
        if ($queryResults.id -eq $null) {
            break;
        }
        $queryResults = $queryService.QueryService_GetNext($hvServices, $queryResults.id)
    }
    } finally {
    if ($queryResults.id -ne $null) {
        $queryService.QueryService_Delete($hvServices, $queryResults.id)
    }
    }

    #Groups Vm Information 
    $vms = @()
    foreach ($vm in $vmresults)
        {
        $tempobject = New-Object -TypeName psobject
        $tempobject |Add-Member -MemberType NoteProperty -Name VM -value $vm.data.name
        $tempobject |Add-Member -MemberType NoteProperty -Name AssignedUser -value $vm.data.assignedusername
        $tempobject |Add-Member -MemberType NoteProperty -Name DesktopPool -value $vm.desktopdata.name
        $tempobject |Add-Member -MemberType NoteProperty -Name DesktopPool -value $vm.managedmachinedetailsdata.hostname

        $vms += $tempobject
        

        }

return $vms

}

Once we hit line 34 we create an array which will hold our VMs after we are done processing them and creating the new objects.

Then it is just a matter of running our new function(Sorry for all the obfuscation)

The nice thing about this which we couldn’t do before is we can treat these as just normal powershell objects….like filtering.

It is a bit of a longer post, but hopefully this will be helpful in creating your own powershell functions for Horizon!


4 Comments

Anonymous · March 17, 2021 at 5:04 pm

This one don’t work I get the following error.
Exception calling “QueryService_Create” with “2” argument(s)

    Stefan · June 14, 2021 at 6:29 am

    Same problem here. I’m no programming expert, but what about definition of variable “$hvServices”?
    regard, Stefan

Anonymous · June 17, 2021 at 6:33 am

Thanks for your post. It may be worth pointing out that the “QueryService_Create” method needs a relatively recent version of View Connection server. I’ve tested on View 7.5 (gets the exception mentioned by ‘Anonymous’ above) and 7.13 (works fine). I believe significant changes were made to the API from version 7.9 so that was probably when this functionality was added.

Stefan Gloeckle · October 6, 2021 at 8:01 am

There is at least one line missing at the top:

$defn.sortDescending = $true
> $hvServices = $Global:DefaultHVServers.ExtensionData
$queryResults = $queryService.QueryService_Create($hvServices, $defn)

and in section ‘Groups Vm Information’ Name DesktopPool ist used twice!

$tempobject |Add-Member -MemberType NoteProperty -Name DesktopPool -value $vm.desktopdata.name
$tempobject |Add-Member -MemberType NoteProperty -Name HypervisorHostname -value $vm.managedmachinedetailsdata.hostname

now it is working fine

thanks

Leave a Reply