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

 

Honoured to be named VMware EUC Champion 2018

You might have seen the announcement on the VMware EUC blog, Twitter or the new EUC Champions page already but I have been named one of the VMware End-User Computing (EUC) Champions for 2018. It is an honour to be awarded this status that only a select few receive each year. For me it feels like a true recognition for the work I have been doing with the Horizon API’s and my activity in the broader (EUC) vCommunity.

What is the EUC Champions Program?

EUC Champions is an experts-only program designed to provide a forum where the end-user computing community and VMware EUC product groups come together and share new product information and ideas through in-person meetings, networking events, industry conferences and webinars. This interaction helps ensure VMware EUC experts receive the most up-to-date information, and VMware product teams hear from industry veterans.

Thought leadership is easier said than done. It takes hard work and an ear to the ground to stay on top of industry trends. Many of our 2018 VMware EUC Champions have been thought leaders for decades, while others are rapidly becoming the go-to experts in their respective area. Whether new or returning, this year’s champions are among the ranks of end-user computing experts, who have done the work, made the commitment and signed up for more of the same in 2018.

What are the requirements to become an EUC Champion?

Not everyone is cut out to be an EUC Champion. It takes deep VMware EUC product expertise, an ability to write about it, a willingness to voice your opinion and the talent to clearly and concisely communicate ideas. EUC Champions are respected by their peers and, most importantly, are respectful of others.

Specifically, we look for candidates that meet the following criteria:

  • Member of the vExpert Program
  • Recognized EUC expert
  • Well regarded member of the greater EUC community
  • Recommended group member

Who are the 2018 EUC Champions?

On the new page there is a nice overview of all 34 EUC Champions

https://www.vmware.com/euc-champions/current-champions.html

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.

Added functions in vmware.hv.helper

Last Saturday I created a pull request to add some new functionality to the VMware.hv.helper. Together with an older PR that was still open it received an okay on Sunday. This is a list of the functionality I have added:

  • Get-HVHealth
    • Shows the health information for the following services:
      • ADDomain
      • CertificateSSOConnector
      • ConnectionServer,EventDatabase
      • SAMLAuthenticator
      • SecurityServer
      • ViewComposer
      • VirtualCenter
      • Pod
  • new-hvpodfederation
    • Initiates the Cloud Pod Architecture.
  • remove-hvpodfederation
    • Uninitiates the Cloud Pod Architecture.
  • get-hvpodfederation
    • Shows information about the Cloud Pod Architecture.
  • register-hvpod
    • Registers a new pod in the Cloud Pod Architecture.
  • unregister-hvpod
    • Removes a pod from the Cloud Pod Architecture. This can either be gracefully or forced.
  • set-hvpodfederation
    • Sets the name of the Cloud Pod Architecture.
  • get-hvsite
    • Retrieves information about all sites in the Cloud Pod Architecture.
  • new-hvsite
    • Creates a new site in the Cloud Pod Architecture.
  • set-hvsite
    • Sets site properties within the Cloud Pod Architecture.
  • remove-hvsite
    • Removes a site from the Cloud Pod Architecture.

The next functionality on my list is to put the pod service methods from this previous post into functions.

Managing Pods in a Horizon View Cloud Pod Environment using PowerCLI & Api’s

After Pod Federations and Sites it is time to manage the actual pods. Let’s take a look at what we can do.

$services1.pod | gm

Looks like a short post to me since there’s onl Get, List and Update

[sta_anchor id=”get” unsan=”Get” /]

Pod_Get

Just like with site’s the get can be used in conjunction with a podid that might be gotten from somewhere else

$services1.pod.pod_get((($services1.site.site_list()).pods | select -first 1))

This selects the first podid listed when pulling all the pods from all sites and gets the information about that pod. We’ll see the same information when doing a list but just with all pod’s listed.

[sta_anchor id=”list” unsan=”List” /]

Pod_List

$services1.pod.pod_list()

Those endpoints are the connection servers in the pod. Let’s take a short detour and get the listing for one of those (the podendpoint service only has list and get so you will not see them separately anyway).

$services1.PodEndpoint.PodEndpoint_Get((($services1.pod.pod_list() | select -first 1).endpoints | select -first 1))

It might look lazy to use the select -first one and yes it is a bit but doing  a foreach to explain things also doesn’t really work in my opinion.

[sta_anchor id=”update” unsan=”Update” /]

Pod_Update

Standard by now, first we need to connect to the podservice.

$podservice=new-object vmware.hv.podservice
$podhelper=$podservice.read($services1, ($services1.pod.pod_list() | select -first 1).id)

Under $podhelper we can already see how to set things.

$podhelper | gm

Let’s update  the easy things.

$podhelper.setdescription("This is a new description")
$podhelper.setDisplayName("This is a new name")
$podservice.update($services1, $podhelper)
$services1.pod.pod_list()

As a result we have updated the name and description of the pod. The other thing we can do is assign the pod to another site. Thankfully I already have two of those created.

$services1.site.site_list()
$siteid=$services1.site.site_list() | select -first 1

$podhelper.setsite($siteid.id)
$podservice.update($services1, $podhelper)
($services1.pod.pod_list()).site
$services1.site.site_list()

Both the lists aren’t required but I added them to show that the pods are spread over both pods now.

 

Creating and managing Sites in a Horizon View Cloud Pod Environment using PowerCLI & Api’s

Intro

Like I said in my previous post about Pod Federations this is a separate post that will show how to handle Sites within a Pod Federation. There are only a couple of API calls that do not include assigning a pod to a site. This is done trough the podservice which I will post about in a next blog post.

Let’s take a look at the site service to see what it actually has in api call’s

$services1.site | gm

So we have Site_create, Site_delete, site_get, site_list and site_update. To Make it myself easy I will use the order of List, create, get, update and delete.

[sta_anchor id=”list” /]

Site_list

With site_list a list of all available site’s will be created, currently I have only one so let’s show that one.

$services1.site.site_list()

Note a lot of information is shown so let’s take a look at the contents of base and pods.

($services1.site.site_list()).base

($services1.site.site_list()).pods

so again not a lot of information since it only contains a name, description and the pod id’s of the member pods.

[sta_anchor id=”create” /]

Site_Create

Since we already saw in the methods under the siteservice that the create needs a bit more information then just a name let’s take a look again at what is required.

$services1.site.site_create

An object is needed of the type vmware.hv.sitebase, we will need to take a look in the API explorer to see what this object should contain. Under Site_create we can click on sitebase.

The sitebase object has 2 properties of which only DisplayName is required. I have tried various ways to keep the description empty but haven’t succeeded so far and with it it the create also doesn’t work so how optional is it?

Let’s create the sitebase object

$sitebase=new-object vmware.hv.sitebase
$sitebase.displayname="blogpostdemosite"
$sitebase.description="This is a blog demo site"
$sitebase

The $sitebase is not required but shows what the object contains. Now we have enough to create the new site.

$services1.site.site_create($sitebase)

[sta_anchor id=”get” /]

Site_Get

In the overview we have seen that a site_get needs a bit more information.

$services1.site.site_get

We already know how to get this site id by using site_list, normally you would only use the site_get with an id received from another service like the pod service. For the example I will use the demo site I create in the site_create part of this post.

First I will need to get the siteid

$demosite=$services1.site.site_list() | where-object {$_.base.displayname -like "*blogpostdemosite*"}

And now we need to apply that to the site_get

$services1.site.site_get($demosite.id)

[sta_anchor id=”update” /]

Site_Update

As said before for an update method it is better to use the helper service for that service.

$siteservice=new-object vmware.hv.siteservice

now what method’s do we see?

$siteservice | gm

To see the difference between the sitebasehelper and siteinfohelper I will create both objects.

$siteinfohelper=$siteservice.read($services1, $demosite.id)
$sitebasehelper=$siteservice.read($services1, $demosite.id)

Now let’s compare them.

$sitebasehelper | gm
$siteinfohelper | gm

 

This is again one of those wtf moments, they both do exactly the same! I will use the sitebasehelper for now will update both the Displayname and description. For this I will need to use the getbasehelper 1 step deeper

$sitebasehelper.getbasehelper() | gm

$sitebasehelper.getbasehelper().setDisplayname("thissitecanberemoved")
$sitebasehelper.getbasehelper().setDescription("yes it can really be removed")

and apply the update, since neither will generate a response I won’t put any screenshots in.

$siteservice.update($services1, $sitebasehelper)

Now let’s see the result for a site_get for this site now

[sta_anchor id=”delete” /]

Site_Delete

We can take a look at it but to delete a site we only need the siteid so let’s remove that site we gave an update.

$services1.site.Site_Delete($demosite.id)

again no visual feedback but if we do a sitelist there’s only one left.

 

 

Initiating and managing the Podfederation in a Horizon Cloud Pod Architecture using PowerCLI & API’s

One of the new cmdlets for the vmware.hv.helper that I am currently working on is initiating the Cloud Pod Architecture (CPA) and more actions related to this. This blog post will show the basics about initiating, and joining a CPA using the API’s. Doing things with site’s within the CPA will be covered in a later blogpost.

If we look at the services available in the Horizon API’s you’ll see that podfederation is one of them, let’s take a look at that and what method’s are available.

$services1.PodFederation | gm

So we can Eject, Get, Initialize, Join, Unintialize, Unjoin and update a podfederation. If we look at the brackets behind the methods than (un)initializing and unjoin don’t need any extra info so let’s get ahead and initialize the podfederation. To show you there’s nothing there yet I made a screenshot of the admin interface.

[sta_anchor id=”initialize” /]

Initialize the podfederation

Now to initiate the podfederation

$services1.PodFederation.PodFederation_Initialize()

And if you are quick enough in switching to the admin interface will also still show it initializing

[sta_anchor id=”get” /]

Get information about the federation

With podfederation_get() we can grab the configuration information.

$services1.PodFederation.PodFederation_Get()
($services1.PodFederation.PodFederation_Get()).data
($services1.PodFederation.PodFederation_Get()).localpodstatus
($services1.PodFederation.PodFederation_Get()).localpodstatus.LocalConnectionServerStatuses

Not a lot of information but there isn’t a lot more anyway in the podfederation itself.

[sta_anchor id=”join” /]

Join a federation

I have another pod that I want to join to this federation since we’ve already seen that this needs some more input let’s check what it exactly needs.

$Services1.PodFederation.PodFederation_join

So we need a remotepod address, presumable one of the connection servers in that pod will be enough, a username where domain\username will do just like in the admin console and a password of the type vmware.hv.securestring. The last one was new for me but thankfully it was described in one of the examples in the api explorer (https://code.vmware.com/apis/75/view and click on Data Object Types).

$vcPassword = New-Object VMware.Hv.SecureString
$enc = [system.Text.Encoding]::UTF8
$vcPassword.Utf8String = $enc.GetBytes('passwd')

With this it’s easy to add the local pod to the podfederation

$Services1.PodFederation.PodFederation_join("connectionserver","domain\username",$svcpassword)

And again if you are fast enough this is also visible in the admin console

And now a get will also show that it has been enabled

[sta_anchor id=”unjoin” /]

Unjoining a Podfederation

If you are braking down a pod because of whatever reason the best way to do this is to unjoin the pod from the federation. As we saw before there’s no extra information need so you just need to connect to a connection server in that pod and do an unjoin.

$services1.PodFederation.PodFederation_Unjoin()

this is really fast so over several tries I did not succeed in making a screenshot of the admin console.

[sta_anchor id=”eject” unsan=”Eject” /]

Ejecting a pod

This is the only podfederation function not available through the admin console as far as I could see. Ejecting a pod for is for me a last option if a datacenter burned down, everything is gone and you want to get rid of the pod. I did it in my lab against an alive pod and had to uninitialize the (now unlinked) podfederation from that pod to be able to rejoin it to the correct pod. This method also requires some input so let’s see what that is.

$services1.PodFederation.PodFederation_Eject

So we need the podid of the pod to eject, this information can be get trough the pod service

$services1.pod.Pod_List()

I want to eject the pod from pod2cbr1

$pod=$services1.pod.Pod_List() | where {$_.displayname -like "*pod2cbr1*"}

and with $pod I can check if I have the correct one

So let’s serve the eviction notice to the pod.

 $services1.PodFederation.PodFederation_Eject($pod.id)

No feedback, nothing but if we check the pod list it’s gone.

I will show how to remove the remnants in the uninitialize chapter.

[sta_anchor id=”update” /]

Updating a Pod Federation

This one sounds bigger then it is since there’s only one thing that we can update in a federation. To do this it is better to use the helper service then to use the podfederation_update method since that can get complicated very fast sometimes. To use the helper service we will need to create some variables first

$podservice=new-object vmware.hv.podfederationservice
$podservicehelper=$podservice.read($services1)

and when we do a get method on it

After some trial and error I know we need to getdatahelper method to continue

This only show the updates that are currently in the queue to be applied with a get method it’s possible to see what can be set.

$podservicehelper.getDatahelper() | gm

What we need to look for is a set so the only options here are setdisplayname that needs a string value and setupdates that needs a load of information and that probably might also be a way to do it but I will use the setdisplayname.

$podservicehelper.getDatahelper().setdisplayname("Whatever name you like")

This will give no feedback and nothing will be changed yet, what needs to be done is to apply this update in the helper service to the service.

$podservice.update($services1, $podservicehelper)

and if you now do a get on the podfederation it will show the changed name.

[sta_anchor id=”Unintialize” /]

Uninitializing a Podfederation

To show the pod uninitialization step I will use the pod that I have ejected from the podfederation pod2cbr1. It is clear that it is a bit wonky if we look at the pod list from that connection server.

So it knows about the pod federation but doesn’t see itself in it anymore.

$services1.PodFederation.PodFederation_Uninitialize()

This is again a fast one so I couldn’t get it visible in the admin console but when checking the data from a get it shows it has been disabled.

Looking from the other pod it still shows the Podfederation as enabled.

No github scripts this time since I will be adding this functionality into the vmware.hv.helper module.

Remove desktop assignment

In this blogpost I showed how to assign a certain vdi machine to a user. This has been made easier since in the vmware.hv.helper module. These days we can use this:

get-hvmachine -machinename MACHINENAME | set-hvmachine -user USER@DOMAIN

This week I got the logical question from Brandon Smith in the comments about removing the assignment. First I need to re-assign the desktop (I have been building a VMUG presentation about PowerCLI & Horizon view so things got messed up)

the result:

Now what need to be done is setting the base.user to $null. I am going to do this by connecting to the machine service and utilize the machinehelper to update the userdata.

$machineservice=new-object vmware.hv.machineservice

We now need to connect to the machinehelper by doing a read on the machineservice. $machineservice.read will give us the info we need to be able to do this.

From this it becomes clear that we will need the services service and the machineid we want to edit. First let’s put the machine id into a variable.

$machineid=(get-hvmachine -machinename MACHINENAME).id

Now I will read the properties of this machineid and put this into the $machineinfohelper variable

$machineinfohelper=$machineservice.read($services1, $machineid)

Since I know the user property is under the base we will need to get the base first and then set the user. This done by doing getbasehelper() on the machineinfohelper and then do .setuser(user) on that but let’s see what’s under the getbasehelper first.

$machineinfohelper.getbasehelper() | gm

A lot of information but as said the one we need is setuser. To assign a desktop we will need to set this to a userorgroupid value (and that is what the vmware.hv.helper cmdlet does). To clear it we will need to set it to $null.

$machineinfohelper.getbasehelper().setuser($null)

At this point no changes have been made yet! We will need to apply this update first.

$machineservice.update($services1, $machineinfohelper)

And if we look at the horizon console the entitlement has been removed.

And the complete script:

$hvserver1=connect-hvserver connectionservername
$services1=$hvserver1.extensiondata
$machinename="MACHINENAME"
$machineid=(get-hvmachine -machinename $machinename).id
$machineservice=new-object vmware.hv.machineservice
$machineinfohelper=$machineservice.read($services1, $machineid)
$machineinfohelper.getbasehelper().setuser($null)
$machineservice.update($services1, $machineinfohelper)

As usual the script can be found on github.

Finding Horizon View local entitlements using PowerCLI

Intro

In a previous post i mentioned that finding the entitlements for a user from the Horizon side of things can be a bit of a hassle. If only active directory groups are used its dead easy: just use the Active directory commands for those groups. If the groups are used for multiple pools and if you have assigned desktops things get a bit more complicated. For now I will only concentrate on the local pod without global entitlements.

getting that info

To get started the vmware.hv.helper module has the get-hventitlement command. As almost always a very useful one but it has some flaws. First it requires full domainname\username or username@fulldomainname.

For example

get-hventitlement -user magneet.lab\user1

or

get-hventitlement -user user1@magneet.lab

Both work but

get-hventitlement -user magneet\user1

gives this message: Get-HVEntitlement: No entitlements found with given search parameters.

At least

get-hventitlement -user user1

If you add the -type group to this command you get all group entitlements

gives an error message that the -user argument does not match the “^.+?[@\\].+?$” pattern. With this last one you at least get an error so you know where to look but not displaying any entitlements is an issue for me.

So, back to the results of these commands, I have assigned the user user1 the following rights

  • Pool04 directly and by using a group
  • directly on a single desktop in pool04.
  • Pool01 only by group.
  • Paint rds app by group
  • Calculator rds app direct
  • Wordpad rds app by both group & directly

When using the get-hventitlement without anything else it doesn’t seem to show a lot of usable things

get-hventitlement -user user1@magneet.lab

If you put this between brackets followed by a period and one of the properties a bit more info is shown.

(get-hventitlement -user user1@magneet.lab).base

Some information about the user, not very usable the session data property gives some information about current sessions (none at the moment)

With the localdata property it looks like we hit the motherload jackpot thingy

(get-hventitlement -user user1@magneet.lab).localdata

Very good, a lot of id’s so what can we do with those? For now I will put this into $entitledids.

$entitledids=(get-hventitlement -user user1@magneet.lab).localdata

I read something about get-hvinternalname when checking out the module, sounds usable.

get-help get-hvinternalname -examples

Ah, so this needs an entityid as input, a machine is an entity so let’s try it. This might need a foreach though because the output gave machines and not machine.

foreach ($Entityid in ($entitledids.machines)){get-hvinternalname $Entityid}

Damn, that’s not usable, let’s double-check with the other id types

foreach ($Entityid in ($entitledids.desktops)){get-hvinternalname $entityid}
foreach ($Entityid in ($entitledids.desktopuserentitlements)){get-hvinternalname $entityid}
foreach ($Entityid in ($entitledids.aplicationuserentitlements)){get-hvinternalname $entityid}
foreach ($Entityid in ($entitledids.applications)){get-hvinternalname $entityid}

The ones we need are readable, couple of them not but I don’t those will be missed.

The missing machine name is actually easy to solve by doing an api call:

foreach ($Entityid in ($entitledids.machines)){($services1.machine.machine_Get($Entityid)).base}

Conclusion

Because this is rather easy to use and since I didn’t have a direct use case for that I decided not to create a complete script. With get-hventitlement, get-hvinternalname and maybe an api call here or there it’s very easy to pull the information about which account or groups have what rights. To see if a user belongs to a group can easily be done with any of the multitude of scripts for that here’s a good example of those.

 

Removing faulty Horizon desktops using PowerCLI

So last week there where a couple of posts on vmtn about people wanting to automatically removing or refreshing faulty Horizon desktops. With faulty I mean desktops in Agent Unreachable or in error state or whatever status are available. Since this was something i had been investigating anyway I decided to make a script for it that had separate menu’s for the status the desktop needs to be and to pick the desktop to be deleted. The latter part can be rebuild to do all those desktops at once  in case something breaks pretty badly during a recompose of the pool.

The largest part of the script is for creating the menu’s. Since the amount of returned desktops is variable and names differ it’s not possible to use a static menu. Instead I have used a menu structure created by Roman Gelman and that can be found inside this script on github. The part that gets things done i have listed below. The $spec array doesn’t need to be created but it is required in the API call to remove the desktop, Powershell assumes everything true by default when it’s empty but it just has to be called otherwise you will get a big fat red error. To remove multiple desktops at once machine_deletemachines needs to be used with an array filled with desktop id’s and $spec.

$spec = New-Object VMware.Hv.machinedeletespec
$spec.deleteFromDisk=$TRUE

$desktops=@()
$desktops=get-hvmachine -state $targetstate
$selectdesktop=@()
foreach ($desktop in $desktops){
    $selectdesktop+= New-Object PSObject -Property @{"Name" = $desktop.base.name
    "ID" = $desktop.id;
    }
}

$selectdesktop=write-menu -menu ($desktops.base.name) -header "Select the desktop you want to remove"
$removedesktop=$desktops | where {$_.base.name -eq $selectdesktop}


try {
	$services1.machine.machine_delete($removedesktop.id, $spec)
	#$services1.machine.machine_reset($removedesktop.id, $spec)	
	write-host "$selectdesktop will be marked for deletion" -ForegroundColor Green
}
catch {
	write-host "Error deleting $selectdesktop" -ForegroundColor Red
}

As always the complete script can be found at Github where it will also be updated. This is how it looks in the end:

Update

After the comment below I decided to create the script to delete all desktops in a certain state. It’s a variation of the script above, just a bit shorter. Again it can be found on Github. Please be aware that due to a limitation in get-hvmachine both these scripts will only handle 1000 desktops at a time. It is safe to just repeat the script to do the rest.

https://github.com/Magneet/Various_Scripts/blob/master/remove_faulty_VDI_desktop.ps1

https://github.com/Magneet/Various_Scripts/blob/master/remove_multiple_faulty_VDI_desktops.ps1