Setting maintenance mode for Linked Clones using API’s

If you have used the VMware.hv.helper the title of this blog post might sound strange since the set-hvmachine already has a way to set maintenance mode. When Ryan Butler asked me the question this week though I didn’t think of that and dived into the api’s immediately. The machines.Machine_EnterMaintenanceMode method looked good to me and than I though of the vmware.hv.helper and noticed that with

Set-HVMachine -Maintenance ENTER_MAINTENANCE_MODE

it was also possible so set maintenance mode. The usage though made me think immediately that this was not actually using a proper api call but the update function. A quick look at the function itself confirmed this. It sets that status of the virtual machine by directly setting the status.

if ($Maintenance) {
      if ($Maintenance -eq 'ENTER_MAINTENANCE_MODE') {
        $updates += Get-MapEntry -key 'managedMachineData.inMaintenanceMode' -value $true
      } else {
        $updates += Get-MapEntry -key 'managedMachineData.inMaintenanceMode' -value $false
      }
    }
(this is just a snippet of the complete function)

If you are below version 7.5 of Horizon view it’s probably of no use to continue with the rest of this blog post. The api explorer only mentions the relevant functions since 7.5! They have been tried against 7.0.3 and 6.2 and there they don’t work.

So back to the drawing board it was and I needed to look at the API explorer, there are 4 relevant methods for maintenance mode.

As usual there are methods for multiple machines that use an array of id’s (with machines in the name) and methods for single machines id’s (without the machines in the name).

Since I usually use instant clones these days I created a small pool with three linked clones. With get-hvmachine I can show you their names and state.

(get-hvmachine -pool pod2_linked).base | select-object name,basicstate

Since I know that get-hvmachine will already give you the id of a machine it’s easy to do a one liner to set one system in maintenance mode.

 $services1.Machine.Machine_EnterMaintenanceMode((get-hvmachine -machinename p2lc001).id)

and exit maintenance mode.

 $services1.Machine.Machine_ExitMaintenanceMode((get-hvmachine -machinename p2lc001).id)

And the entire pool?

$services1.Machine.Machine_EnterMaintenanceModemachines((get-hvmachine -pool pod2_linked).id)

And exit maintenance mode for the entire pool.

$services1.Machine.Machine_ExitMaintenanceModemachines((get-hvmachine -pool pod2_linked).id)

Okay so we now know how this works but I don’t want to use to vmware.hv.helper module for this at all because I want to be able to use a list of machines or based on part of the name. That can be done using a query. The query entitytype to use is MachineSummaryView and if you use queryfiltercontains it’s also possible to use only a part of the name for a kind of wildcard selection. Combine several of these in with queryfilteror and it gives the opportunity to select them from a list.

$connectionserver="servername"
$hvserver1=connect-hvserver $connectionserver 
$Services1= $hvServer1.ExtensionData
$machines=get-content machines.txt
$queryService = New-Object VMware.Hv.QueryServiceService
$defn = New-Object VMware.Hv.QueryDefinition
$defn.queryentitytype='MachineSummaryView'
$filterset=@()
foreach ($machine in $machines) {
    $queryfiltercontains=New-Object VMware.Hv.QueryFiltercontains -Property @{ 'memberName' = 'base.name'; 'value' = $machine }    
    $filterset+=$queryfiltercontains
    }
$orFilter = New-Object VMware.Hv.QueryFilterOr
$orFilter.filters = $filterSet
$defn.filter=$orFilter
$ids=($queryService.QueryService_Create($Services1, $defn)).results
$services1.Machine.Machine_EnterMaintenanceModeMachines($ids.id)
p2lc001
p2lc003

Now I replaced the names in the txt file with only p2lc00

$connectionserver="servername"
$hvserver1=connect-hvserver $connectionserver 
$Services1= $hvServer1.ExtensionData
$machines=get-content machines.txt
$queryService = New-Object VMware.Hv.QueryServiceService
$defn = New-Object VMware.Hv.QueryDefinition
$defn.queryentitytype='MachineSummaryView'
$filterset=@()
foreach ($machine in $machines) {
    $queryfiltercontains=New-Object VMware.Hv.QueryFiltercontains -Property @{ 'memberName' = 'base.name'; 'value' = $machine }    
    $filterset+=$queryfiltercontains
    }
$orFilter = New-Object VMware.Hv.QueryFilterOr
$orFilter.filters = $filterSet
$defn.filter=$orFilter
$ids=($queryService.QueryService_Create($Services1, $defn)).results
$services1.Machine.Machine_ExitMaintenanceModeMachines($ids.id)

And back into maintenance mode

So this is a nice way to manage the machines and their maintenance state. Please remember that these scripts only work against horizon 7.5 and higher.

Sending messages to users with the Horizon API’s

I got the question today from Fabian Lenz if it is possible to send messages to end users using the Horizon API. I knew I had seen it somewhere already and here’s a quick explanation.

There are two method’s to do this, one for a single session and the other for a group of sessions. Both fall under the session service.

$services1.session | gm

You can see both the methods called session_sendmessage and session_sendmessages if we look at what’s required for both we see that the difference is a single sessionid or an array of session id’s.

Let’s see what the API explorer says what’s needed.

So the msgtype is a string that can have three values and the message is just a string, let’s test this.

I am lazy and will use get-hvlocalsession for the sessionid.

$session=get-HVlocalsession | select -first 1

I do the -first 1 so it isn’t an array but a single session.

Now let’s send a message.

 $services1.session.Session_SendMessage($session.id,"INFO","This is a test message for retouw.nl at 30-10-2018 19:13h")

And the result:

Now let’s do the same for multiple sessions.

$sessions=get-HVlocalsession
$services1.session.Session_SendMessages($sessions.id,"ERROR","This is a test message with multiple recipients for retouw.nl at 30-10-2018 19:25h")

And to show that this also works for global sessions (both where connected to pod2cbr1)

$sessions=get-HVglobalsession 
$services2.session.Session_SendMessages($globalsessions.id,"WARNING","This is a test message with multiple global recipients for retouw.nl at 30-10-2018 19:30h")

If you want to filter the sessions on user or machine name you can filter the $globalsessions on $globalsessions.namesdata.basenames

 $globalsessions.namesdata.basenames | select-object username,machineorrdsservername,clientname

With the localsessions it’s located in $sessions.namesdata

$sessions.namesdata | select-object username,machineorrdsservername,clientname

It’s also possible to filter this with the query service, take a look on my previous post on how to handle queries.

So now you know how to send messages to users. Not that they always read these messages but at least you can try warning them a bit faster now!

Horizon View Api’s: back to basics part 2: Queries

So this is the second post in this series about the Horizon View API basics. While functions logically would be part 2 I have decided on doing queries first since you might need the result for a query before you can use some of the functions. Some if it will seem double from my recent post about pulling event information but not everyone might be looking into that. This post will not have as much gifs as the first post but that’s because only the end results count for this one.

Looking at the API Explorer these are the services that we can actually query:

There are some examples in the API explorer but will show you some different examples with the aduserorgroupsummaryview since that one has a lot of documented filters and I can easily add some extra accounts to show different queries.

The first thing that we always need to do with queries is to declare the queryservice object

$queryservice=new-object vmware.hv.queryserviceservice

If you do a get-method on this object it will show several possible methods to use.

The next thing to do is to create a definition object in which we will declare the things the query looks for.

$defn = New-Object VMware.Hv.QueryDefinition

When getting this object and its get-method it shows the definitions we can set, as you can see all method’s are already visible from just viewing the object so we don’t need to use the get-method anymore.

Now we need to set a QueryEntityType these can be found in the API explorer under the Queryable Entity Types. They have to be used between quotation marks.

$defn.queryentitytype='ADUserOrGroupSummaryView'

Now we’re already set to create some results by putting the query into an object.

$queryResults = $queryService.QueryService_Create($hvServices1, $defn)

As you can see below this will create an object where the results property contains all the data. This Results property has 2 other properties of it’s own that actually contain all the data.

And if we expand the base property it gives us one of the build-in Active Directory groups.

$queryresults.results | select-object -first 1 | select -expandproperty Base

Since this can go several layers deep it is smarter to use the round brackets to get this information. (Select -expandproperty following select – expandproperty is just ugly and takes too long)

($queryresults.results | select-object -first 1).base

This way it’s also easy to count the amount of returned objects. This is very useful if you have a bigger environment and want to take counts of sessions with their status (i.e. Connected, disconnected, error etc)

($queryresults.results).count

Next up is adding a filter, so we only get user accounts but we need to do some maintenance first. If you do too many queries it is possible that you will get some errors about too many filters or something (of course I am not getting them while writing this post) so, it might be needed to remove the old stored queries is possible with the queryservice_deleteall method.

$queryservice.QueryService_DeleteAll($hvservices1)

This does not give any feedback on the results so let’s continue with the old query and put a filter on it. First you need to know what kind of filter you need and the options are listed in the API explorer.

The first one I will use is queryfilterequals since I use that the most. I start by defining a filter object consisting of a property with a value.

$filter = New-Object VMware.Hv.QueryFilterEquals -Property @{ 'memberName' = 'base.group'; 'value' = $false }

Then I will add it to the querydefinition

 $defn.filter=$filter

Now I will show you the results + the fact that you don’t necessarily need to define an object for the results. I have selected the first result to show you that it contains the domain administrator account

($queryService.QueryService_Create($hvServices1, $defn)).results.base | select -first 1

It is also possible to combine several filters into one query, while the ad service might not be the most useful for this it can still be used as an example. The thing to do is to first create a couple of filters.

Please be aware that these membernames are case sensitive!

$notgroupfilter = New-Object VMware.Hv.QueryFilterEquals -Property @{ 'memberName' = 'base.group'; 'value' = $false }
$usernamefilter = New-Object VMware.Hv.QueryFilterEquals -Property @{ 'memberName' = 'base.loginName'; 'value' = "m_wouter" }

These need to be combined into one array

$filterarray=@()
$filterarray+=$notgroupfilter
$filterarray+=$usernamefilter

To filter on multiple things we need to have a filterand object

$filterAnd = New-Object VMware.Hv.QueryFilterAnd

And then we can add the $filterarray to this object

$filterAnd.Filters = $filterarray

and finally we put this object into the querydefinition object

$defn.Filter = $filterAnd

Now let’s run the query

($queryService.QueryService_Create($hvServices1, $defn)).results.base

That’s it for the basics of doing queries using the Horizon View API’s. There are some more things that we can do with these like sorting them, but I think you can find that on your own in the API explorer examples.

[Update 15-10] VMware PowerCLI 11.0.0 release with new Horizon (7.6!) API calls

UPDATE 12-10: The new API explorer page also has been published, it just needs to be added to the main page. Check this link: https://code.vmware.com/apis/445

Update 15-10: I have received an overview from VMware about the other changes:

New API Endpoints:
ConnectionServer_GetTags
GlobalSettings_GetEnvironmentSettings
QueryService_DeleteByIds
Datastore_GetDatastoreRequirements
Datastore_ListDatastoresByDesktopOrFarm
RemoteApplication_EndApplication

There also have been some changes to some objects (MachineBase,AccessGroup etc) to include more properties

Original Article:

Today the latest version of PowerCLI was released with version 11.0.0. When you look at the release notes it’s obvious that some extra things have been added for the Horizon VIew API’s.

PowerCLI has been moving at quite the rapid pace over the last 2 years. In 2018, we’ve been releasing roughly every other month to make sure we get the latest features, performance improvements, and updates available as quickly as possible. Well, it’s been two months and we’re not going to break this trend. Today, we are releasing PowerCLI 11.0.0!

PowerCLI 11.0.0 comes with the following updates:

  • Added a new Security module
  • Added new cmdlets for Host Profiles
  • Added a new cmdlet to interact with NSX-T in VMware Cloud on AWS
  • Support for vSphere 6.7 Update 1
  • Support for NSX-T 2.3
  • Support for Horizon View 7.6
  • Support for vCloud Director 9.5
  • Multiplatform support for the Cloud module
  • Updated the Get-ErrorReport cmdlet
  • Removed the PCloud module
  • Removed the HA module

Even though Jake Robinson already gave me a heads up that this version was coming it’s always the question what has been added for Horizon View. According to the API explorer page no new querydefinitions have been added. Like last time I decided to compare the services against the old list and there are two new additions:

  • CategoryFolder
  • ResourceSettings

I have tried both against a Horizon 7.5 setup and they failed so these are only exposed from Horizon View 7.6 and up.

The first one called Categoryfolder is linked to the possibility to put rdsh applications into folders.

It currently has only one function:

I have also investigated if there was a way to change things using the helper function but sadly it has no .update api call so that’s a no-go. I currently have no rdsh on my lab so I can do the list but it doesn’t show anything.

The other new service is the .ResourceSettings just like categoryfolder it also only has one function:

For this one I can actually show what it’s used for:

It shows the general settings for forced logoffs.

Sadly this service also doesn’t show a way to change things.

Sadly I have no found no way yet to see what queryservice entity’s have been added so hopefully we will have a new API explorer soon (maybe with release notes this time, pretty please VMware?) that shows us all the new goods.

Horizon View Api’s: back to basics part 1: Connecting

Intro

I have done a lot of deep dives but just like the VMware{Code} session I did at VMworld I think it’s time to go back to the basics on how to work with the Horizon api’s from PowerCLI. First up is connecting to a Connection server and disconnecting. I will be showing various ways to do but the new and secure one is the best for me since that’s also usable when you want to schedule scripts.

The easy way

The easiest wat to connect is by using the connect-hvserver SERVERNAME

connect-hvserver pod1cbr1

This will give you a nice credentials popup.

The Unsecure way

The previous way that I used was by using the -user, -pass and maybe the -domain parameters.

connect-hvserver pod1cbr1 -user m_wouter@magneet -pass Password

The ‘new’ and secure way

Since one of the latest updates it is also possible to use a credential object. We found this out during the Hackathon @VMworld US that it is possible. It will use the good old credentials function from Powershell like in this post from 2008. First you’ll need to make a file with the encrypted password. Big thanks to Ariel & Edgar 🙂 check this for the vDocumentation script.

 read-host -assecurestring | convertfrom-securestring | out-file password.txt

The next two lines I will combine into one, just because I can.

$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist "magneet\m_wouter",(get-content .\password.txt | ConvertTo-SecureString)

Doing it in two lines is also possible and might make it a bit easier to read

$pass= get-content .\password.txt | ConvertTo-SecureString
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist "magneet\m_wouter", $pass

And then it’s time to connect

connect-hvserver -server pod1cbr1 -cred $cred

That’s easy right?

Connect to the api’s

There’s a little bit more to it so you can actually use the api’s. First we need to put the session into a variable. I always use a number so it’s easy to separate my various pods.

$hvserver1=connect-hvserver -server pod1cbr1 -cred $cred

Next up is actually making the services visible. Again I added the number for when I am working with multiple pod’s.

$Services1= $hvServer1.ExtensionData

And a quick look at the available services which I will explain in a next blog post.

Disconnecting

If you are connected to a single Connection server this is easy, just a disconnect-hvserver is enough.

disconnect-hvserver

Or without confirmation, this is a standard powershell thing.

disconnect-hvserver -confirm:$false

This will not work when you are connected to multiple Pod’s so you’ll need to specify the server you are connected to.

disconnect-hvserver -server pod2cbr1 -confirm:$false

The End

Next time I will go into some of the things you can do with the services I quickly showed you.

New View API query services in PowerCLI 10.1.1: pulling event information without the sql password.

A while back I already posted about new services that where available for the View API’s in PowerCLI 10.1.1. Recently the api explorer for this version was published and can be found here. Two things that I didn’t find back then was the addition of two services for the query service. The first is GlobalApplicationEntitlementInfo this one can be compared to the previously already available GlobalEntitlementSummaryView and will return information about global entitlements.

The second added services is extremely useful: you can now query the event database. This means you don’t need the actual sql password anymore to query the events. According to the api explorer at least Horizon 7.3 is required and only events from the Event and Event_Data database tables. A simple query will show all events.

$queryservice=new-object VMware.Hv.QueryServiceservice
$defn=new-object VMware.Hv.QueryDefinition
$defn.queryEntityType = 'EventSummaryView'
$results=($queryservice.QueryService_Query($services1,$defn)).results
$results

As you can see the data is divided in data and namesdata properties, these contain the same data as what is returned with get-hvevent. I added some selections to show only one event.

$results | where {$_.data.eventtype -like "*BROKER_USERLOGGEDIN*"}  | select -last 1 | select -expandproperty data

and

$results | where {$_.data.eventtype -like "*BROKER_USERLOGGEDIN*"}  | select -last 1 | select -expandproperty namesdata

Offcourse it;s better to use filtering from the query directly. The full lust for that is available from the api explorer but I will give a couple of examples. (be aware that membername and the value are case sensitive)

$queryservice=new-object VMware.Hv.QueryServiceservice
$defn=new-object VMware.Hv.QueryDefinition
$defn.queryEntityType = 'EventSummaryView'
$equalsFilter = New-Object VMware.Hv.QueryFilterEquals
$equalsFilter.membername='data.eventType'
$equalsFilter.value="BROKER_USERLOGGEDIN"
$defn.filter=$equalsFilter
($queryservice.QueryService_Query($services1,$defn)).results.data | select -last 1

Or by severity

$queryservice=new-object VMware.Hv.QueryServiceservice
$defn=new-object VMware.Hv.QueryDefinition
$defn.queryEntityType = 'EventSummaryView'
$equalsFilter = New-Object VMware.Hv.QueryFilterEquals
$equalsFilter.membername='data.severity'
$equalsFilter.value="WARNING"
$defn.filter=$equalsFilter
($queryservice.QueryService_Query($services1,$defn)).results.data | select -last 1

As said it can be filtered on other properties as well but that might require some more logic to get the userid or desktopid for example. This is a very useful addition in my opinion to the Horizon View api’s.

New Horizon API calls in PowerCLI 10.1.1

VMware quietly released a new version of PowerCLI last week: 10.1.1. This release is mainly an update for the Horizon View API’s. This to bring it back on level with the current Horizon release at 7.5. The release notes are not very extensive but it has a fix for some people getting time-outs when connecting to a Connection server  plus a bunch of new api calls.

I have dumped the output from the available api calls into two text files and made a comparison:

Since there’s no update yet in the API explorer I will have to make an educated guess on what the functions do:

DesktopTask

When looking at the available method’s for this call it looks like it has everything to do with Desktop task. But it also can’t do a damn thing without an vmware.hv.desktoptaskid. This will most probably bu retrievable using a query. This is something I will further investigate in the future.

DiagOperation

To be honest I have no idea yet what this one does. I have tried created a VMware.Hv.DiagOperationRequest and tried to send it but got an error that no message queue handler was found. This might be something from Horizon 7.5 since I haven’t updated my lab yet.

GatewayAccessUserOrGroup

This one is easy, it creates, deletes, gets and lists remote access users. You can expect a function for this in the near future since it looks easy to build.

JwtToken

According to my sources this is a SSO token between the flex and html5 clients.

LogonTiming

This obviously is created to pull logon timing as the name suggests. I have put a session ID in a variable but sadly the data is not usable from PowerCLI. WHat it seems to be is the api call the Helpdesk client uses to pull the logon time. I didn’t have the timing profiler turned on initially and neither the helpdesk tool or this call gave my any information. Disconnected sessions also don’t give any information and when reconnected it gives the reconnection time not the initial logontime for when the session started. This is the same behaviour as the helpdesk tool.

Apparently the output is in a json format and for now I doubt if it will be usable in a function.

While the session itself has this information.

NetworkProxyConfiguration

No idea yet why there is a networkproxy configuration in here.

Performance

This gets some performance data using a session id as also visible in the helpdesk tool.

RemoteApplication

Gives per session information on the Skype 4 Business pairing mode.

RemoteAssistantTicket

100% sure related to the remote assistance function in the helpdesk tool.

RemoteProcess

Looks like this one gets some information from a query and then kills the process, will have to dig into it some further later on. This for sure is a function in the helpdesk tool.

ViewClient

Again from the helpdesktool, this gives the client version of a session.

Conclusion

For now I only see the DesktopTask and GatewayAccessUserOrGroup ending up in a function in the vmware.hv.helper. The first one will need some digging on how it exactly works but it has the looks of a usable call. The latter on can be in there pretty fast if I find the time to do so. The other ones

 

Update

Already received some extra information about some calls.

New experimental functions for the vmware.hv.helper on github

While working on my presentation for the 2nd vEUCtechcon event in Utrecht (The Netherlands) on may 28th I have added a list of new functions to the vmware.hv.helper module. While I haven’t had the time yet to clean them up to be proper coded scripts I have decided to already publish them on Github. All of them work but might be missing a feature or two and almost all of them are get-hv* or new-hv* type functions. Since the presentation is all about building an environment I have decided to build the remove parts later on. You might have already seen some screenshots on twitter recently:

Added functions that are not in the official module yet:

  • register-hvvirtualcenter
  • set-hveventdatabase
  • set-hvlicense
  • get-hvlicense
  • new-hvinstantcloneadministrator
  • New-HVRole
  • Get-HVRole
  • Get-HVpermission
  • New-HVPermission
  • Get-HVVirtualcenter
  • Get-HVInstantCloneAdministrator
  • Get-HVPod
  • Set-HVPod
  • Get-HVHomeSite
  • New-HVHomeSite

 

Registering an Instantclone administrator using PowerCLI

Another question Sean Massey asked me if it is possible to register an instant clone domain administrator. This is possible using the instantcloneenginedomainadministrator service with the InstantCloneEngineDomainAdministrator_create method. This needs a spec with the following content:

  • spec (vmware.hv.InstantCloneEngineDomainAdministratorSpec)
    • base (vmware.hv.InstantCloneEngineDomainAdministratorBase)
      • username (string)
      • domain (domainid)
      • password(vmware.hv.securestring)

The password can be created using the same scriptlet I used to register a new vCenter server. The domain ID can actually be gotten by listing all domains using

$services1.ADDomain.addomain_list()

For now I have created a scripts that requires you to give some details so it can register the instant clone domain administrator. It can also be found on Github but I will also definitively add it to the vmware.hv.helper module.

$icausername=read-host "What username to use for instantclone administrator?"
$icadomain=read-host "please give the dns name for the domain to user (i.e. domain.com)"
$icapassword=read-host "vCenter User password?" -assecurestring
$temppw = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($icaPassword)
$PlainicaPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($temppw)
$icadminPassword = New-Object VMware.Hv.SecureString
$enc = [system.Text.Encoding]::UTF8
$icadminPassword.Utf8String = $enc.GetBytes($PlainicaPassword)
$spec=new-object vmware.hv.InstantCloneEngineDomainAdministratorSpec
$spec.base=new-object vmware.hv.InstantCloneEngineDomainAdministratorBase
$spec.base.domain=(($services1.ADDomain.addomain_list() | where {$_.DnsName -eq $icadomain} | select-object -first 1).id)
$spec.base.username=$icausername
$spec.base.password=$icadminpassword
$services1.InstantCloneEngineDomainAdministrator.InstantCloneEngineDomainAdministrator_Create($spec)

Pulling horizon session information using PowerCLI

I should’ve already posted a blog about this but better late then never. At the end of february I posted about several new functions being added to the vmware.hv.helper and two out of three where about pulling session information. Recently I received some questions about using those since it’s the raw data being returned. For my Dutch vmug presentation I used several gif’s that showed what you can do with that data. I might need to update the cmdlets so all information will be shown at once but that’s for another time since it might slow down the cmdlet a lot and I don’t like that.

Usage

Since get-hvglobalsession and get-hvlocalsession show almost similar data I will only show the latter one.

get-hvglocalsession

As you see this only shows the methods contained inside the session. We can show the content by pipelining it to  select-object -expandproperty but I prefer the bracket method since these might go several layers deep.

(get-hvlocalsession).namesdata

Some of the returned values are logical like the username, machineorrdsservername. The desktop name though is the actual desktop pool the user is connected to. Desktoptype can be Automated, Manual or RDS depending on the type of desktop and Desktopsource can be Virtual_Center (VM’s hosted on vCenter but not managed by Horizon or Full Clone desktops), View_Composer(when using Linked Clones), Instant_Clone_engine (when using Instant Clones), Unmanaged (physical machines, non-vCenter vm’s) or RDS (Terminal Servers). Farmname will be used when it’s an RDS session. The Securitygateway will show the Connection Server the user connected to or the UAG/Security server used.

the same can be done with referencedata and sessiondata

(get-hvlocalsession).referencedata
(get-hvlocalsession).sessiondata

Not a lot of directly usefull information but a bunch of id’s that you might be able to use with the api’s if needed.

A lot of information about the session itself.

The actual code

The get-hvglobalsession actually is a query repeated for all pods. First it connects to the query service and then creates a query to run against each pod and add that to a sessionlist.

$query_service_helper = New-Object VMware.Hv.GlobalSessionQueryServiceService
$query=new-object vmware.hv.GlobalSessionQueryServiceQuerySpec

$SessionList = @()
foreach ($pod in $services.Pod.Pod_List()) {
  $query.pod=$pod.id
  $queryResults = $query_service_helper.GlobalSessionQueryService_QueryWithSpec($services, $query)
  $GetNext = $false
  do {
    if ($GetNext) { $queryResults = $query_service_helper.GlobalSessionQueryService_GetNext($services, $queryResults.id) }
    $SessionList += $queryResults.results
    $GetNext = $true
  } while ($queryResults.remainingCount -gt 0)
    $query_service_helper.GlobalSessionQueryService_Delete($services, $queryresults.id)

}
return $sessionlist
} 

The get-hvlocalsession is almost the same, it just doesn’t need to foreach since it doesn’t have multiple pods to query.

 $query_service_helper = New-Object VMware.Hv.QueryServiceService
  $query = New-Object VMware.Hv.QueryDefinition

  $query.queryEntityType = 'SessionLocalSummaryView'
  $SessionList = @()
  $GetNext = $false
  $queryResults = $query_service_helper.QueryService_Create($services, $query)
  do {
    if ($GetNext) { $queryResults = $query_service_helper.QueryService_GetNext($services, $queryResults.id) }
    $SessionList += $queryResults.results
    $GetNext = $true
  } 
  while ($queryResults.remainingCount -gt 0)
    $query_service_helper.QueryService_Delete($services, $queryResults.id)
  

  return $sessionlist
  [System.gc]::collect()
} 

In both there is a do while because otherwise it will run into some restrictions about maximum amount of data to return.