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
}

 

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

[sta_anchor id=”xml” /]

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

[sta_anchor id=”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?

[sta_anchor id=”automated” /]

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.

 

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

[sta_anchor id=”nsxmob” /]

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

[sta_anchor id=”vrop” /]

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.

[sta_anchor id=”ednxcalb” /]

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

[sta_anchor id=”vmco” /]

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.

[sta_anchor id=”cndfesxi” /]

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

[sta_anchor id=”esxiae” /]

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

[sta_anchor id=”vhtml5wc” /]

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

[sta_anchor id=”hsr” /]

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

[sta_anchor id=”vmobc” /]

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

[sta_anchor id=”wsoneamt” /]

Workspace ONE Access Migration Tool

[sta_anchor id=”osot” /]

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.

[sta_anchor id=”sddciefvcoaws” /]

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

[sta_anchor id=”veba” /]

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

https://www.virtuallyghetto.com/2021/04/vmware-event-broker-appliance-veba-v0-6-is-now-available.html

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

[sta_anchor id=”cwfnpm” /]

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.

[sta_anchor id=”vracsc” /]

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

[sta_anchor id=”hvddv” /]

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.

[sta_anchor id=”sddciefvcoaws” /]

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

[sta_anchor id=”wsonemci” /]

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

[sta_anchor id=”wsoneuemwmt” /]

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.

[sta_anchor id=”vsanhclc” /]

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

[sta_anchor id=”vmss2core” /]

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

[sta_anchor id=”vrbt” /]

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

[sta_anchor id=”hcpat” /]

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.

[sta_anchor id=”wsoneaafm” /]

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

[sta_anchor id=”avpu” /]

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.

[sta_anchor id=”dodstigg” /]

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.

[sta_anchor id=”osot” /]

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"
    }
}

Pushing a new image using the VMware Horizon Python Module

One of the REST api calls that where added for Horizon 8 2012 was the ability to push images to Desktop Pools (sadly not for farms yet). This week I added that functionality to the VMware Horizon Python Module. Looking at the swagger UI these are the needed arguments:

So the source can be either the streams from Horizon Cloud or a regular vm/snapshot combo. For the time you will need to use some moment in epoch. The optional items for adding the virtual tpm, stop on error I have set the default for what they are listed. As logoff policy I have chosen to set a default in WAIT_FOR_LOGOFF.

For this blog posts I have to go with the vm/snapshot combo as I don’t have streams setup at the moment. First I need to connect:

import requests, getpass, urllib, json, operator, numpy, time
import vmware_horizon


requests.packages.urllib3.disable_warnings()
url="https://pod2cbr1.loft.lab"
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")

Than I open the ports for the classes I will be using

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)

Now let’s look at what the desktop_pool_push_image method needs

First I will grab the correct desktop pool, I will use Pod02-Pool02 this time. There are several ways to get the correct pool but I have chosen to use this one.

desktop_pools=inventory.get_desktop_pools()
desktop_pool = next(item for item in desktop_pools if item["name"] == "Pod02-Pool02")
poolid=desktop_pool["id"]

To get the VM and Snapshots I first need to get the vCenter and datacenter id’s

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

I created a new golden image last Friday and it has this name: W10-L-2021-03-19-17-27 so I need to get the compatible base vm’s and get the id for this one

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"] == "W10-L-2021-03-19-17-27")
basevmid=base_vm["id"]

I had Packer create a snapshot and I can get that in a similar way

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"]

I get the current time in epoch using the time module (google is your best friend to define a moment in the future in epoch)

current_time = time.time()

For this example I add all the arguments but if you don’t change fromt he defaults that’s not needed

inventory.desktop_pool_push_image(desktop_pool_id=poolid,parent_vm_id=basevmid,snapshot_id=snapid, start_time=current_time, add_virtual_tpm=False, stop_on_first_error=False, logoff_policy="FORCE_LOGOFF")

And closing the connection

end=hvconnectionobj.hv_disconnect()
print(end)

and when I now look at my desktop pool it’s pushing the new image

I have created a new folder on Github for examples and the script to deploy new images is the first example. I did move a couple of the names to variables so make ie better usable. You can find it here. Or see the code below this.

import requests, getpass, urllib, time
import vmware_horizon

requests.packages.urllib3.disable_warnings()

url                     = "https://pod2cbr1.loft.lab"
desktop_pool_name       = "Pod02-Pool01"
base_vm_name            = "W10-L-2021-03-19-17-27"
snapshot_name           = "Snap_2"

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)

desktop_pools=inventory.get_desktop_pools()
desktop_pool = next(item for item in desktop_pools if item["name"] == desktop_pool_name)
poolid=desktop_pool["id"]

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"] == base_vm_name)
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"] == snapshot_name)
snapid=base_snapshot["id"]

current_time = time.time()
inventory.desktop_pool_push_image(desktop_pool_id=poolid,parent_vm_id=basevmid,snapshot_id=snapid, start_time=current_time)

end=hvconnectionobj.hv_disconnect()
print(end)











 

 

The VMware Labs flings monthly for February 2021 – Reach alert!

It’s been a busy month on the flings front, no less than 17(!!) new releases and updated flings. This is how my browser tabs look:

If I have therm all correct there are 6 new releases and 10 updates (2 1 of which update without a changelog so a boo for that!) so this post is going to be a long one!

New Releases

Community NVMe Driver for ESXi

VMware Cloud Foundation Powernova

Workspace ONE Access Migration Tool

Sample Data Platform Deployment on Virtualized Cloud Infrastructure

Community Networking Driver for ESXi

Code Stream Concourse Integrator

Updates

ESXi Compatibility Checker

VMware Machine Learning Platform

Virtualized High Performance Computing Toolkit

Horizon Peripherals Intelligence

Workspace ONE App Analyzer for macOS

VMware OS Optimization Tool

Horizon Helpdesk Utility

HCIBench

Horizon Reach

Workspace ONE Discovery

App Volumes Migration Utility

New Releases

[sta_anchor id=”cndfe” /]

Community NVMe Driver for ESXi

This Fling is a collection of ESXi Native Drivers which enables ESXi to recognize and consume various NVMe-based storage devices. These devices are not officially on the VMware HCL and have been developed to enable and support the VMware Community.

Currently, this Fling provides an emulated NVMe driver for the Apple 2018 Intel Mac Mini 8,1 and the Apple 2019 Intel Mac Pro 7,1 allowing customers to use the local NVMe SSD with ESXi. This driver is packaged up as an Offline Bundle and is only activated when it detects ESXi has been installed on either an Apple Mac Mini or Apple Mac Pro.

[sta_anchor id=”vcfp” /]

VMware Cloud Foundation Powernova

VMware Cloud Foundation Powernova is a Fling built on top of VCF that provides the users the ability to perform Power Operations (Power ON, Power OFF) seamlessly across the entire inventory. It has a sleek UI to visualize the entire VCF inventory (which is the first of its kind for VCF) across the domains of VCF.

The UI is easy to use and elucidates the current Health and Power State of each node in the VCF inventory. Powernova lets the user work on the Power Operations on the components with domain specific inter dependencies automatically resolved.

Powernova also performs valid health checks on all nodes in the VCF inventory to ensure Power Operations are performed only on healthy nodes. Powernova takes minimal input (4 user defined inputs on their VCF system) and does all the magic for the users behind the scenes.

If any infrastructure maintenance activity, VCF migration activity, or power operations need to be performed only on specific domains in VCF, then Powernova is the one stop solution for all VCF users.

[sta_anchor id=”wsoamt” /]

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

Features
  • Copying of App Categories
  • Migrating Weblinks (3rd party IDP), icons as is
  • Creating a link to federated apps and copying the icons (to maintain the same user experience)
  • Copying App Assignment to a Category mapping

[sta_anchor id=”sdpdovci” /]

Sample Data Platform Deployment on Virtualized Cloud Infrastructure

Data is king and your users need a sample data platform quickly.

With this Fling, you will leverage your VMware Cloud Foundation 4.0 with vRealize Automation deployment and stand a sample data platform based on vSphere Virtual Machines in less than 20-minutes comprising of Kafka, Spark, Solr, and ELK.

You can also choose whether to deploy a wavefront proxy and configure the components to send data to the wavefront proxy or use your own.

[sta_anchor id=”cndfe” /]

Community Networking Driver for ESXi

This Fling is a collection of ESXi Native Drivers which enables ESXi to recognize and consume various PCIe-based network adapters (See Requirements for details). These devices are not officially on the VMware HCL and have been developed to enable and support the VMware Community.

[sta_anchor id=”csci” /]

Code Stream Concourse Integrator

The Code Stream Concourse Integrator (CSCI) Fling provides integration between a vRealize Automation Code Stream and Concourse CI tools with which users can trigger Concourse CI pipelines from Code Stream pipelines without any additional tooling/scripting. This enables users to use the features from both the tools flexibly and seamlessly as per their needs. This solution is built using Code Stream’s extensibility feature named Custom Integration.

Updates

[sta_anchor id=”ecc” /]

ESXi Compatibility Checker

The ESXi Compatibility Checker helps the vSphere admin out in checking if their environment will work with later versions of ESXi. [non-sponsored advertisement]Also check Runecast, they can run a simulation for you as well.[/non-sponsored advertisement]

Changelog

Build 20210219

  • Fix for ESX / VC 7.0 U1 Versioning issues
  • A new logo 😉

[sta_anchor id=”vmlp” /]

VMware Machine Learning Platform

Our goal is to provide an end-to-end ML platform for Data Scientists to perform their job more effectively by running ML workloads on top of VMware infrastructure.

Using vMLP allows to:

Save the costs by enabling efficient use of shared GPUs for ML workfloads
Reduce the risks of broken Data Science workflows by leveraging well-tested and ready-to-use demos and project templates
Faster “go-to-market” for ML models by utilizing end-to-end oriented tooling including fast and easy model deployment and serving via standardized REST API

Changelog

Version 0.4.1

  • Jupyter: R Kernel
  • Jupyter: BitFusion 2.5.0 Demo
  • Jupyter: MADlib/RTS4MADlib on Greenplum Demo
  • Multiple bug fixes

[sta_anchor id=”vhpct” /]

Virtualized High Performance Computing Toolkit

This toolkit is intended to facilitate managing the lifecycle of these special configurations by leveraging vSphere APIs. It also includes features that help vSphere administrators perform some common vSphere tasks that are related to creating such high-performing environments, such as VM cloning, setting Latency Sensitivity, and sizing vCPUs, memory, etc.

Changelog

Nope 🙁

[sta_anchor id=”hpi” /]

Horizon Peripherals Intelligence

Horizon Peripherals Intelligence is an online self-serviced diagnosis service that can help increase the satisfaction when using peripheral devices with Horizon product by both the end users and the admin user. Currently, we support diagnosis for the following device categories – USB storage devices, USB printers, USB scanners, Cameras. We will continue to cover more device categories in the future

Changelog

Version 1.0

  • Add support for USB Audios, Speechmics, Signaturepads, Barcode scanners
  • Add support for L10n of web pages in simplified Chinese, traditional Chinese and English
  • Add support for window 7 and windows 2012R2
  • Add support for 32 bits OS
  • Add support for cmdline installation

[sta_anchor id=”woaafm” /]

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 

  • Added support for Big Sur
  • Updated icon

[sta_anchor id=”osot” /]

VMware OS Optimization Tool

Image optimize you must with osot!

Changelog

  • nope 🙁

Update: OSOT didn’t receive an update, someone only edited the page according to Hilko.

[sta_anchor id=”hhu” /]

Horizon Helpdesk Utility

Besides ControlUp the helpdesk fling  is the best tool to help your users.

Changelog

Version 1.5.0.24

  • Added support for Horizon 8.1

[sta_anchor id=”hcibench” /]

HCIBench

HCIBench stands for “Hyper-converged Infrastructure Benchmark”. It’s essentially an automation wrapper around the popular and proven open source benchmark tools: Vdbench and Fio that make it easier to automate testing across a HCI cluster. HCIBench aims to simplify and accelerate customer POC performance testing in a consistent and controlled way. The tool fully automates the end-to-end process of deploying test VMs, coordinating workload runs, aggregating test results, performance analysis and collecting necessary data for troubleshooting purposes.

Changelog

Version 2.5.3

  • Fixed graphite permission issue which blocked vdbench/fio grafana display
  • Updated drop cache script to make it compatible with upcoming vSphere
  • md5sum: 622625cc7a551bd7bf07ff4f19a57a17 HCIBench_2.5.3.ova

[sta_anchor id=”reach” /]

Horizon Reach

Again if you’re not a ControlUp customer Reach is the next best thing to manage you’re Horizon environment.

Changelog

Version 1.3.1.2

  • Added support for Horizon 8.1
  • Bugfixes

[sta_anchor id=”wsod” /]

Workspace ONE Discovery

VMware Workspace ONE UEM is used to manage Windows 10 endpoints, whether it be Certificate Management, Application Deployment or Profile Management. The Discovery Fling enables you to view these from the device point of view and review the Workspace ONE related services, which applications have been successfully deployed, use the granular view to see exactly what has been configured with Profiles, view User & Machine certificates and see which Microsoft Windows Updates have been applied.

Changelog

February, 16, 2021 – Version 1.2

  • Replaced icon
  • New logo 🙂

[sta_anchor id=”avmu” /]

App Volumes Migration Utility

App Volumes Migration Utility allows admins to migrate AppStacks managed by VMware App Volumes 2.18, to the new application package format of App Volumes 4. The format of these packages in App Volumes 4 have evolved to improve performance and help simplify application management.

Changelog

Version 1.0.7 Update

  • Migration fails if their are blacklisted registry entries containing embedded NULL chars.
  • File system migration fails if their are directories having a trailing DOT name ( ex- Microsoft. ).

Filtering/Searching and pagination with the Python module for VMware Horizon

Yesterday I added the first method to the VMware Horizon Python module that makes use of filtering while the day before that I added pagination. VMware{Code} has a document describing available options for both but let me give some explanation.

Pagination

Pagination is where you perform a query but only get an x amount of objects returned by default. The rest of the objects are available on the next page or pages. This is exactly what I ran into with the vmware.hv.helper Powershell module a long time ago. With the REST api’s this is rather easy to add since if there are more pages/objects left the headers will contain a key named HAS_MORE_RECORDS. For all the methods that I add where pagination is supported you don’t need to handle this though as I have added it to the method itself. What I did add was the option the change the maximum page size. I default to 100 and the maximum is 1000, if you supply an interrupt higher than 1000 this will be corrected to 1000.

Filtering

Filtering needs some more work from the user of the module to be able to use it.

What options are there for filtering?

For the type we have: And, Or and Not

For the filters themselves there are: Equals, NotEquals, Contains, StartsWith and Between.

The formula is you pick one from the first row and combine that with one or more from the second row.

To apply these the document describes the base schema like this:

{
    “type”: ”And”,
    “filter”: <filter object>
}

and a filter object looks like this:

{
    "type":"Equals",
    "name":"domain",
    "value":"ad-example0"
}

or this for a range:

{
    "type":"Between",
    "name":"assignedUsers",
    "fromValue":"10",
    "toValue":"20"
}

Combining both into a single object looks like this:

{
    "type":"Not",
    "filter": {
        "type":"Equals",
        "name":"domain",
        "value":"ad-example0"
    }
}

This all looks like a dictionary with a nested dictionary when translating it to Python but when you have multiple filters it suddenly looks like this:

{
    "type":"And",
  "filters": [
        {
            "type":"Equals", 
            "name":"domain",
            "value":"ad-example0"
        },
        {
            "type":"StartsWith", 
            "name":"name",
            "value":"test"
        }
    ]
}

otherwise know as a dictionary with a list of dictionaries in it and since the latter also works with a single dict inside the list I have taken that route. The document also describes encoding and minifying the code to it works for a REST api call but I have done all of that for you so no need to worry about it, just build the dictionary and you are good!

Now let’s actually perform a search

First I create my base object with the type AND and a list for the filters key

filter_dict = {}
filter_dict["type"] = "And"
filter_dict["filters"] = []

Next I create the filters object where the type is contains and I filter on the field name with the value LP-00

filter1={}
filter1["type"] = "Contains"
filter1["name"] = "name"
filter1["value"] = "LP-00"

And now I add the filters1 object to the filter_dict filters list

filter["filters"].append(filter1)

and I get the machines with a pagesize of 1 to show the pagination (the pool with these machines only has 2 😉 )

machines = obj.get_machines(maxpagesize=1, filter = filter_dict)

And this would be the entire python script

import requests, getpass, urllib, json
import vmware_horizon

requests.packages.urllib3.disable_warnings()

url="https://loftcbr01.loft.lab"
username = "m_wouter"
domain = "loft.lab"
pw = getpass.getpass()

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

obj = vmware_horizon.Inventory(url=hvconnectionobj.url, access_token=hvconnectionobj.access_token)

filter_dict = {}
filter_dict["type"] = "And"
filter_dict["filters"] = []
filter1={}
filter1["type"] = "Contains"
filter1["name"] = "name"
filter1["value"] = "LP-00"

filter["filters"].append(filter1)

machines = obj.get_machines(maxpagesize=1, filter = filter_dict)

for i in machines:
    print(i["name"])

hvconnectionobj.hv_disconnect()

And it shows this in python: