Get-EsxCli on steroids V2

Get-EsxCli V2 made easy. Generate automatically a PowerCLI function for each EsxCli command.

Updates 21/07/2016: Two issues fixed
Use $PSBoundParameters.ContainsKey(‘parametername’) to be able to use $false for a parameter value.
checking-for-bound-parameters
Type of the host parameter modified to not use the implementation type.
powercli-best-practice-correct-use-strong-typing
I am also starting to use github in a better way, thanks to VMware vSphere PowerCLI Reference.

The Get-EsxCli cmdlet is very powerful and can let us run esxcli command even if ssh is not allowed for a host.
The Get-EsxCli -V2 is a drastic improvement

The first version of Get-EsxCLI on steroids was a wrapper around get-esxcli to bypass the limitation of the V1.

This new version has been totally rewritten for V2 and enhance the benefits provided by V2:

You can now generate you own PowerCLI functions without having to wait for me to test all new ESXi release.
Compatible with all esxcli namespace including third party vendors.
The functions generated will be compatible with new build of ESXi, as long as the namespace remains the same, even if new parameters are added.
The functions will also handle partially backward compatibility.
The esxcli mandatory parameters are now mandatory in the PowerCLI function.

With ESXi 6.0 U2, the script generates 473 PowerCli functions in less than 3 minutes.
Note:
The host was prepared for NSX otherwise everything in the vxlan namespace will be missing.

Let’s have a look at some of the functions generated, how to use them, and how to generate them.

ping-EsxCLI.network.diag

function ping-EsxCLI.network.diag{
<#
.SYNOPSIS
Send ICMP echo requests to network hosts.

.DESCRIPTION
This function provide access via Power-Cli to the esxcli equivalent function.
All parameters and help associated to the original esxcli function are available.
This function is based on the original get-esxcli -v2 PowerCLI cmdlet
A PowerCli VMHost object is a mandatory parameter
It is also possible to pipe VMhost objects to execute this function accross many hosts in one operation

.NOTES
Author: Christophe Calvet
Blog: http://www.thecrazyconsultant.com/get-esxcli_on_steroids

.PARAMETER count
Specify the number of packets to send.

.PARAMETER debug2
VMKPing debug mode.

.PARAMETER df
Set DF bit on IPv4 packets.

.PARAMETER host2
Specify the host to send packets to. This parameter is required when not executing ping in debug mode (-D)

.PARAMETER interface
Specify the outgoing interface.

.PARAMETER interval
Set the interval for sending packets in seconds.

.PARAMETER ipv4
Ping with ICMPv4 echo requests.

.PARAMETER ipv6
Ping with ICMPv6 echo requests.

.PARAMETER netstack
Specify the TCP/IP netstack which the interface resides on

.PARAMETER nexthop
Override the system's default route selection, in dotted quad notation. (IPv4 only. Requires interface option)

.PARAMETER size
Set the payload size of the packets to send.

.PARAMETER ttl
Set IPv4 Time To Live or IPv6 Hop Limit

.PARAMETER wait
Set the timeout to wait if no responses are received in seconds.

.PARAMETER VMHost
One or many PowerCli VMHost object

.EXAMPLE
Some examples are available in the blog
#>
	[CmdletBinding()]
	param(
	[string]$host2,
	[string]$wait,
	[boolean]$df,
	[string]$interval,
	[long]$ttl,
	[boolean]$debug2,
	[string]$nexthop,
	[long]$count,
	[string]$netstack,
	[long]$size,
	[boolean]$ipv4,
	[boolean]$ipv6,
	[string]$interface,
	[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
	[VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost[]]$VMhost
	)
	process{
	foreach($SelectedVMHost in $VMhost){
		Try{
		$esxcliv2 = Get-EsxCLI -VMHost $SelectedVMHost -V2
		}
		Catch{
		Write-error "Not able to get-esxcli for $(($SelectedVMHost).name)"
		continue
		}

		if($esxcliv2.network.diag.ping){
		#Namespace available for this ESXi host
		}
		Else{
		Write-error "The namespace esxcliv2.network.diag.ping is not available for $(($SelectedVMHost).name)"
		continue
		}

			if($esxcliv2.network.diag.ping | gm | where {$_.Name -eq 'CreateArgs'}){
			#To anticipate scenario with commands that didn't have any parameters in a previous build
			#More details about this challenge in the blog
			$HashTable = $esxcliv2.network.diag.ping.CreateArgs()
				if($PSBoundParameters.ContainsKey('host2')){
					if($HashTable.containskey('host')){
						$HashTable.host = $host2
					}
					Else{
						Write-error "The parameter host2 is not available for $(($SelectedVMHost).name)"
						continue
					}
				}
				if($PSBoundParameters.ContainsKey('wait')){
					if($HashTable.containskey('wait')){
						$HashTable.wait = $wait
					}
					Else{
						Write-error "The parameter wait is not available for $(($SelectedVMHost).name)"
						continue
					}
				}
				if($PSBoundParameters.ContainsKey('df')){
					if($HashTable.containskey('df')){
						$HashTable.df = $df
					}
					Else{
						Write-error "The parameter df is not available for $(($SelectedVMHost).name)"
						continue
					}
				}
				if($PSBoundParameters.ContainsKey('interval')){
					if($HashTable.containskey('interval')){
						$HashTable.interval = $interval
					}
					Else{
						Write-error "The parameter interval is not available for $(($SelectedVMHost).name)"
						continue
					}
				}
				if($PSBoundParameters.ContainsKey('ttl')){
					if($HashTable.containskey('ttl')){
						$HashTable.ttl = $ttl
					}
					Else{
						Write-error "The parameter ttl is not available for $(($SelectedVMHost).name)"
						continue
					}
				}
				if($PSBoundParameters.ContainsKey('debug2')){
					if($HashTable.containskey('debug')){
						$HashTable.debug = $debug2
					}
					Else{
						Write-error "The parameter debug2 is not available for $(($SelectedVMHost).name)"
						continue
					}
				}
				if($PSBoundParameters.ContainsKey('nexthop')){
					if($HashTable.containskey('nexthop')){
						$HashTable.nexthop = $nexthop
					}
					Else{
						Write-error "The parameter nexthop is not available for $(($SelectedVMHost).name)"
						continue
					}
				}
				if($PSBoundParameters.ContainsKey('count')){
					if($HashTable.containskey('count')){
						$HashTable.count = $count
					}
					Else{
						Write-error "The parameter count is not available for $(($SelectedVMHost).name)"
						continue
					}
				}
				if($PSBoundParameters.ContainsKey('netstack')){
					if($HashTable.containskey('netstack')){
						$HashTable.netstack = $netstack
					}
					Else{
						Write-error "The parameter netstack is not available for $(($SelectedVMHost).name)"
						continue
					}
				}
				if($PSBoundParameters.ContainsKey('size')){
					if($HashTable.containskey('size')){
						$HashTable.size = $size
					}
					Else{
						Write-error "The parameter size is not available for $(($SelectedVMHost).name)"
						continue
					}
				}
				if($PSBoundParameters.ContainsKey('ipv4')){
					if($HashTable.containskey('ipv4')){
						$HashTable.ipv4 = $ipv4
					}
					Else{
						Write-error "The parameter ipv4 is not available for $(($SelectedVMHost).name)"
						continue
					}
				}
				if($PSBoundParameters.ContainsKey('ipv6')){
					if($HashTable.containskey('ipv6')){
						$HashTable.ipv6 = $ipv6
					}
					Else{
						Write-error "The parameter ipv6 is not available for $(($SelectedVMHost).name)"
						continue
					}
				}
				if($PSBoundParameters.ContainsKey('interface')){
					if($HashTable.containskey('interface')){
						$HashTable.interface = $interface
					}
					Else{
						Write-error "The parameter interface is not available for $(($SelectedVMHost).name)"
						continue
					}
				}

					$esxcliv2.network.diag.ping.invoke($hashtable)
			}
			Else{
				if($PSBoundParameters.ContainsKey('host2') -or $PSBoundParameters.ContainsKey('wait') -or $PSBoundParameters.ContainsKey('df') -or $PSBoundParameters.ContainsKey('interval') -or $PSBoundParameters.ContainsKey('ttl') -or $PSBoundParameters.ContainsKey('debug2') -or $PSBoundParameters.ContainsKey('nexthop') -or $PSBoundParameters.ContainsKey('count') -or $PSBoundParameters.ContainsKey('netstack') -or $PSBoundParameters.ContainsKey('size') -or $PSBoundParameters.ContainsKey('ipv4') -or $PSBoundParameters.ContainsKey('ipv6') -or $PSBoundParameters.ContainsKey('interface')){
					Write-error "No parameters are available for $(($SelectedVMHost).name)"
				}
				Else{
					$esxcliv2.network.diag.ping.invoke()
				}
			}
		}
	}
}

Key points of the script:
Convert all esxcli parameters as function parameters and add the associated help:
A “2” is appended to the name of variables that will conflict with existing variables in PowerShell like $debug or $host
If a parameter in esxcli contains a “-” in its name, it will be removed.

Foreach($selectedVMhost in $VMhost)
If you “pipe” VMhosts, one process will be executed per VMhost, and consequently one VMhost per loop
Ex: $MyHost1,$Myhost2 | ping-EsxCLI.network.diag -c 2 -host2 ‘10.0.0.8’
If you call the function with many VMhost, one process will be executedm with all VMhost in one loop
Ex: ping-EsxCLI.network.diag -c 2 -host2 ‘10.0.0.8’ -VmHost $MyHost1,$Myhost2

Check if is possible to get-esxcli for this host
continue
If an issue is detected, go directly to the next VMhost in the loop.

Check if the namespace is available on this host
continue
If an issue is detected, go directly to the next VMhost in the loop.

if($esxcliv2.network.diag.ping | gm | where {$_.Name -eq ‘CreateArgs’}){ – For partial backward compatibility
The goal is to check if this VMhost, has really the capacity to CreateArgs for this namespace
For this esxcli command it is not needed, this ping function always had some parameters
It will be useful for case like list-esxcli.hardware.pci.
The version 5.0 didn’t have the Class and Mask parameters, so trying to get an argument table will not work.

Obtain the hashtable and fill it.
If a parameter has been used when calling the function, fill the hashtable accordingly.

Write-Error “The parameter — is not available for $($SelectedVMhost).name” – For partial backward compatibility
If you are attempting to use a parameter that is not available for an ESXi an error will be displayed
Continue is used to go directly to the next host in the loop.
EX: “netstack” for ping-EsxCLI.network.diag while targetting a ESXi 5.1

No parameters available for this ESXi build – For partial backward compatibility
This is to handle the scenario like list-esxcli.hardware.pci
If you use the command without any parameters it will work on all ESXi host 5.0 and above
But if “class” or “mask” is used, the function will work only on ESXi host 5.1 and above

list-EsxCLI.esxcli.command

function list-EsxCLI.esxcli.command{
<#
.SYNOPSIS
This command will list all of the esxcli commands with their namespace, object, command name and description.

.DESCRIPTION
This function provide access via Power-Cli to the esxcli equivalent function.
All parameters and help associated to the original esxcli function are available.
This function is based on the original get-esxcli -v2 PowerCLI cmdlet
A PowerCli VMHost object is a mandatory parameter
It is also possible to pipe VMhost objects to execute this function accross many hosts in one operation

.NOTES
Author: Christophe Calvet
Blog: http://www.thecrazyconsultant.com/get-esxcli_on_steroids

.PARAMETER VMHost
One or many PowerCli VMHost object

.EXAMPLE
Some examples are available in the blog
#>
	[CmdletBinding()]
	param(
	[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
	[VMware.VimAutomation.ViCore.Impl.V1.Inventory.VMHostImpl[]]$VMhost
	)
	process{
	foreach($SelectedVMHost in $VMhost){
		Try{
		$esxcliv2 = Get-EsxCLI -VMHost $SelectedVMHost -V2
		}
		Catch{
		Write-error "Not able to get-esxcli for $(($SelectedVMHost).name)"
		continue
		}

		if($esxcliv2.esxcli.command.list){
		#Namespace available for this ESXi host
		}
		Else{
		Write-error "The namespace esxcliv2.esxcli.command.list is not available for $(($SelectedVMHost).name)"
		continue
		}

			$esxcliv2.esxcli.command.list.invoke()
		}
	}
}

When a command do not have any parameters, the script generated is much more simple.
But it will still be compatible with new version if any new parameters are added.
It is possible to execute “.invoke()” without any hashtable for commands that expect a hashtable

How to use the functions generated:

Test if all hosts connected to a vCenter server can ping a NTP server.
The challenge here is that output of the esxcli command will provide you two CodeProperty “Summary” and “Trace”

$NTPSERVER = “10.0.0.8”
Get-VMhost | foreach {($MyHost = $_)} | ping-esxcli.network.diag -count 2 -host2 $NTPSERVER | Select-Object @{Name="MyHost";expression={$MyHost}} -expand summary | ogv

Configure HP 3PAR storage claim rules for new hosts in a new cluster “Cluster01”
From the official documentation
esxcli storage nmp satp rule add -s “VMW_SATP_ALUA” -P “VMW_PSP_RR” –O “iops=1” -c “tpgs_on” -V “3PARdata” -M “VV” -e “HP 3PAR Custom Rule”
You will obtain the equivalent for all hosts in a cluster, without enabling SSH, by using:

Get-cluster –name “Cluster01” | get-vmhost | add-esxcli.storage.nmp.satp.rule –satp "VMW_SATP_ALUA" –psp "VMW_PSP_RR" –pspoption "iops=1" –claimoption "tpgs_on" –vendor "3PARdata" –model "VV" –description "HP 3PAR Custom Rule”

And to check the result

Get-cluster –name “Cluster01” | get-vmhost | foreach {($MyHost = $_)} | list-esxcli.storage.nmp.satp.rule | Select-Object @{Name="MyHost";expression={$MyHost}},* | Where {$_.Vendor –eq “3PARdata”} | ogv

The inspiration for the next one is coming from a post from Ather Beg, who I had the chance to work with. How to remind yourself of Advanced Settings changes in ESXi.
Original command
esxcli system settings advanced list –d
Now let do this for all hosts connected to vCenter server.

Get-vmhost | foreach {($MyHost = $_)} | list-esxcli.system.settings.advanced –delta $True | Select-Object @{Name="MyHost";expression={$MyHost}},*| ogv

Note that the above function will throw “a parameter delta is not available” error for older build of ESXi instead of executing the command and ignore this parameter.

New-PowerCLI_ESXCLIv2_Function
The script that makes script.
I still have to improve it, there are many duplicates lines.

Summary at a very high level:
For each namespace identify all “methods” and create a function for each method. (Identified via the method property)
Then identify all “child space”, if any, and call the function in a recursive way for each of them. (Identified via the childelement property)
The top “esxcli” object doesn’t have any child element, use instead the properties of type CodeProperty
Bug with esxcliv2.vsan.childelements so use as a workaround the properties of type CodeProperty.

Bug identified in Get-EsxCli:
$esxcliv2.vsan.childelements reports Name instead of Datastores.
A workaround is available as described above.

Thanks:
I had a call regarding the challenges associated with Get-ESXCLI v1 with Alan Renouf.
I would like to thanks him and the PowerCLI team to listen to their customers.

One thought on “Get-EsxCli on steroids V2

  1. Pingback: deprecated post 01 - The Crazy Consultant

Leave a Reply

Your email address will not be published. Required fields are marked *