Home > Blogs > VMware PowerCLI Blog > Monthly Archives: April 2016

Monthly Archives: April 2016

PowerCLI 6.3 R1: Get-ESXCLI Why the V2?

If you have installed the latest release of PowerCLI (6.3 R1) then you may have noticed a warning if you have run the Get-ESXCLI cmdlet that looks a little like this:

image

What does it mean?

Whilst talking to the community and listening to our customers we heard that there were a couple of issues with this cmdlet, some that lead to problems with scripts breaking when being used against environments that have more than one version of ESX or maybe upgraded versions of ESX using older scripts with this cmdlet.  Lets take a bi of a deeper dive into these issues so you can understand why we made the change and how the new –V2 version is so much better!

Lets discuss the 2 main problems….

1. Positional values and the $null variable

When using the Get-ESXCLI cmdlet we would only allow invocation based on positional parameters, this was somewhat painful as providing just 1 parameter for a esxcli which had 15 options would result in long, weird looking scripts with a lot of $null parameters to fill the blanks, it was hard to work out which parameters where needed and where they fit in between the $null entires.  Lets take a look at the “ESXCLI network diag ping” namespace for example, to call this namespace and ping a given address twice in ESXCLI you would need to call the namespace with a –count and a –host option as per the docs here.  We would have had to find out where to put these when calling the Get-ESXCLI cmdlet and where to add the $null for everything we didn’t want to use.

Once done we would end up with a script which may look like the following:

$esxcli = Get-ESXCLI -VMHost (Get-VMhost | Select -first 1)

$esxcli.network.diag.ping(2,$null,$null,“10.0.0.8”,$null,$null,$null,$null,$null,$null,$null,$null)

Not the most intuitive way to do things but it worked right?! Well, yeah kind of but that leads us to problem 2…

2.  Mixed environments or upgraded environments

One of the rules for updating ESXCLI namespaces is that you are allowed to add options but not remove or modify existing ones, this stops breaking changes for anyone using ESXCLI in a script, the issue was that when people were using these through PowerCLI and potentially using it against multiple hosts of different versions we could have a different number of $null parameters as we need one for every parameter we don’t use.

For example, we may have the following example in ESX 5.1:

$esxcli = Get-ESXCLI -VMHost (Get-VMhost | Select -first 1)

$esxcli.network.diag.ping(2,$null,$null,“10.0.0.8”,$null,$null,$null,$null,$null,$null,$null,$null)

This may work fine but then what if we at VMware extending this namespace to give it an extra option, we would need an extra $null through Get-ESXCLI, so for example in ESX 5.5 this may be:

$esxcli = Get-ESXCLI -VMHost (Get-VMhost | Select -first 1)

$esxcli.network.diag.ping(2,$null,$null,“10.0.0.8”,$null,$null,$null,$null,$null,$null,$null,$null,$null)

As you can see, if we ran either the 5.1 version against a 5.5 hosts or the 5.5 version against the 5.1 host we would have errors and broken scripts.  Of course you could check for each version but this starts to get complex when sharing scripts for others to use and the multiple versions of esxcli namespaces we have out there.

So what does V2 do that’s different?

The Get-ESXCLI cmdlet has now been updated with a –V2 parameter which supports specifying method arguments by name, essentially we are able to fix both issues above and provide an easier way to explore what an ESXCLI namespace requires and allow us to provide only the items we care about, lets walk though the same example but using the –V2 switch

Firstly lets store the new version in a variable like we did above:

$esxcli2 = Get-ESXCLI -VMHost (Get-VMhost | Select -first 1) -V2

Next we can find out what options are available to set for the namespace by calling the new CreateArgs method and storing this in a variable:

$arguments = $esxcli2.network.diag.ping.CreateArgs()

Now we can see what’s in the variable by calling it:

PowerCLI C:\> $arguments

Name                           Value
—-                           —–
host                           Unset, ([string], optional)
wait                           Unset, ([string], optional)
df                             Unset, ([boolean], optional)
interval                       Unset, ([string], optional)
ttl                            Unset, ([long], optional)
debug                          Unset, ([boolean], optional)
nexthop                        Unset, ([string], optional)
count                          Unset, ([long], optional)
netstack                       Unset, ([string], optional)
size                           Unset, ([long], optional)
ipv4                           Unset, ([boolean], optional)
ipv6                           Unset, ([boolean], optional)
interface                      Unset, ([string], optional)

As you can see, its helped us out here, it has prefilled this array with the options we can specify, now all we need to do is assign values to the entries we want to use:

$arguments.count = 2

$arguments.host = “10.0.0.8”

If we look at what’s in the variable again we can see we have set just the two entries we need:

PowerCLI C:\> $arguments

Name                           Value
—-                           —–
host                           10.0.0.8
wait                           Unset, ([string], optional)
df                             Unset, ([boolean], optional)
interval                       Unset, ([string], optional)
ttl                            Unset, ([long], optional)
debug                          Unset, ([boolean], optional)
nexthop                        Unset, ([string], optional)
count                          2
netstack                       Unset, ([string], optional)
size                           Unset, ([long], optional)
ipv4                           Unset, ([boolean], optional)
ipv6                           Unset, ([boolean], optional)
interface                      Unset, ([string], optional)

Next all we need to do is send these options back by using the Invoke method and providing our set $arguments variable:

$esxcli2.network.diag.ping.Invoke($arguments)

lets put this together so you can see how easy this is now:

$esxcli2 = Get-ESXCLI -VMHost (Get-VMhost | Select -first 1) -V2

$arguments = $esxcli2.network.diag.ping.CreateArgs()

$arguments.count = 2

$arguments.host = “10.0.0.8”

$esxcli2.network.diag.ping.Invoke($arguments)

Conclusion

As you can see, not only is this much easier to understand but we also don’t have to use lots of $null entries and it will also work against multiple versions of ESXi or allow you to share your scripts knowing that they will work against any version of ESXi that supports the namespaces you used in ESXCLI.

A Win, Win situation here, so why wouldn’t you use the –V2 option?!

PowerCLI Best Practice: Correct Use of Strong Typing

All software is changed over time to allow for improvements, this ensures it is providing the best experience for the user and evolving into a better product. PowerCLI is no exception! Yet, as you know PowerCLI is not the typical GUI application where we can move a text box or change the color of a button. Once written, scripts require that every piece of PowerCLI they use retains that same functionality in future versions, this is something we take seriously at VMware as we don’t want to break existing scripts that have been written but clearly do want to move the product forward.

In order to do this we need to provide you, the users of PowerCLI with a compatibility contract. A contract which states which elements are guaranteed to stay the same, and which elements may change. Scripts are supposed to rely on the areas which stay the same and steer clear of the areas we may change to provide a better user experience.

As a general rule of thumb, PowerShell-style use of PowerCLI is safe. This includes cmdlets and their parameters and retrieving the values of properties in objects returned by cmdlets. What is not safe is the .Net programming-style use of PowerCLI, for example relying on the specific types of objects, whilst looking at scripts in the community and talking to customers we have seen a trend which may cause issues when followed.  We want the best for our customers and to keep you informed on what we consider a best practice when using types and therefore we would like to explain a little more about this, we want to ensure this is being used in the correct way and that your scripts are written to always be safe with the changes we make.

I’ll focus on one particular aspect of compatibility which was affected by changes in Get-VM in the recently released PowerCLI 6.3. Script authors often explicitly specify the types of parameters in a function. This adds an extra layer of reliability, especially in highly reusable functions:

function MyFunction([Full.Type.Name.Goes.Here] $myObject)

Or, sometimes types are used to check an input and take different action depending on the type of an object:

if ($myObject -is [Full.Type.Name.Goes.Here]) { … }

But how do you get the correct type name to use here? Use $myObject.GetType() or Get-Member, right? These methods indeed return the actual implementation type of the object. For a VM, the result is “VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl”.

Unfortunately, PowerCLI cannot guarantee that the implementation type won’t change in the future. There are scripts that we have seen that rely on implementation types (see that “impl” in the name?) and these scripts occasionally break when bigger changes need to be made in PowerCLI, this clearly can cause issues, issues we would prefer you didn’t get and therefore we would like to give you a best practice on what we think the safest way to do this actually is, so if you are using types in your scripts you can continue to do this and ensure any future changes we make will not impact you.

The approach we would suggest is to use what the PowerCLI team calls the “.Types” interface. “.Types” is a set of types which is guaranteed to maintain compatibility with scripts, across different PowerCLI versions. The “.Types” type for e.g. a VM is “VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine”.  Note how the “.Impl” part of the name is now replaced by “.Types”.

This brings us to the question, how do you get the correct “.Types” type for a given object?  

Basically, any type in the “Types” namespace, implemented by your object is ok to use. But usually the closest match is a type which has the same name as the implementation type but without the “Impl” suffix. So usually the following will get you the type name:

$myType = $myObject.GetType()

$compatibleTypeName = $myType.Name.Replace(“Impl”, “”)

 

$myType.GetInterfaces()

    | where { $_.FullName -like "*.Types.*" -and $_.Name -eq $compatibleTypeName }

    | select FullName

 

If it doesn’t find the type you are looking for, you can still examine the full list of supported “.Types” types and pick the one you like best:

 

$myObject.GetType().GetInterfaces()

    | where { $_.FullName -like "*.Types.*" }

    | select FullName

Here’s what the previous examples look like once the implementation types are replaced by the correct compatible types:

INCOMPATIBLE:

function MyFunction([VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl] $myObject)

if ($myObject -is [VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl]) { … }

 

 

COMPATIBLE:

function MyFunction([VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $myObject)

if ($myObject -is [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]) { … }

All of the above can come in handy to you particularly if you have scripts which use the “VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl” type. In PowerCLI 6.3, Get-VM cmdlet performance was optimized and, as a result, the type of objects returned by the cmdlet needed to change to “VMware.VimAutomation.ViCore.Impl.V1.VM.UniversalVirtualMachineImpl”. The change affects cases where the call to Get-VM does not specify filtering by the parent object, such as VMHost. With PowerCLI 6.3, calls such as plain Get-VM or Get-VM –Name ‘infra*’ return objects of the new UniversalVirtualMachineImpl type.

To detect problematic scripts which may be affected by future PowerCLI changes, you can search for the “VMware.VimAutomation.*.Impl.*” string. 

To detect scripts which are likely to be affected specifically by the recent Get-VM change, search for the “VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl” string and use “VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine” instead.