[API]New way to gather Horizon Events

A good bunch of my audience has probably already noticed it but with Horizon 8 release 2106 VMware has added a new method to gather Horizon Events: the AuditEventSummaryView query. In this post I will describe how to consume this query using the soap API. I have been told by VMware specialists that this updated version of the eventsummaryview is actually safe to use and wont put a burden on the connection servers.

A quick small script to consume this query could look like this:

[CmdletBinding()]
param (
    [Parameter(Mandatory=$false,
    HelpMessage='Credential object as domain\username with password' )]
    [PSCredential] $Credential,

    [Parameter(Mandatory=$true,  HelpMessage='FQDN of the connectionserver' )]
    [ValidateNotNullOrEmpty()]
    [string] $ConnectionServerFQDN
)

if($Credential){
    $creds = $credential
}
else{
    $creds = get-credential
}

$ErrorActionPreference = 'Stop'

# Loading powercli modules
Import-Module VMware.VimAutomation.HorizonView
Import-Module VMware.VimAutomation.Core

$hvserver1=connect-hvserver $ConnectionServerFQDN -credential $creds
$Services1= $hvServer1.ExtensionData

$queryservice=new-object vmware.hv.queryserviceservice
$defn = New-Object VMware.Hv.QueryDefinition
$defn.queryentitytype='AuditEventSummaryView'


$eventlist = @()
$GetNext = $false
$queryResults = $queryservice.QueryService_Create($Services1, $defn)
do {
    if ($GetNext) {
        $queryResults = $queryservice.QueryService_GetNext($Services1, $queryResults.id) 
    }
    $eventlist += $queryResults.results
    $GetNext = $true
}
while ($queryResults.remainingCount -gt 0)
$queryservice.QueryService_Delete($Services1, $queryResults.id)
return $eventlist

I run it like this, show the event count and the last one

$creds = import-clixml d:\homelab\creds.xml
$events = D:\GIT\Scripts\get-horizon-audit-events.ps1 -ConnectionServerFQDN loftcbr01.loft.lab -Credential $creds
$events.count
$events | select-object -last 1

If you want to filter the data a bit more there are plenty of options for that:

I have added some filtering options to the above script, if you supply the filtertype argument the filterdata and filtervalue are mandatory. Filtertype for now can be either Equals or Contains, filterdata can be any of the data types from the image above and the value is the value you’re going to filter on. To be honest not all of the data types worked when I was creating this post but the message actually did.

[CmdletBinding(DefaultParameterSetName='noFilter')]
param (
    [Parameter(Mandatory=$false,
    HelpMessage='Credential object as domain\username with password' )]
    [PSCredential] $Credential,

    [Parameter(Mandatory=$true,  HelpMessage='FQDN of the connectionserver' )]
    [ValidateNotNullOrEmpty()]
    [string] $ConnectionServerFQDN,

    [Parameter(ParameterSetName='Filter',Mandatory=$true,HelpMessage = "Name of the data type to filter on.")]
    [Parameter(ParameterSetName='noFilter',Mandatory=$false,HelpMessage = "Name of the data type to filter on.")]
    [string]$filterdata,

    [Parameter(ParameterSetName='Filter',Mandatory=$true,HelpMessage = "Value to filter on.")]
    [Parameter(ParameterSetName='noFilter',Mandatory=$false,HelpMessage = "Value to filter on.")]
    [string]$filtervalue,

    [Parameter(ParameterSetName='Filter',HelpMessage = "FIltertype: Equals or Contains.")]
    [validateset("Equals","Contains")]
    [string]$filtertype

)

if($Credential){
    $creds = $credential
}
else{
    $creds = get-credential
}

$ErrorActionPreference = 'Stop'

# Loading powercli modules
Import-Module VMware.VimAutomation.HorizonView
Import-Module VMware.VimAutomation.Core

$hvserver1=connect-hvserver $ConnectionServerFQDN -credential $creds
$Services1= $hvServer1.ExtensionData

$queryservice=new-object vmware.hv.queryserviceservice
$defn = New-Object VMware.Hv.QueryDefinition
$defn.queryentitytype='AuditEventSummaryView'

if($filtertype){
    if($filtertype -eq "Contains"){
        $defn.Filter= New-Object VMware.Hv.QueryFilterContains -property @{'MemberName'=$filterdata; 'value'=$filtervalue}
    }
    else{
        $defn.Filter= New-Object VMware.Hv.QueryFilterEquals -property @{'MemberName'=$filterdata; 'value'=$filtervalue}
    }
}

$eventlist = @()
$GetNext = $false
$queryResults = $queryservice.QueryService_Create($Services1, $defn)
do {
    if ($GetNext) {
        $queryResults = $queryservice.QueryService_GetNext($Services1, $queryResults.id) 
    }
    $eventlist += $queryResults.results
    $GetNext = $true
}
while ($queryResults.remainingCount -gt 0)
$queryservice.QueryService_Delete($Services1, $queryResults.id)
return $eventlist

I run and check it like this:

$events = D:\GIT\Scripts\get-horizon-audit-events.ps1 -ConnectionServerFQDN loftcbr01.loft.lab -Credential $creds -filtertype Contains -filterdata message -filtervalue "has logged in"
$events | Select-Object message -last 10

The last version shown here can be downloaded from my github: Various_Scripts/get-horizon-audit-events.ps1 at master · Magneet/Various_Scripts (github.com)

 

Script to cleanup desktops running on old snapshot

So last year Guy Leech asked if if I had a script to identify machines running on an old snapshot. I Created a script for that here. This week Madan Kumar asked for a script that finds these same VDI desktops but that also cleans them out if needed. For this I have created the Horizon_cleanup_old_images.ps1 script (yes I suck at making up names).

If you run a get-help for the script you’ll see this:

By default the script only requires a Connectionserverfqdn and poolname as it works on a per pool level. It will try to give the users a gracefully logoff and has options to force the logoff ( in case their sessions is locked) or to delete the machine. And if you just want to have a preview there’s an option for that as well.

Optional arguments are:

-credential: this can be created with get-credential or can be retrieved from a stored credentials xml file, just make sure that it looks like domain\username and password

-deletedesktops: if used it will forcefully try to logoff the users but always deletes the desktop

-forcedlogoff: A normal logoff doesn’t work when the sessions is locked so you might need to force it

-preview: no actions are taken, just the information will be displayed to screen.

Let’s use the script

d:\git\scripts\Horizon_cleanup_old_image.ps1 -Credential $creds -ConnectionServerFQDN pod2cbr1.loft.lab -poolname "Pod02-Pool02" -preview

Yes I use write-host but it’s all 1 liners so shouldn’t be too slow and I like colors but as you see with the preview mode it shows what would happen. One of these sessions is locked so let’s see what happens when I log them off.

yes an error but I think it’s clear why, the graceful logoff worked for 2 users but not the third one, I will add the forced option now.

That looks good and when I look at the desktop pool everything is fine there as well.

And that’s being confirmed by the script

Now I will use the delete option for my other desktop pool.

First again with the preview option

and without

and seen from the Horizon Admin

As linked above the script can be found on github but also below this line.

<#
    .SYNOPSIS
    Cleans up desktops running on an image that's not the default for a desktop pool

    .DESCRIPTION
    This script uses the Horizon soap api's to pull data about machines inside a desktop pool that are running on a snapshot or base vm that's not currently configiured on the desktop pool. By default it logs off the users but there are options to forcefully logoff the user or delete the machines.

    .EXAMPLE
    .\Horizon_cleanup_old_image.ps1 -Credential $creds -ConnectionServerFQDN pod2cbr1.loft.lab -poolname "Pod02 Pool02" -delete -preview

    .PARAMETER Credential
    Mandatory: Yes
    Type: PSCredential
    Object with credentials for the connection server with domain\username and password

    .PARAMETER ConnectionServerFQDN
    Mandatory: No
    Default: String
    FQDN of the connection server to connect to

    .PARAMETER Poolname
    Mandatory: Yes
    Type: string
    Display name of the Desktop Pool to check

    .PARAMETER Deletedesktops
    Mandatory: No
    Enables the deleteion of the desktops, this includes an attempt to forcefully logoff the users.

    .PARAMETER Forcedlogoff
    Mandatory: No
    Enables the forcefully logging off of the users.

    .PARAMETER Preview
    Mandatory: No
    Makes the script run in preview mode and not undertake any actions.

    .NOTES
    Created by: Wouter Kursten
    First version: 27-06-2021

    .COMPONENT
    VMWare PowerCLI

#>

[CmdletBinding()]
param (
    [Parameter(Mandatory=$false,
    HelpMessage='Credential object as domain\username with password' )]
    [PSCredential] $Credential,

    [Parameter(Mandatory=$true,  HelpMessage='FQDN of the connectionserver' )]
    [ValidateNotNullOrEmpty()]
    [string] $ConnectionServerFQDN,

    [parameter(Mandatory = $true,
    HelpMessage = "Display Name of the desktop pool to logoff the users.")]
    [string]$poolname = $false,

    [Parameter(Mandatory=$false, 
    HelpMessage='Deletes the desktops instead of forcing the logoff' )]
    [switch] $deletedesktops,

    [Parameter(Mandatory=$false, 
    HelpMessage='Gives a preview only, no action will be undertaken.' )]
    [switch] $preview,

    [Parameter(Mandatory=$false, 
    HelpMessage='Forcefully logs off the users in case the desktop is locked or disconnected.' )]
    [switch] $forcedlogoff
)

if($Credential){
    $creds = $credential
}
else{
    $creds = get-credential
}

$ErrorActionPreference = 'Stop'

# Preview info
if($preview){
    write-host "Running in preview mode no actions will be taken" -foregroundcolor Magenta
 }

 # Loading powercli modules
 Import-Module VMware.VimAutomation.HorizonView
 Import-Module VMware.VimAutomation.Core

$hvserver1=connect-hvserver $ConnectionServerFQDN -credential $creds
$Services1= $hvServer1.ExtensionData

# --- Get Services for interacting with the Horizon API Service ---
$Services1= $hvServer1.ExtensionData

# --- Get Desktop pool
$poolqueryservice=new-object vmware.hv.queryserviceservice
$pooldefn = New-Object VMware.Hv.QueryDefinition
$pooldefn.queryentitytype='DesktopSummaryView'
$pooldefn.Filter= New-Object VMware.Hv.QueryFilterEquals -property @{'MemberName'='desktopSummaryData.displayName'; 'value'=$poolname}
try{
    $poolqueryResults = $poolqueryService.QueryService_Create($Services1, $pooldefn) 
    $poolqueryservice.QueryService_DeleteAll($services1)
    $results = $poolqueryResults.results
}
catch{
    write-error "There was an error retreiving details for $poolname"
}

# we need more details of the pool though and check if we even got one
if($results.count -eq 1){
    $pool = $Services1.Desktop.Desktop_Get($results.id)
}
else{
    write-host "No pool found with name $poolname" -foregroundcolor Red
    break
}

# Search for machine details
$queryservice=new-object vmware.hv.queryserviceservice
$defn = New-Object VMware.Hv.QueryDefinition
$defn.queryentitytype='MachineDetailsView'
$defn.filter = New-Object VMware.Hv.QueryFilterEquals -Property @{ 'memberName' = 'desktopData.id'; 'value' = $pool.id }
[array]$queryResults = $queryService.QueryService_Create($Services1, $defn)
$services1.QueryService.QueryService_DeleteAll()
# Process the results
if ($queryResults.results.count -ge 1){
    [array]$poolmachines=$queryResults.results
    [array]$wrongsnaps=$poolmachines | where-object {$_.managedmachinedetailsdata.baseimagesnapshotpath -notlike  $pool.automateddesktopdata.VirtualCenternamesdata.snapshotpath -OR $_.managedmachinedetailsdata.baseimagepath -notlike $pool.automateddesktopdata.VirtualCenternamesdata.parentvmpath}
    # If there are desktops on a wrong snapsot we need to do something with that info
    if($wrongsnaps.count -ge 1){
        if($deletedesktops){
            write-host "Removing:" $wrongsnaps.data.name -foregroundcolor yellow
            $deletespec = new-object vmware.hv.machinedeletespec
            $deletespec.DeleteFromDisk = $true
            $deletespec.ForceLogoffSession = $true
            if(!$preview){
                $Services1.Machine.Machine_DeleteMachines($wrongsnaps.id, $deletespec)
            }
        }
        else{
            write-host "Logging users off from:" $wrongsnaps.data.name -foregroundcolor yellow
            [array]$sessiondata = $wrongsnaps.sessiondata
            write-host "Users being logged off are:" $sessiondata.username -foregroundcolor yellow
            if(!$preview){
                if($forcedlogoff){
                    write-host "Forcefully logging off users" -foregroundcolor yellow
                    $services1.session.Session_LogoffSessionsForced($sessiondata.id)
                }
                else{
                    write-host "Gracefully logging off users" -foregroundcolor yellow
                    $services1.session.Session_LogoffSessions($sessiondata.id)
                }
            }
        }
    }
    else{
        write-host "No machines found on a wrong snapshot" -foregroundcolor Green
    }
}
else{
    write-host "No machines found in $poolname" -foregroundcolor red
}

 

Getting Session information using the Python Module for VMware Horizon with filtering

Since Horizon 8 it is possible to retrieve session information using the REST api’s. In the python module for Horizon I have translated that functionality to the get_sessions function.

import requests, getpass, urllib, json

import vmware_horizon

requests.packages.urllib3.disable_warnings()
url = input("URL\n")

username = input("Username\n")

domain = input("Domain\n")

pw = getpass.getpass()

hvconnectionobj = vmware_horizon.Connection(username = username,domain = domain,password = pw,url = url)
hvconnectionobj.hv_connect()
print("connected")

inventory=vmware_horizon.Inventory(url=hvconnectionobj.url, access_token=hvconnectionobj.access_token)
sessions = inventory.get_sessions()
for i in sessions:
    for ii in i:
        print(ii, '=', i[ii] )


end=hvconnectionobj.hv_disconnect()
print(end)

You see a lot of the information is returned using an id but there are plenty of functions that you can use to get the readable information for those.

Since Horizon 2103 VMware has added an option use filtering when getting session information. In the example above you can see that I have a connected and a disconnected session, let’s get the connected one. First I create the filter.

filter = {}
filter["type"] = "And"
filter["filters"] = []
filter1={}

filter1["type"] = "Equals"
filter1["name"] = "session_state"
filter1["value"] = "CONNECTED"

filter["filters"].append(filter1)

Next I run the action with the filter argument.

sessions = inventory.get_sessions(filter=filter)

And now you see only the connected one is returned

And when I switch to disconnected

The new python module has been pushed to github together with example scripts for retrieving all sessions and with filtering.

vCheck for Horizon : small updates & running it (automatically) from ControlUp

Remark : as of the time I was writing this article the vCheck for Horizon  sba hasn’t been published yet. Contact me if you want me to send you the xml file.

Last week I have been doing a few small updates to the vCheck for Horizon. The file with the encrypted password is now replaced by an xml file that holds all of the credentials and is usable directly for Horizon connections. I have been using this for my blog posts and demo’s for a while already. Also I have renamed the security & composer plugins by default to old as those have been deprecated and I don’t want to have them active by default anymore. Further I have done some small changes to other plugins.

Creating the xml file

Running the vCheck from horizon

But but wasn’t the vCheck all about running it automagically?

Creating the xml file

this can be easily done like this:

$creds = get-credential
$creds | export-clixml filename.xml

as you might have seen during my demo’s you can use this $creds object to make a connection.

$creds = import-clixml filename.xml
$server = connect-hvserver connectionserver.fq.dn -credential $creds

Make sure to save this file to a good location and edit the “00 Connection Plugin for View.ps1” file with the proper location. I haven’t completely removed the old ways yet, they’re still partially there.

Running the vCheck script from ControlUp

Before everything else: make sure the machine that will run the vCheck has the latest version of PowerCLI installed. Download it here, and unpack the modules to “C:\Program Files\WindowsPowerShell\Modules” like this:

There are several things that I needed to do before I was able to run the vCheck from the ControlUp console or as an Automated Action (more on this later!). First I needed a way of providing credentials to the script. This is something I do using the Create Credentials for Horizon Scripts script action. If you already use some of the other Horizon related sba’s or the Horizon Sync script you might already have this configured. What it essentially does is creating an xml file as described above in a folder dedicated for ControlUp and usable by the service account that ran the sba. More on this can be found here.

Next was a way to get the vCheck itself. I do this by downloading it from github and unpack the zip file to C:\ProgramData\ControlUp\ScriptSupport\vCheck-HorizonView-master if downloads from the interwebz aren’t available you can download and unzip it yourself, just make sure it looks like this:

Next would be all the settings for email, connection server etcetera. This is all done trough the Script Action Arguments. WHat technically happens is that the Script Action removes both the globalvariables.ps1 and the “00 Connection Plugin for View.ps1” files and recreates them with those settings.

Once you have downloaded the vCheck you can run it manually so right click any random machine and select the vCheck for Horizon.

and fill in all the details you need. If you set the Send Email to false there is no need for the rest of the info.

Once you see this screen the script has completed, make sure to read the last line for the correct status and where you saved to file to.

And on my d:\ I see the file (and some others I used for testing)

And even better I received it in my mail as well

But but wasn’t the vCheck all about running it automagically?

Well yes it is and you can do that starting with version 8.5 that we have announced today! For now it will need to run hourly but in future releases we can also do this once a day. What you need to do for this is first change the default for the script action. Select that sba and hot the modify button.

Under settings you can keep the execution context as console (this will use the monitor when you run it as an automated action) or select other machine to pick a scripting server. Also make sure to select a shared credential that has a Horizon credentials file created on this machine.

Now go to the arguments tab and edit the default settings to set them to what you need.

Before:

Editing the first one

and done, press ok after this.

Now click finalize so the monitors can also use them and go trough all the steps ( no need to share with the community) an dmake sure the ControlUp Monitors have the permissions to run the sba.

Next we go to triggers > add trigger and select the new type: scheduled and click next

select the hourly schedule and a proper start and end time and click next

On the filter criteria make sure that you set the name to a single machine, otherwise the script will run for all machines in your environment. I recommend the same machine you used for the execution context. Keep in mind that the schedule functions just like any other trigger so you need to filter properly to what machine it applies and if you don’t it will apply to all of them.

You can select the folder this machine resides in and/or select a schedule to run the script in.

At the follow-up actions click add and select run an action from the pulldown menu. Under script name select the vCheck for Horizon, click ok and next

Make sure to create a clear name and click finish

The html should automatically end up in the export location and in your email.

 

How to add Rocky Linux (& Other not supported Linux Flavors) to ControlUp

Disclaimer: I am a ControlUp employee but am posting this on my own. We officially only support the Linux flavors here: https://support.controlup.com/hc/en-us/articles/360001497917-Linux-Integration-with-ControlUp

Disclaimer 2: I am NOT a Linux expert, I consider myself a basic user for most Linux things.

With the recent rise of the truly community driven (beta) releases of Rocky Linux I thought it was time to see what would be needed to add Rocky Linux to the ControlUp console. If you want to know more about how and what with Rocky Linux I recommend reading my good friend Angelo Luciani’s blog post here. TLDR: Rocky Linux is a project from one of the CentOS co-founders and is supposed to be enterprise grade once GA is reached.

Adding the linux machines to ControlUp

Fixing Linux to work with ControlUp

Adding linux machines to ControlUp

To compare the process with CentOS 7, 8 and Ubuntu 2004 LTS I have deployed fresh vm’s for these distributions. For Rocky and the CentOS vm’s I selected Server with a GUI, for Ubuntu I picked the default installation. All of them got 1 cpu, 2GB ram and 64GB disk space (overkill, yes I know). I also added the same user to all of them during the deployment. Network wise I just gave them a proper host name and selected dhcp as that was enough for this test.

The setup screen for Rocky Linux

To be able to add unsupported Linux Flavors you need to enable this in Settings > Agents

After deploying all 4 of the vm’s I added them to the ControlUp console with the same Linux Data collector.

I could use another credential that I setup in the past for my domoticz & Unify linux machines, after this click add to add the machines.

I entered the ip range to scan, hit scan, selected the 4 linux machines (note: CentOS 8 is not being reported as a not supported flavor, I have reported a bug for this), click add and hit ok twice.

By default the CentOS 7 machine is the only one working correctly.

if you see some weird negative number for CPU make sure to check your locale settings that it is the same as the regional settings on your Console/Monitor.

Now let’s make sure we get proper metrics in the console 🙂

Fixing Linux machines to work with ControlUp

Rocky Linux needs the same fixes as CentOS 8, Ubuntu needs another step.

  1. Install gcc : for CentOS / Rock
    sudo yum install gcc

    or for Ubuntu

    sudo apt-get install gcc
  2. run
    ps -V
    1. if the output has ps from procps you need to remember the number 6
    2. If the output has ps from procps-ng you need to remember the number 7
  3. Create a file called a.c
    1. run:
      nano a.c

      (or vi, whatever you like)

    2. Add:
      1. #include <stdio.h> 
        int main() 
        {
          printf("6\n");
          return 0;
        }

        replace the 6 with 7 if you had procps-ng in the previous step

    3. save this file
  4. run:
    gcc a.c
  5. run:
    sudo cp a.out /bin/rpm
  6. Install sysstat: For Rocky Linux / CentOS
    sudo yum install sysstat

    or for Ubuntu

    sudo apt-get install sysstat
  7. Reboot ( yes I needed this)

For Rocky Linux and CentOS you’re done

For Ubuntu you will also need to install ifconfig from net-tools before the reboot

  1. run:
    sudo apt-get install net-tools

For me this was enough to have all four of these machines look properly in the ControlUp Console. Remember we might not officially support this way of working but it works good

The VMware Labs flings monthly for April 2021 – Easy deploy for AVI Loadbalancers & NSX Mobile!

So it feels like yesterday that I created the previous flings post but April flew by and it’s almost summer, time for some more bad weather over here in The Netherlands. I see a three new flings and ten who received an update. One of the new ones already has quite a long changelog with fixes!

New Releases

NSX Mobile

vRealize Orchestrator Parser

Easy Deploy for NSX Advanced Load Balancing

Updates

Virtual Machine Compute Optimizer

Community Networking Driver for ESXi

ESXi Arm Edition

vSphere HTML5 Web Client

Horizon Session Recording

vSphere Mobile Client

Workspace ONE Access Migration Tool

VMware OS Optimization Tool

SDDC Import/Export for VMware Cloud on AWS

VMware Event Broker Appliance

New Releases

NSX Mobile

Are you an NSX admin? Do you spend major part of your work in monitoring the network and/or its security? Do you have the NSX web UI open on your laptop/desktop most of the day to make sure all the services are up and connectivity between systems is fine?

Carrying a laptop all the time with you could be quite challenging task, especially in situations like the current pandemic. However, your smartphone would be on you most of the time. NSX Mobile brings the ease of monitoring the networking and security right from your phone.

NSX Mobile complements the full-fledged NSX-T web UI by providing monitoring capabilities on the go. If you find something wrong, you can use the conventional web UI or ask someone else to investigate the matter immediately. Focus of the app is to provide instant notifications when something goes wrong and side by side ability to monitor the network & its security from a smartphone.

Features

  • Just install it & login with your NSX-T credentials to get started (make sure that the NSX version is 3.0+ and the NSX IP/domain name is reachable from your smartphone)
  • List and search all networking and security entities (e.g. Tier-0s, Network segments, Firewall Rules, etc.)
  • View alarms generated on NSX (e.g. CPU usage high OR a VPN failed to realize OR Intrusion detected in case if you have IDS firewall, etc.)
  • Push notifications – COMING SOON
  • Quick actions (enable/disable options wherever possible, actions on failures/alarms) – COMING SOON

vRealize Orchestrator Parser

The vRealize Orchestrator Parser is a tool developed to extend the vRealize Build Tools Fling toolchain or to be used stand-alone with the Export Package to Folder option in native vRealize Orchestrator(vRO).

vRealize Orchestrator Parser parses vRO workflow XML files and extracts programming language code (Javascript, Python, Powershell, etc) and stores it as discrete files, that can then be checked into a source code control system, and or edited directly as discrete programming language source code from a traditional text-based source code editor, such as Visual Studio Code. These discrete files can also be consumed by other, third-party CI/CD systems like Maven and Jenkins. They can be edited, and they can be imported back into vRO workflow XML files. ‘Diffs’ and changes on the resulting code files are easily observed and tied to SCCS version numbers and releases, and can easily be merged and branched through normal software engineering development practices.

Easy Deploy for NSX Advanced Load Balancing

Easy Deploy for NSX Advanced Load Balancer (formerly Avi Networks) Fling is a virtual appliance that helps you deploy Avi in a handful of clicks!
This will enable you to leverage the power of multi-cloud application services platform that includes load balancing, web application firewall, container ingress, and application analytics across any cloud. No extensive knowledge required as it’s meant to make demo, training and proof-of-concept (POC) easy!

Features

  • A familiar VMware Clarity User Interface
  • Automatically deploy an Avi Controller and Avi Service Engines
  • Seamless integration with your VMware Cloud on AWS environment (with AVS and GCVE support coming soon!)
  • Option to deploy sample app that leverages Avi load balancing

For more information, read this blog post: New Fling: Easy Deploy for NSX Load Balancing a.k.a EasyAvi

Changelog

1.2.5

  • New Avi release supported – 20.1.5

1.2.4

  • Fixed SDDC conflict – what if you want to redeploy on the same sddc with another EasyAvi
  • Fixes “Output link /avi at the end does not work” issue
  • Destroy.sh – avoid TF error when trying to delete CL
  • Fixed “Typo in the outputs Advanced pplication Private IP Address”
  • Check for vCenter API connectivity before starting TF
  • Hide the button “DFW – Update NSX exclusion list with SE(s)”
  • Hide Domain Name field in the UI

1.2.3

  • Changed getMypublic.sh by beforeTf.sh

1.2.2

  •  Fix typo in outputs

1.2.1

  • Hide Public IP in outputs if empty

1.2.0

  • MD5 Checksum
  • Remove cat sddc.json from logs
  • Auto Apply
  • Auto routing to Step 3

1.1.0

  • Minor fixes

1.0.0

  • First Release

Updates

Virtual Machine Compute Optimizer

The Virtual Machine Computer Optimizer (VMCO) is a Powershell script and module that uses the PowerCLI module to capture information about the Hosts and VMS running in your vSphere environment, and reports back on whether the VMs are configured optimally based on the Host CPU and memory. It will flag a VM as “TRUE” if it is optimized and “FALSE” if it is not. For non-optimized VMs, a recommendation is made that will keep the same number of vCPUs currently configured, with the optimal number of virtual cores and sockets.

Changelog

Version 3.0.0

  • Script will install or update the required modules (VMCO and PowerCLI). The script is now a single script that acts as the easy button to walk through the module installs, connecting to a vCenter, and exporting the results.

Community Networking Driver for ESXi

The Community Networking Driver for ESXi fling provides the user the ability to use usually not supported by ESXi.

Changelog

April 08, 2021 – v1.1

Net-Community-Driver_1.1.0.0-1vmw.700.1.0.15843807_17858744.zip
md5: 587d7d408184c90f6baf4204bb309171

  • Resolve issue when using Intel vPro which can cause ESXi PSOD

ESXi Arm Edition

ESXi on arm based system, nuf said!

Changelog

April 02, 2021 – v1.3

Note: Upgrade is NOT possible, only fresh installation is supported. If you select “Preserve VMFS” option, you can re-register your existing Virtual Machines.

  • Improved hardware compatibility (various bug fixes/enhancements)
  • Add support for Experimental Ampere Altra (single socket systems only) (please see Requirements for more details)
  • ACPI support for virtual machines
  • NVMe and PVSCSI boot support in vEFI
  • Workaround for ISO boot on some Arm servers
  • Address VMM crash with newer guest OSes and Neoverse N1-based systems
  • Improved guest interrupt controller virtualization
  • Improved (skeletal) PMU virtualization
  • Improved big endian VM support

Build 17839012
VMware-VMvisor-Installer-7.0.0-17839012.aarch64.iso

vSphere HTML5 Web Client

Event though we have had the html5 web client around for a while they’re still using the html5 fling to test some new features!

Changelog

Fling 5.0 – build 15670023

Updated the instructions with the new location of some files and services for the HTML5 client fling v6.pdf

Horizon Session Recording

The Horizon Session Recording tool allows for (on-demand) recording of Horizon sessions.

Changelog

Version 2.2.5

  • Added support for > Horizon 8.1

vSphere Mobile Client

Personally I don’t have a use for it but I do like the idea of being able to manage my vSphere from a mobile device using the vSphere Mobile Client.

Changelog

Version 2.2.0 Update:

New features:

  • Add filtering by severity options for Alarm and Events
  • Add windows key button in the virtual console keyboard for key combos

Improvements:

  • Improve VM console stability on device rotation
  • Add missing back button on the login pages
  • Update app logo icon and splash screens

Version 2.1.0 Update:

Improvements:

  • Compatibility with some ESXi versions using certain licenses has been improved. Operations should now work against those hosts.

Version 2.0.0 Update:

New features:

  • Introduction of VMware Cloud with VMware on AWS. Access your cloud vCenter servers from within the mobile app.
  • VM details page: navigation to related objects now possible

Improvements:

  • Virtual Machine details page now loads faster when it’s powered off
  • Fixed an issue where the app would show two spinners when navigating between views

Workspace ONE Access Migration Tool

Workspace ONE Access Migration Tool helps ease migration of Apps from one WS1 Access tenant to another (on-premises to SaaS or SaaS to SaaS) and use cases that require mirroring one tenant to another (for setting up UAT from PROD or vice versa) by providing capabilities listed below

Changelog

Version 1.0.0.24

  • Migrate App Entitlements (groups only)
  • New Logo and UI branding
  • Bug fixes

VMware OS Optimization Tool

Optimize you must, use you should OSOT!

Changelog

April 5, b2003

  • Resolved bug where Windows Store Apps were being removed even though they were being selected to be kept. This included changing the filter condition for Remove All Windows built-in apps.

SDDC Import/Export for VMware Cloud on AWS

The SDDC Import/Export for VMware Cloud on AWS tool enables you to save and restore your VMware Cloud on AWS (VMC) Software-Defined Data Center (SDDC) networking and security configuration.

Changelog

Version 1.4

  • New feature – on-prem NSX-T DFW configuration export, import into VMC on AWS
  • New feature – on-prem vCenter folder structure export, import into VMC on AWS
  • New feature – Indented JSON output for easier reading
  • Bugfix – Bumped minimum Python version to the actual requirement of 3.6
  • Bugfix – Fixed issue where the exception block of a try/except on GET calls errored

VMware Event Broker Appliance

The VMware Event Broker Appliance (VEBA) Fling enables customers to unlock the hidden potential of events in their SDDC to easily event-driven automation based on vCenter Server Events and take vCenter Server Events to the next level! Extending vSphere by easily triggering custom or prebuilt actions to deliver powerful integrations within your datacenter across public cloud has never been more easier before.

Changelog

VMware Event Broker Appliance (VEBA) v0.6 is now available!

Creating a RDS farm using the Python module for VMware Horizon

One of the goals and hopes I had with my 100DaysOfCode (I am writing this on day 100!) was that the Horizon REST api’s to create desktop pools and RDS farms would have been available at the end. Only half of that came out and with Horizon 8 2103 we can finally create a RDS farm using those rest api’s. I have decided to add this to the Python module based on a dictionary that the user sends to the new_farm method. I could still add a fully fetched function but that would require a lot of arguments and using **kwargs is an option but than the user would still need to find out what to use.

First I will need to know what json data I actually need, let’s have a look at the api explorer page to get a grip on this

{
  "access_group_id": "6fd4638a-381f-4518-aed6-042aa3d9f14c",
  "automated_farm_settings": {
    "customization_settings": {
      "ad_container_rdn": "CN=Computers",
      "cloneprep_customization_settings": {
        "post_synchronization_script_name": "cloneprep_postsync_script",
        "post_synchronization_script_parameters": "p1 p2 p3",
        "power_off_script_name": "cloneprep_poweroff_script",
        "power_off_script_parameters": "p1 p2 p3",
        "priming_computer_account": "a219420d-4799-4517-8f78-39c74c7c4efc"
      },
      "instant_clone_domain_account_id": "6f85b3a5-e7d0-4ad6-a1e3-37168dd1ed51",
      "reuse_pre_existing_accounts": false
    },
    "enable_provisioning": true,
    "max_session_type": "LIMITED",
    "max_sessions": 50,
    "min_ready_vms": 0,
    "nics": [
      {
        "network_interface_card_id": "c9896e51-48a2-4d82-ae9e-a0246981b473",
        "network_label_assignment_specs": [
          {
            "enabled": true,
            "max_label": 1,
            "max_label_type": "LIMITED",
            "network_label_name": "vm-network"
          }
        ]
      }
    ],
    "pattern_naming_settings": {
      "max_number_of_rds_servers": 5,
      "naming_pattern": "vm-{n}-sales"
    },
    "provisioning_settings": {
      "base_snapshot_id": "snapshot-1",
      "datacenter_id": "datacenter-1",
      "host_or_cluster_id": "domain-s425",
      "im_stream_id": "6f85b3a5-e7d0-4ad6-a1e3-37168dd1ed51",
      "im_tag_id": "3d45b3a5-e7d0-4ad6-a1e3-37168dd1ed51",
      "parent_vm_id": "vm-2",
      "resource_pool_id": "resgroup-1",
      "vm_folder_id": "group-v1"
    },
    "stop_provisioning_on_error": true,
    "storage_settings": {
      "datastores": [
        {
          "datastore_id": "datastore-1"
        }
      ],
      "replica_disk_datastore_id": "datastore-1",
      "use_separate_datastores_replica_and_os_disks": false,
      "use_view_storage_accelerator": false,
      "use_vsan": false
    },
    "transparent_page_sharing_scope": "VM",
    "vcenter_id": "f148f3e8-db0e-4abb-9c33-7e5205ccd360"
  },
  "description": "Farm Description",
  "display_name": "ManualFarm",
  "display_protocol_settings": {
    "allow_users_to_choose_protocol": true,
    "default_display_protocol": "PCOIP",
    "grid_vgpus_enabled": true,
    "session_collaboration_enabled": false
  },
  "enabled": true,
  "load_balancer_settings": {
    "cpu_threshold": 10,
    "disk_queue_length_threshold": 15,
    "disk_read_latency_threshold": 10,
    "disk_write_latency_threshold": 15,
    "include_session_count": true,
    "memory_threshold": 10
  },
  "name": "ManualFarm",
  "rds_server_ids": [
    "5134796a-322g-5fe5-343f-4daa5d25ebfe",
    "2a43f96c-102b-4ed3-953f-35deg43d43b0ge"
  ],
  "server_error_threshold": 0,
  "session_settings": {
    "disconnected_session_timeout_minutes": 5,
    "disconnected_session_timeout_policy": "NEVER",
    "empty_session_timeout_minutes": 5,
    "empty_session_timeout_policy": "AFTER",
    "logoff_after_timeout": false,
    "pre_launch_session_timeout_minutes": 10,
    "pre_launch_session_timeout_policy": "AFTER"
  },
  "type": "MANUAL",
  "use_custom_script_for_load_balancing": false
}

This also includes some that are not required so for my own farm I settled with this json. This is for an Instant Clone farm.

{
    "access_group_id": "6fd4638a-381f-4518-aed6-042aa3d9f14c",
    "automated_farm_settings": {
        "customization_settings": {
            "ad_container_rdn": "OU=Pod1,OU=RDS,OU=VMware,OU=EUC",
            "instant_clone_domain_account_id": "6f85b3a5-e7d0-4ad6-a1e3-37168dd1ed51",
            "reuse_pre_existing_accounts": true
        },
        "enable_provisioning": false,
        "max_session_type": "LIMITED",
        "max_sessions": 50,
        "min_ready_vms": 1,
        "pattern_naming_settings": {
            "max_number_of_rds_servers": 2,
            "naming_pattern": "vm-{n}-sales"
        },
        "provisioning_settings": {
            "base_snapshot_id": "snapshot-1",
            "datacenter_id": "datacenter-1",
            "host_or_cluster_id": "domain-s425",
            "parent_vm_id": "vm-2",
            "resource_pool_id": "resgroup-1",
            "vm_folder_id": "group-v1"
        },
        "stop_provisioning_on_error": true,
        "storage_settings": {
            "datastores": [
                {
                    "datastore_id": "datastore-1"
                }
            ],
            "use_separate_datastores_replica_and_os_disks": false,
            "use_view_storage_accelerator": false,
            "use_vsan": false
        },
        "transparent_page_sharing_scope": "VM",
        "vcenter_id": "f148f3e8-db0e-4abb-9c33-7e5205ccd360"
    },
    "description": "demo_farm",
    "display_name": "demo_farm",
    "display_protocol_settings": {
        "allow_users_to_choose_protocol": true,
        "default_display_protocol": "BLAST",
        "grid_vgpus_enabled": false,
        "session_collaboration_enabled": true
    },
    "enabled": false,
    "load_balancer_settings": {
        "cpu_threshold": 10,
        "disk_queue_length_threshold": 15,
        "disk_read_latency_threshold": 10,
        "disk_write_latency_threshold": 15,
        "include_session_count": true,
        "memory_threshold": 10
    },
    "name": "demo_farm",
    "server_error_threshold": 0,
    "session_settings": {
        "disconnected_session_timeout_minutes": 5,
        "disconnected_session_timeout_policy": "NEVER",
        "empty_session_timeout_minutes": 5,
        "empty_session_timeout_policy": "AFTER",
        "logoff_after_timeout": false,
        "pre_launch_session_timeout_minutes": 10,
        "pre_launch_session_timeout_policy": "AFTER"
    },
    "type": "AUTOMATED",
    "use_custom_script_for_load_balancing": false
}

As said I send a dictionary to the method so let’s import data into a dict called data and I will print it to screen. The dictionary needs to follow this specific order of lines so that’s why a json is very useful to start with.

with open('/mnt/d/homelab/farm.json') as f:
    data = json.load(f)

As you can see in both the json and the output there’s a lot of things we can change and some things that we need to change lik id’s for all the components like vCenter, base vm, base snapshot and more. First I need the access_group_id this can be retreived using the get_local_access_groups method. For all of these I will also set the variable in the dictionary that we need.

local_access_group = next(item for item in (config.get_local_access_groups()) if item["name"] == "Root")
data["access_group_id"] = local_access_group["id"]

Than it’s time for the Instant Clone Admin id

ic_domain_account = next(item for item in (config.get_ic_domain_accounts()) if item["username"] == "administrator")
data["automated_farm_settings"]["customization_settings"]["instant_clone_domain_account_id"] = ic_domain_account["id"]

For the basevm and snapshot id’s I used the same method but a bit differently as I had already used this method in another script

vcenters = monitor.virtual_centers()
vcid = vcenters[0]["id"]
dcs = external.get_datacenters(vcenter_id=vcid)
dcid = dcs[0]["id"]

base_vms = external.get_base_vms(vcenter_id=vcid,datacenter_id=dcid,filter_incompatible_vms=True)

base_vm = next(item for item in base_vms if item["name"] == "srv2019-p1-2020-10-13-08-44")
basevmid=base_vm["id"]

base_snapshots = external.get_base_snapshots(vcenter_id=vcid, base_vm_id=base_vm["id"])

base_snapshot = next(item for item in base_snapshots if item["name"] == "Created by Packer")

snapid=base_snapshot["id"]
data["automated_farm_settings"]["provisioning_settings"]["base_snapshot_id"] = snapid
data["automated_farm_settings"]["provisioning_settings"]["parent_vm_id"] = basevmid

Host or cluster id

host_or_clusters = external.get_hosts_or_clusters(vcenter_id=vcid, datacenter_id=dcid)
for i in host_or_clusters:
    if (i["details"]["name"]) == "Cluster_Pod1":
        host_or_cluster = i
data["automated_farm_settings"]["provisioning_settings"]["host_or_cluster_id"] = host_or_cluster["id"]

Resource Pool

resource_pools = external.get_resource_pools(vcenter_id=vcid, host_or_cluster_id=host_or_cluster["id"])
for i in resource_pools:
    # print(i)
    if (i["type"] == "CLUSTER"):
        resource_pool = i
data["automated_farm_settings"]["provisioning_settings"]["resource_pool_id"] = resource_pool["id"]

VM folder again is a bit different as I have to get the id from one of the children objects

vm_folders = external.get_vm_folders(vcenter_id=vcid, datacenter_id=dcid)
for i in vm_folders:
    children=(i["children"])
    for ii in children:
        # print(ii["name"])
        if (ii["name"]) == "Pod1":
            vm_folder = i
data["automated_farm_settings"]["provisioning_settings"]["vm_folder_id"] = vm_folder["id"]

Datacenter and vcenter id’s I already had to grab for the base vm and base snapshot so I can just add them

data["automated_farm_settings"]["provisioning_settings"]["datacenter_id"] = dcid
data["automated_farm_settings"]["vcenter_id"] = vcid

Datastores is a bit more funky as there can be multiple so I needed to create a list first and than populate that based on the name of the datastores I have.

datastore_list = []
datastores = external.get_datastores(vcenter_id=vcid, host_or_cluster_id=host_or_cluster["id"])
for i in datastores:
    # print(i)
    if (i["name"] == "VDI-500") or i["name"] == "VDI-200":
        ds = {}
        ds["datastore_id"] = i["id"]
        datastore_list.append(ds)
data["automated_farm_settings"]["storage_settings"]["datastores"] = datastore_list

For my final script I put them in a bit different order and I decided to change a whole lot more options but if you have your json perfected this shouldn’t always be required. Also take note that for true/false in the json that I use the True/False from python.

import requests, getpass, urllib, json

import vmware_horizon

requests.packages.urllib3.disable_warnings()
url = input("URL\n")

username = input("Username\n")

domain = input("Domain\n")

pw = getpass.getpass()

hvconnectionobj = vmware_horizon.Connection(username = username,domain = domain,password = pw,url = url)
hvconnectionobj.hv_connect()
print("connected")

monitor = obj=vmware_horizon.Monitor(url=hvconnectionobj.url, access_token=hvconnectionobj.access_token)
external=vmware_horizon.External(url=hvconnectionobj.url, access_token=hvconnectionobj.access_token)
inventory=vmware_horizon.Inventory(url=hvconnectionobj.url, access_token=hvconnectionobj.access_token)
config=vmware_horizon.Config(url=hvconnectionobj.url, access_token=hvconnectionobj.access_token)

with open('/mnt/d/homelab/farm.json') as f:
    data = json.load(f)

vcenters = monitor.virtual_centers()
vcid = vcenters[0]["id"]
dcs = external.get_datacenters(vcenter_id=vcid)
dcid = dcs[0]["id"]

base_vms = external.get_base_vms(vcenter_id=vcid,datacenter_id=dcid,filter_incompatible_vms=True)

base_vm = next(item for item in base_vms if item["name"] == "srv2019-p1-2020-10-13-08-44")
basevmid=base_vm["id"]

base_snapshots = external.get_base_snapshots(vcenter_id=vcid, base_vm_id=base_vm["id"])

base_snapshot = next(item for item in base_snapshots if item["name"] == "Created by Packer")

snapid=base_snapshot["id"]

host_or_clusters = external.get_hosts_or_clusters(vcenter_id=vcid, datacenter_id=dcid)
for i in host_or_clusters:
    if (i["details"]["name"]) == "Cluster_Pod1":
        host_or_cluster = i

resource_pools = external.get_resource_pools(vcenter_id=vcid, host_or_cluster_id=host_or_cluster["id"])
for i in resource_pools:
    # print(i)
    if (i["type"] == "CLUSTER"):
        resource_pool = i

vm_folders = external.get_vm_folders(vcenter_id=vcid, datacenter_id=dcid)
for i in vm_folders:
    children=(i["children"])
    for ii in children:
        # print(ii["name"])
        if (ii["name"]) == "Pod1":
            vm_folder = i

datastore_list = []
datastores = external.get_datastores(vcenter_id=vcid, host_or_cluster_id=host_or_cluster["id"])
for i in datastores:
    # print(i)
    if (i["name"] == "VDI-500") or i["name"] == "VDI-200":
        ds = {}
        ds["datastore_id"] = i["id"]
        datastore_list.append(ds)

local_access_group = next(item for item in (config.get_local_access_groups()) if item["name"] == "Root")
ic_domain_account = next(item for item in (config.get_ic_domain_accounts()) if item["username"] == "administrator")

data["access_group_id"] = local_access_group["id"]
data["automated_farm_settings"]["customization_settings"]["ad_container_rdn"] = "OU=Pod1,OU=RDS,OU=VMware,OU=EUC"
data["automated_farm_settings"]["customization_settings"]["reuse_pre_existing_accounts"] = True
data["automated_farm_settings"]["customization_settings"]["instant_clone_domain_account_id"] = ic_domain_account["id"]
data["automated_farm_settings"]["enable_provisioning"] = False
data["automated_farm_settings"]["max_sessions"] = 50
data["automated_farm_settings"]["min_ready_vms"] = 3
data["automated_farm_settings"]["pattern_naming_settings"]["max_number_of_rds_servers"] = 4
data["automated_farm_settings"]["pattern_naming_settings"]["naming_pattern"] = "farmdemo-{n:fixed=3}"
data["automated_farm_settings"]["provisioning_settings"]["base_snapshot_id"] = snapid
data["automated_farm_settings"]["provisioning_settings"]["parent_vm_id"] = basevmid
data["automated_farm_settings"]["provisioning_settings"]["host_or_cluster_id"] = host_or_cluster["id"]
data["automated_farm_settings"]["provisioning_settings"]["resource_pool_id"] = resource_pool["id"]
data["automated_farm_settings"]["provisioning_settings"]["vm_folder_id"] = vm_folder["id"]
data["automated_farm_settings"]["provisioning_settings"]["datacenter_id"] = dcid
data["automated_farm_settings"]["stop_provisioning_on_error"] = True
data["automated_farm_settings"]["storage_settings"]["datastores"] = datastore_list
data["automated_farm_settings"]["transparent_page_sharing_scope"] = "GLOBAL"
data["automated_farm_settings"]["vcenter_id"] = vcid
data["description"] = "Python_demo_farm"
data["display_name"] = "Python_demo_farm"
data["display_protocol_settings"]["allow_users_to_choose_protocol"] = True
data["display_protocol_settings"]["default_display_protocol"] = "BLAST"
data["display_protocol_settings"]["session_collaboration_enabled"] = True
data["enabled"] = False
data["load_balancer_settings"]["cpu_threshold"] = 12
data["load_balancer_settings"]["disk_queue_length_threshold"] = 16
data["load_balancer_settings"]["disk_read_latency_threshold"] = 12
data["load_balancer_settings"]["disk_write_latency_threshold"] = 16
data["load_balancer_settings"]["include_session_count"] = True
data["load_balancer_settings"]["memory_threshold"] = 12
data["name"] = "Python_demo_farm"
data["session_settings"]["disconnected_session_timeout_minutes"] = 5
data["session_settings"]["disconnected_session_timeout_policy"] = "NEVER"
data["session_settings"]["empty_session_timeout_minutes"] = 6
data["session_settings"]["empty_session_timeout_policy"] = "AFTER"
data["session_settings"]["logoff_after_timeout"] = False
data["session_settings"]["pre_launch_session_timeout_minutes"] = 12
data["session_settings"]["pre_launch_session_timeout_policy"] = "AFTER"
data["type"] = "AUTOMATED"

inventory.new_farm(farm_data=data)

end=hvconnectionobj.hv_disconnect()
print(end)

How does this look? Actually you don’t see a lot happening but the farm will have been created

As always the script can be found on my github in the examples folder together with the json file.

With this I am closing my 100DaysOfCode challenge but I pledge to keep maintaining the python module and I will extend it when new REST api calls arrive for VMware Horizon.

The VMware Labs flings monthly for March 2021 – New Website

A day late but never late than never, this is your monthly overview with all the latest and greatest VMware flings. The flings site received a new fresh look head out to https://flings.vmware.com to have a look yourself. I am not sure if the (are they new?) tags for updated or new flings are okay yet as one has been marked updated but it seems to be new while one new one I could find a blog post for from last month but I missed it for my overview, not sure what happened there. Overall I see four new flings and ten received an update.

New Releases

Configuration Wizard for Nuance PowerMic

vRealize Automation Code Stream CLI

Hillview: Distributed Data Visualization

SDDC Import/Export for VMware Cloud on AWS

Updates

Workspace ONE Mobileconfig Importer

Workspace One UEM Workload Migration Tool

vSAN Hardware Compatibility List Checker

Vmss2core

vRealize Build Tools

Horizon Cloud Pod Architecture Tools

Workspace ONE App Analyzer for macOS

App Volumes Packaging Utility

DoD Security Technical Implementation Guide(STIG) ESXi VIB

VMware OS Optimization Tool

New Releases

Configuration Wizard for Nuance PowerMic

Nuance PowerMics are the leading dictation device used in Healthcare today.
This handy standalone Fling will assist in determining the optimal PowerMic configuration for a specific customer environment.

To determine the configuration that works best for you, we need to know a few details about the customer environment:

  • Endpoint type
  • Endpoint vendor
  • Endpoint operating system
  • Single/nested mode
  • Horizon protocol

The PowerMic Configuration Wizard will then provide the specific settings for optimal PowerMic performance and accuracy in the customers environment.

vRealize Automation Code Stream CLI

vRealize Automation Code Stream CLI is a command line tool written in Go to interact with the vRealize Automation Code Stream APIs. This Fling is written to help automate Code Stream and provide a simple way to migrate content between Code Stream instances and projects.

  • Import and Export Code Stream artefacts such as Pipelines, Variables, Endpoints
  • Perform CRUD operations on Code Stream artefacts such as Pipelines, Variables, Endpoints
  • Trigger Executions of Pipelines

Hillview: Distributed Data Visualization

Hillview is a simple cloud-based spreadsheet program for browsing large data collections. The data manipulated is read-only. Users can sort, find, filter, transform, query, zoom-in/out, and chart data. Operations are performed using direct manipulation in the GUI. Hillview is designed to work on very large data sets (billions of rows). Hillview can import data from a variety of sources: CSV files, ORC files, Parquet files, databases, parallel databases; new connectors can be added with relatively little effort. Hillview takes advantage of all the cores of the worker machines for fast visualizations.

Hillview is a distributed system, composed of two pieces:

  • A distributed set of one or many workers, which should be installed close to the data (e.g., on the machines that host the data).
  • A front-end service that runs a web server and aggregates data from all workers.

The source code of Hillview is available as an open-source project with an Apache-2 license from Hillview’s github repository. For any questions, feature requests or bug reports please file an issue on github.

SDDC Import/Export for VMware Cloud on AWS

The SDDC Import/Export for VMware Cloud on AWS tool enables you to save and restore your VMware Cloud on AWS (VMC) Software-Defined Data Center (SDDC) networking and security configuration.

There are many situations when customers want to migrate from an existing SDDC to a different one. While HCX addresses the data migration challenge, this tool offers customers the ability to copy the configuration from a source to a destination SDDC.

A few example migration scenarios are:

  • SDDC to SDDC migration from bare-metal (i3) to a different bare-metal type (i3en)
  • SDDC to SDDC migration from VMware-based org to an AWS-based org
  • SDDC to SDDC migration from region (i.e. London) to a different region (i.e. Dublin).

Other use cases are:

  • Backups – save the entire SDDC configuration
  • Lab purposes – customers or partners might want to deploy SDDCs with a pre-populated configuration.
  • DR purposes – deploy a pre-populated configuration in conjunction with VMware Site Recovery or VMware Cloud Disaster Recovery

Detailed instructions can be found on the Instructions tab, in the README.md included in the Zip file or on Patrick’s blog (http://www.patrickkremer.com/sddc-import-export/).

Details about the use cases and origins of the project can be found on Nico’s blog (https://nicovibert.com/2021/02/08/fling-sddc-import-export-for-vmware-cloud-on-aws/).

Updates

Workspace ONE Mobileconfig Importer

The Workspace ONE mobileconfig Importer gives you the ability to import existing mobileconfig files directly into a Workspace ONE UEM environment as a Custom Settings profile, import app preference plist files in order to created managed preference profiles, and to create new Custom Settings profiles from scratch. When importing existing configuration profiles, the tool will attempt to separate each PayloadContent dictionary into a separate payload for the Workspace ONE profile.

Changelog

Version 1.1

  • Support for Big Sur
  • Updated icon

Workspace One UEM Workload Migration Tool

Quite a few updates for this fling already!

The Workspace One UEM Workload Migration Tool allows a seamless migration of Applications and Device configurations between different Workspace One UEM environments. With the push of a button, workloads move from UAT to Production, instead of having to manually enter the information or upload files manually. Therefore, decreasing the time to move data between Dev/UAT environments to Production.

Changelog

Version 2.1.0

  • Fixed app upload issues for Workspace One UEM 1910+
  • Fixed profile search issue for Workspace One UEM 1910+
  • Added profile update support
  • Added template folder structure creation
  • Updated Mac app to support notarization for Catalina

Version 2.0.1

  • Fixed Baseline Migration issue
  • Fixed Profile Errors not displaying in the UI

Version 2.0.0

  • Baseline Migration Support
  • MacOS application
  • UI refactoring to make bulk migrations easier
  • Added support for script detection with Win32 applications

Version 1.0.1

  • Fixed issue with expired credentials.

vSAN Hardware Compatibility List Checker

The vSAN Hardware Compatibility List Checker is a tool that verifies all installed storage adapters against the vSAN supported storage controller list. The tool will verify if the model, driver and firmware version of the storage adapter are supported.

Changelog

Version 2.2

  • Support multi-platforms for Windows, Linux and MacOS
  • Bug fixed

Vmss2core

Vmss2core is a tool to convert VMware checkpoint state files into formats that third party debugger tools understand. It can handle both suspend (.vmss) and snapshot (.vmsn) checkpoint state files (hereafter referred to as a ‘vmss file’) as well as both monolithic and non-monolithic (separate .vmem file) encapsulation of checkpoint state data.

Changelog

Version 1.0.1

  • Fixed running out of memory issues
  • Added support for more versions of Windows 10/Windows 2016

vRealize Build Tools

vRealize Build Tools provides tools to development and release teams implementing solutions based on vRealize Automation (vRA) and vRealize Orchestrator (vRO). The solution targets Virtual Infrastructure Administrators and Solution Developers working in parallel on multiple vRealize-based projects who want to use standard DevOps practices.

Changelog

Version 2.12.5 Update

  • [vRBT] Package installer – Add support for installation on a standalone vRO (not embedded) version 8.x with basic authentication
  • [vRA] Added catalog entitlements and examples to the vra-ng archetype
  • [vRO] Support / in Workflow name or path, by substituting it with dash (-) character.
  • [vRBT] Added http / socket timeouts support in the installer
  • [ABX] Support for ABX actions
  • [vRO] Support of placeholders in workflow description
  • [vRA] Import vRA8 custom resources before blueprints
  • [vROPS] Fixed policy import / export problem with vROPs 8.2, maintaining backward compatibility
  • [MVN] Fixed ussue with installer timeouts
  • [TS] vRO pkg – Adds support for slash in workflow path or name
  • [vRBT] Package installer – updated documentation, added checking of workflow input, writing of workflow error message to file, setting of installer exit code when executing of a workflow
  • [TS] Allow additional trigger events for policies trigered by the vcd mqtt plugin
  • [MVN] Fix Missing vRA Tenant After Successful package import
  • [MVN] Fix vROPS import fails on certain assets

Horizon Cloud Pod Architecture Tools

The Horizon Cloud Pod Architecture Tools mainly acts as a wrapper around the lmvutil for Horizon Cloud Pod Archtitecture.

Changelog

Version 1.1

Based on the customer requests, have added few more command line options for CSV reports generation and AD LDS data cleanup.

What’s New:

Adds support to cleanup stale global local entitlement assignments from ADAM DB.

  • Global AD LDS Command:
    adlds-analyzer.cmd –resolve-localpool-ga
  • Scans the cloud pod database and resolves the stale entries of local pool global assignments.
    Note: This resolves deleted local pool conflicts of current pod only. If dashboard session data load error or session search fails in a different pod, a scan and resolve has to be executed in that pod.
  • List of new commands added to Local AD LDS:
    adlds-analyzer.cmd –export-machine
  • All machine data exported as CSV file. Compatibility: Horizon 7.10 and above, 8.x
    adlds-analyzer.cmd –export-machine -pool=”DesktopPool1,DesktopPool2″
  • All machine data exported as CSV file. Use -pool= to filter machines by desktop pool name. Compatibility: Horizon 7.10 and above, 8.x
  • Spaces are not allowed in -pool= optional argument.
    adlds-analyzer.cmd –export-session
  • All local sessions data exported as CSV file. Compatibility: Horizon 7.10 and above, 8.x
    adlds-analyzer.cmd –export-session -pool=”PoolName1,PoolName2″ -farm=”FarmName1,FarmName2″
  • All local session data exported as CSV file. Use -pool= to filter sessions by desktop pool name. -farm= filter sessions by RDS farm name. Compatibility: Horizon 7.10 and above, 8.x
  • Spaces are not allowed in -pool= and -farm= optional argument.
    adlds-analyzer.cmd –check-apps-integrity
  • Scans and lists the stale application icons in local ADLDS instance.
    adlds-analyzer.cmd –check-apps-integrity -input=”AbsoluteFilePath1″,”AbsoluteFilePath2″
  • Reads the list of adam LDIF files in “-input=” for parsing and exports the stale application icons data to a file.
    adlds-analyzer.cmd –export-named-lic-users
  • Exports the utilized and un-utilized named license users list information.

Workspace ONE App Analyzer for macOS

The Workspace ONE macOS App Analyzer will determine any Privacy Permissions, Kernel Extensions, or System Extensions needed by an installed macOS application, and can be used to automatically create profiles in Workspace ONE UEM to whitelist those same settings when deploying apps to managed devices.

Changelog

Version 1.2.1

  • Fixed bug that caused crash with certain System Extension configurations

App Volumes Packaging Utility

This App Volumes Packaging Utility helps to package applications. With this fling, packagers can add the necessary metadata to MSIX app attach VHDs so they can be used alongside existing AV format packages. The MSIX format VHDs will require App Volumes 4, version 2006 or later and Windows 10, version 2004 or later.

Changelog

Version 1.2 Update

  • Fixed bugs found in internal testing.

DoD Security Technical Implementation Guide(STIG) ESXi VIB

The DoD Security Technical Implementation Guide (‘STIG’) ESXi VIB is a Fling that provides a custom VMware-signed ESXi vSphere Installation Bundle (‘VIB’) to assist in remediating Defense Information Systems Agency STIG controls for ESXi. This VIB has been developed to help customers rapidly implement the more challenging aspects of the vSphere STIG. These include the fact that installation is time consuming and must be done manually on the ESXi hosts. In certain cases, it may require complex scripting, or even development of an in-house VIB that would not be officially digitally signed by VMware (and therefore would not be deployed as a normal patch would). The need for a VMware-signed VIB is due to the system level files that are to be replaced. These files cannot be modified at a community supported acceptance level. The use of the VMware-signed STIG VIB provides customers the following benefits:

  • The ability to use vSphere Update Manager (‘VUM’) to quickly deploy the VIB to ESXi hosts (you cannot do this with a customer created VIB)
  • The ability to use VUM to quickly check if all ESXi hosts have the STIG VIB installed and therefore are also in compliance
  • No need to manually replace and copy files directly on each ESXi host in your environment
  • No need to create complex shell scripts that run each time ESXi boots to re-apply settings

Changelog

Update March 2021

  • New ESXi 7.0 STIG VIB release
  • Updated sshd_config file to meet the ESXi 7.0 Draft STIG which is also now the default config in 7.0 U2 with the exception of permitting root user logins.
  • Removed /etc/vmware/welcome file from VIB since it can be configured via the UI or PowerCLI now with issue.
  • Draft ESXi content can be found here: https://github.com/vmware/dod-compliance-and-automation/tree/master/vsphere/7.0/docs
  • See the updated Overview and Installation guide included in the download.

VMware OS Optimization Tool

No comments needed, just use the OS Optimization Tool when creating your golden images!

Changelog

March 2021, b2002

  • Fixed issue where the theme file was being updated by a Generalize task and the previously selected optimizations including wallpaper color were being lost.
  • The administrator username used during Generalize was not getting passed through properly to the unattend answer file. This resulted in a mismatch when using some languages versions of Windows.
  • Removed legacy code GPO Policy corruption
  • Removed CMD.exe box that displayed at logon.
  • Windows Store Apps were not being removed properly on Windows 10 version 20H2. Fixed the optimizations to cope with the differences introduced in this version.

Optimizations

Changed step Block all consumer Microsoft account user authentication to be unselected by default. When disabled this was causing failures to login to Edge and Windows store.

Changed the step Turn off Thumbnail Previews in File Explorer to be unselected by default. This was causing no thumbnails to show for store apps in search results.

Windows Update

On Non-Enterprise editions of Windows 10, KB4023057 installs a new application called Microsoft Update Health Tools: https://support.microsoft.com/en-us/topic/kb4023057-update-for-windows-10-update-service-components-fccad0ca-dc10-2e46-9ed1-7e392450fb3a. Added logic to ensure that the Windows Update Medic Service is disabled including after re-enabling and disabling Windows Updates using the Update tab.

Templates

Windows 8 and 8.1 templates have been removed from the list of built-in templates. To optimize these versions of Windows, use the separate download for version b1130.

Removed old Windows 10 templates from the Public Templates repository:

  • Windows 10 1809-2004-Server 2019
  • Windows 10 1507-1803-Server 2016

January 2021, b2001 Bug Fixes

  • All optimization entries have been added back into the main user template. This allows manual tuning and selection of all optimizations.
  • Fixed two hardware acceleration selections were not previously controlled by the Common Option for Visual Effect to disable hardware acceleration.

Optimize

  • During an Optimize, the optimization selections are automatically exported to a default json file (%ProgramData%\VMware\OSOT\OptimizedTemplateData.json).

Analyze

  • When an Analyze is run, if the default json file exists (meaning that this image has already been optimized), this is imported and used to select the optimizations and the Common Options selections with the previous choices.
  • If the default selections are required, on subsequent runs of the OS Optimization Tool, delete the default json file, relaunch the tool and run Analyze.

Command Line

  • The OptimizedTemplateData.json file can also be used from the command line with the -applyoptimization parameter.

Optimizations

  • Changed entries for Hyper-V services to not be selected by default. These services are required for VMs deployed onto Azure. Windows installation sets these to manual (trigger) so these so not cause any overhead on vSphere, when left with the default setting.

Powercli script to assign a dedicated Horizon machine to multiple users

Yesterday Robin Stolpe again reached out that he was having issues assigning multiple accounts to the same dedicated machine. He couldn’t get this running with the vmware.hv.helper and looking that with how it is implemented now it will probably never work. I decided to put together some of the functions I have used for ControlUp script based actions and some of my other work to put together the following script (that can be found on Github here.)

[CmdletBinding()]
Param
(
    [Parameter(Mandatory=$False,
    ParameterSetName="separatecredentials",
    HelpMessage='Enter a username' )]
    [ValidateNotNullOrEmpty()]
    [string] $Username,

    [Parameter(Mandatory=$false,
    ParameterSetName="separatecredentials",
    HelpMessage='Domain i.e. loft.lab' )]
    [string] $Domain,

    [Parameter(Mandatory=$false,
    ParameterSetName="separatecredentials",
    HelpMessage='Password in plain text' )]
    [string] $Password,

    [Parameter(Mandatory=$true,  HelpMessage='FQDN of the connectionserver' )]
    [ValidateNotNullOrEmpty()]
    [string] $ConnectionServerFQDN,

    [Parameter(Mandatory=$false,
    ParameterSetName="credsfile",
    HelpMessage='Path to credentials xml file' )]
    [ValidateNotNullOrEmpty()]
    [string] $Credentialfile,

    [Parameter(Mandatory=$false,  HelpMessage='username of the user to logoff (domain\user i.e. loft.lab\user1')]
    [ValidateNotNullOrEmpty()]
    [string[]] $TargetUsers,

    [Parameter(Mandatory=$false, HelpMessage='Name of the desktop pool the machine belongs to')]
  [string] $TargetPool,

    [Parameter(Mandatory=$false, HelpMessage='dns name of the machine the user is on i.d. lp-002.loft.lab')]
  [string] $TargetMachine,

    [Parameter(Mandatory=$false, HelpMessage='domain for the target users')]
  [string] $TargetDomain
)

if($Credentialfile -and ((test-path $Credentialfile) -eq $true)){
    try{
        write-host "Using credentialsfile"
        $credentials=Import-Clixml $Credentialfile
        $username=($credentials.username).split("\")[1]
        $domain=($credentials.username).split("\")[0]
        $secpw=$credentials.password
        $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secpw)
        $password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
    }
    catch{
        write-error -Message "Error importing credentials"
        break
    }
}
elseif($Credentials -and ((test-path $credentials) -eq $false)){
    write-error "Invalid Path to credentials file"
    break
}
elseif($username -and $Domain -and $Password){
    write-host "Using separate credentials"
}


function Get-HVDesktopPool {
    param (
        [parameter(Mandatory = $true,
        HelpMessage = "Displayname of the Desktop Pool.")]
        [string]$HVPoolName,
        [parameter(Mandatory = $true,
        HelpMessage = "The Horizon View Connection server object.")]
        [VMware.VimAutomation.HorizonView.Impl.V1.ViewObjectImpl]$HVConnectionServer
    )
    # Try to get the Desktop pools in this pod
    try {
        # create the service object first
        [VMware.Hv.QueryServiceService]$queryService = New-Object VMware.Hv.QueryServiceService
        # Create the object with the definiton of what to query
        [VMware.Hv.QueryDefinition]$defn = New-Object VMware.Hv.QueryDefinition
        # entity type to query
        $defn.queryEntityType = 'DesktopSummaryView'
        # Filter on the correct displayname
        $defn.Filter = New-Object VMware.Hv.QueryFilterEquals -property @{'memberName'='desktopSummaryData.displayName'; 'value' = "$HVPoolname"}
        # Perform the actual query
        [array]$queryResults= ($queryService.queryService_create($HVConnectionServer.extensionData, $defn)).results
        # Remove the query
        $queryService.QueryService_DeleteAll($HVConnectionServer.extensionData)
        # Return the results
        if (!$queryResults){
            write-host "Can't find $HVPoolName, exiting."
            exit
        }
        else {
            return $queryResults
        }
    }
    catch {
        write-host 'There was a problem retreiving the Horizon View Desktop Pool.'
    }
}

function Get-HVDesktopMachine {
    param (
        [parameter(Mandatory = $true,
        HelpMessage = "ID of the Desktop Pool.")]
        [VMware.Hv.DesktopId]$HVPoolID,
        [parameter(Mandatory = $true,
        HelpMessage = "Name of the Desktop machine.")]
        [string]$HVMachineName,
        [parameter(Mandatory = $true,
        HelpMessage = "The Horizon View Connection server object.")]
        [VMware.VimAutomation.HorizonView.Impl.V1.ViewObjectImpl]$HVConnectionServer
    )

    try {
        # create the service object first
        [VMware.Hv.QueryServiceService]$queryService = New-Object VMware.Hv.QueryServiceService
        # Create the object with the definiton of what to query
        [VMware.Hv.QueryDefinition]$defn = New-Object VMware.Hv.QueryDefinition
        # entity type to query
        $defn.queryEntityType = 'MachineDetailsView'
        # Filter so we get the correct machine in the correct pool
        $poolfilter = New-Object VMware.Hv.QueryFilterEquals -property @{'memberName'='desktopData.id'; 'value' = $HVPoolID}
        $machinefilter = New-Object VMware.Hv.QueryFilterEquals -property @{'memberName'='data.name'; 'value' = "$HVMachineName"}
        $filterlist = @()
        $filterlist += $poolfilter
        $filterlist += $machinefilter
        $filterAnd = New-Object VMware.Hv.QueryFilterAnd
        $filterAnd.Filters = $filterlist
        $defn.Filter = $filterAnd
        # Perform the actual query
        [array]$queryResults= ($queryService.queryService_create($HVConnectionServer.extensionData, $defn)).results
        # Remove the query
        $queryService.QueryService_DeleteAll($HVConnectionServer.extensionData)
        # Return the results
        if (!$queryResults){
            write-host "Can't find $HVPoolName, exiting."
            exit
        }
        else{
            return $queryResults
        }
    }
    catch {
        write-host 'There was a problem retreiving the Horizon View Desktop Pool.'
    }
}

function Get-HVUser {
    param (
        [parameter(Mandatory = $true,
        HelpMessage = "User loginname..")]
        [string]$HVUserLoginName,
        [parameter(Mandatory = $true,
        HelpMessage = "Name of the Domain.")]
        [string]$HVDomain,
        [parameter(Mandatory = $true,
        HelpMessage = "The Horizon View Connection server object.")]
        [VMware.VimAutomation.HorizonView.Impl.V1.ViewObjectImpl]$HVConnectionServer
    )

    try {
        # create the service object first
        [VMware.Hv.QueryServiceService]$queryService = New-Object VMware.Hv.QueryServiceService
        # Create the object with the definiton of what to query
        [VMware.Hv.QueryDefinition]$defn = New-Object VMware.Hv.QueryDefinition
        # entity type to query
        $defn.queryEntityType = 'ADUserOrGroupSummaryView'
        # Filter to get the correct user
        $userloginnamefilter = New-Object VMware.Hv.QueryFilterEquals -property @{'memberName'='base.loginName'; 'value' = $HVUserLoginName}
        $domainfilter = New-Object VMware.Hv.QueryFilterEquals -property @{'memberName'='base.domain'; 'value' = "$HVDomain"}
        $filterlist = @()
        $filterlist += $userloginnamefilter
        $filterlist += $domainfilter
        $filterAnd = New-Object VMware.Hv.QueryFilterAnd
        $filterAnd.Filters = $filterlist
        $defn.Filter = $filterAnd
        # Perform the actual query
        [array]$queryResults= ($queryService.queryService_create($HVConnectionServer.extensionData, $defn)).results
        # Remove the query
        $queryService.QueryService_DeleteAll($HVConnectionServer.extensionData)
        # Return the results
        if (!$queryResults){
            write-host "Can't find user $HVUserLoginName in domain $HVDomain, exiting."
            exit
        }
        else {
            return $queryResults
        }
    }
    catch {
        write-host 'There was a problem retreiving the user.'
    }
}

$hvserver1=connect-hvserver $ConnectionServerFQDN -user $username -domain $domain -password $password
$Services1= $hvServer1.ExtensionData

$desktop_pool=Get-HVDesktopPool -hvpoolname $TargetPool -HVConnectionServer $hvserver1

$poolid=$desktop_pool.id

$machine = get-hvdesktopmachine -HVConnectionServer $hvserver1 -HVMachineName $TargetMachine -HVPoolID $poolid
$machineid = $machine.id
$useridlist=@()

foreach ($targetuser in $TargetUsers){
    $user = Get-HVUser -HVConnectionServer $hvserver1 -hvdomain $TargetDomain -HVUserLoginName $targetUser
    $useridlist+=$user.id
}

$Services1.Machine.Machine_assignUsers($machineid, $useridlist)

So first I have 3 functions to get the Pool, the machine and users. With a foreach on the $Targetusers list I create a list of the userid’s that is required to use for the Machine_assignUsers function of the machine service.

Powercli script to (forcefully) log off Horizon users

A long time ago in a galaxy far far away I wrote this blog post to log a user off from their vdi session. Today I got an inquiry from Robin Stolpe that he was trying to make it a script with arguments instead if the menu’s but was having some issues with that. This gave me the chance to make it a bit nicer of a script with the option to user username/domain/password as credentials but also a credentialfile , optional forcefully logging off the users and with Robin’s requirements of being able to provide the exact username and the machine that user is working on.

The script can be ran like this:

D:\GIT\Scripts\logoff_user.ps1 -Credentialfile "D:\homelab\creds.xml" -TargetUser "loft.lab\m_wouter" -TargetMachine "lp-001.loft.lab" -ConnectionServerFQDN loftcbr01.loft.lab

or with credentials and the -force parameter

D:\GIT\Scripts\logoff_user.ps1 -TargetUser "loft.lab\m_wouter" -TargetMachine "lp-001.loft.lab" -ConnectionServerFQDN loftcbr01.loft.lab -Username m_wouter -domain loft.lab -password "HAHAHAHA" -force

Now let’s have a look how the script is build.

So I started with the parameters and for that I included 2 parameter sets so you can either choose to have the separate credentials or to use a credentials file.

[CmdletBinding()]
Param
(
    [Parameter(Mandatory=$False,
    ParameterSetName="separatecredentials",
    HelpMessage='Enter a username' )]
    [ValidateNotNullOrEmpty()]
    [string] $Username,

    [Parameter(Mandatory=$false,
    ParameterSetName="separatecredentials",
    HelpMessage='Domain i.e. loft.lab' )]
    [string] $Domain,

    [Parameter(Mandatory=$false,
    ParameterSetName="separatecredentials",
    HelpMessage='Password in plain text' )]
    [string] $Password,

    [Parameter(Mandatory=$true,  HelpMessage='FQDN of the connectionserver' )]
    [ValidateNotNullOrEmpty()]
    [string] $ConnectionServerFQDN,

    [Parameter(Mandatory=$false,
    ParameterSetName="credsfile",
    HelpMessage='Path to credentials xml file' )]
    [ValidateNotNullOrEmpty()]
    [string] $Credentialfile,

    [Parameter(Mandatory=$false, HelpMessage='Synchronise the local site only' )]
    [switch] $Force,

    [Parameter(Mandatory=$false,  HelpMessage='username of the user to logoff (domain\user i.e. loft.lab\user1')]
    [ValidateNotNullOrEmpty()]
    [string] $TargetUser,

    [Parameter(Mandatory=$false, HelpMessage='dns name of the machine the user is on i.d. lp-002.loft.lab')]
  [string] $TargetMachine
)

Than I check if a credential file was supplied and if I can actually import it

if($Credentialfile -and ((test-path $Credentialfile) -eq $true)){
    try{
        write-host "Using credentialsfile"
        $credentials=Import-Clixml $Credentialfile
        $username=($credentials.username).split("\")[1]
        $domain=($credentials.username).split("\")[0]
        $secpw=$credentials.password
        $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secpw)
        $password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
    }
    catch{
        write-error -Message "Error importing credentials"
        break
    }
}
elseif($Credentials -and ((test-path $credentials) -eq $false)){
    write-error "Invalid Path to credentials file"
}
elseif($username -and $Domain -and $Password){
    write-host "Using separate credentials"
}

The file doesn’t exist:

Or an error importing the xml (duh, what do you think what happoens when you use a json instead of xml, fool!)

Then it’s a matter of logging in, performing a query and checking if there’s really a session for this user. As you can see I am using machineOrRDSServerDNS so it should also work for RDS sessions.

$hvserver1=connect-hvserver $ConnectionServerFQDN -user $username -domain $domain -password $password
$Services1= $hvServer1.ExtensionData

$queryService = New-Object VMware.Hv.QueryServiceService
$sessionfilterspec = New-Object VMware.Hv.QueryDefinition
$sessionfilterspec.queryEntityType = 'SessionLocalSummaryView'
$sessionfilter1= New-Object VMware.Hv.QueryFilterEquals
$sessionfilter1.membername='namesData.userName'
$sessionfilter1.value=$TargetUser
$sessionfilter2= New-Object VMware.Hv.QueryFilterEquals
$sessionfilter2.membername='namesData.machineOrRDSServerDNS'
$sessionfilter2.value=$TargetMachine
$sessionfilter=new-object vmware.hv.QueryFilterAnd
$sessionfilter.filters=@($sessionfilter1, $sessionfilter2)
$sessionfilterspec.filter=$sessionfilter
$session=($queryService.QueryService_Create($Services1, $sessionfilterspec)).results
$queryService.QueryService_DeleteAll($services1)
if($session.count -eq 0){
    write-host "No session found for $targetuser on $targetmachine"
    break
}

And last but not least logging the user of with or without the -force option

if($Force){
    write-host "Forcefully logging off $targetUser from $targetmachine"
    $Services1.Session.Session_Logoffforced($session.id)
}
else{
    write-host "Logging off $targetUser from $targetmachine"
    try{
        $Services1.Session.Session_Logoff($session.id)
    }
    catch{
        write-error "error logging the user off, maybe the sessions was locked. Try with -force"
    }
}

This session was locked

So let’s force that thing

And here’s the entire script but you can also find it on my github.

[CmdletBinding()]
Param
(
    [Parameter(Mandatory=$False,
    ParameterSetName="separatecredentials",
    HelpMessage='Enter a username' )]
    [ValidateNotNullOrEmpty()]
    [string] $Username,

    [Parameter(Mandatory=$false,
    ParameterSetName="separatecredentials",
    HelpMessage='Domain i.e. loft.lab' )]
    [string] $Domain,

    [Parameter(Mandatory=$false,
    ParameterSetName="separatecredentials",
    HelpMessage='Password in plain text' )]
    [string] $Password,

    [Parameter(Mandatory=$true,  HelpMessage='FQDN of the connectionserver' )]
    [ValidateNotNullOrEmpty()]
    [string] $ConnectionServerFQDN,

    [Parameter(Mandatory=$false,
    ParameterSetName="credsfile",
    HelpMessage='Path to credentials xml file' )]
    [ValidateNotNullOrEmpty()]
    [string] $Credentialfile,

    [Parameter(Mandatory=$false, HelpMessage='Synchronise the local site only' )]
    [switch] $Force,

    [Parameter(Mandatory=$false,  HelpMessage='username of the user to logoff (domain\user i.e. loft.lab\user1')]
    [ValidateNotNullOrEmpty()]
    [string] $TargetUser,

    [Parameter(Mandatory=$false, HelpMessage='dns name of the machine the user is on i.d. lp-002.loft.lab')]
  [string] $TargetMachine
)

if($Credentialfile -and ((test-path $Credentialfile) -eq $true)){
    try{
        write-host "Using credentialsfile"
        $credentials=Import-Clixml $Credentialfile
        $username=($credentials.username).split("\")[1]
        $domain=($credentials.username).split("\")[0]
        $secpw=$credentials.password
        $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secpw)
        $password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
    }
    catch{
        write-error -Message "Error importing credentials"
        break
    }
}
elseif($Credentials -and ((test-path $credentials) -eq $false)){
    write-error "Invalid Path to credentials file"
    break
}
elseif($username -and $Domain -and $Password){
    write-host "Using separate credentials"
}


$hvserver1=connect-hvserver $ConnectionServerFQDN -user $username -domain $domain -password $password
$Services1= $hvServer1.ExtensionData

$queryService = New-Object VMware.Hv.QueryServiceService
$sessionfilterspec = New-Object VMware.Hv.QueryDefinition
$sessionfilterspec.queryEntityType = 'SessionLocalSummaryView'
$sessionfilter1= New-Object VMware.Hv.QueryFilterEquals
$sessionfilter1.membername='namesData.userName'
$sessionfilter1.value=$TargetUser
$sessionfilter2= New-Object VMware.Hv.QueryFilterEquals
$sessionfilter2.membername='namesData.machineOrRDSServerDNS'
$sessionfilter2.value=$TargetMachine
$sessionfilter=new-object vmware.hv.QueryFilterAnd
$sessionfilter.filters=@($sessionfilter1, $sessionfilter2)
$sessionfilterspec.filter=$sessionfilter
$session=($queryService.QueryService_Create($Services1, $sessionfilterspec)).results
$queryService.QueryService_DeleteAll($services1)
if($session.count -eq 0){
    write-host "No session found for $targetuser on $targetmachine"
    break
}

if($Force){
    write-host "Forcefully logging off $targetUser from $targetmachine"
    $Services1.Session.Session_Logoffforced($session.id)
}
else{
    write-host "Logging off $targetUser from $targetmachine"
    try{
        $Services1.Session.Session_Logoff($session.id)
    }
    catch{
        write-error "error logging the user off, maybe the sessions was locked. Try with -force"
    }
}