Exploring the types exposed by PowerCLI

One of the things I love about PowerShell, is that once we know the fundamentals, we can learn the rest on our own, by just exploring, experimenting and playing with it. As I like to tell people, discoverability is the number 1 feature of PowerShell.

So let’s see how we can explore PowerCLI assemblies and the object types they expose.

First, what is an assembly ?

PowerCLI is packaged as a bunch of PowerShell modules (snapins as well, but these are an endangered species so let’s focus on modules).
There are 2 broad types of modules : script modules and binary modules. Script modules are written in PowerShell and have a .psm1 file extension. Binary modules are written in C# and are .NET Framework assemblies (.dll).

In the case of PowerCLI, the modules are binary. Well, this is not totally accurate, due to the transition of PowerCLI from snapins to modules. For example, let’s look at the main module : VMware.VimAutomation.Core

PowerCLI C:\> Get-Module -Name VMware.VimAutomation.Core |
Format-List Name,ModuleType,Path


Name       : VMware.VimAutomation.Core
ModuleType : Script
Path       : C:\Program Files (x86)\VMware\Infrastructure\vSphere
             PowerCLI\Modules\VMware.VimAutomation.Core\VMware.VimAutomation.Core.ps1

 
This is telling us that VMware.VimAutomation.Core is just a script module.
But looking at the content of the script VMware.VimAutomation.Core.ps1, we see that its main purpose is to load the necessary dlls and the good old snapin :

$dllToLoad |%{    
    $dllPath = [System.IO.Path]::Combine($coreAssembliesPath, $_)

    # Load DLL
    [Void] [Reflection.Assembly]::LoadFile($dllPath)
}

$snapinName = "VMware.VimAutomation.Core"
if (!(Get-PSSnapin $snapinName -ErrorAction Ignore)) {	  
		Add-PSSnapin $snapinName		
}

 
And the snapin itself is a dll :

PSSnapin VMware.VimAutomation.Core
 
Here is how to list all the .NET assemblies PowerCLI is made of (output truncated for brevity):

PowerCLI C:\> Get-ChildItem -Path "C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI" -Recurse -Filter "*.dll" |
Select-Object -Property Fullname

FullName
--------
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\CryptoSupport.dll
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\ICSharpCode.SharpZipLib.dll
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\Interop.Shell32.dll
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\msvcr90.dll
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\VMware.Binding.Ls2.dll
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\VMware.DeployAutomation.dll
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\VMware.DeployAutomation.SoapService50.dll
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\VMware.ImageBuilder.dll
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\Vmware.ImageBuilder.SoapService50.dll
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\VMware.Security.CredentialStore.dll
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\VMware.Vim.dll
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\VMware.VimAutomation.Sdk.Impl.dll
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\VMware.VimAutomation.Sdk.Interop.dll
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\VMware.VimAutomation.Sdk.Types.dll
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\VMware.VimAutomation.Sdk.Util10.dll
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\VMware.VimAutomation.Sdk.Util10Ps.dll
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\VMware.VimAutomation.ViCore.Cmdlets.dll
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\VMware.VimAutomation.ViCore.Impl.dll
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\VMware.VimAutomation.ViCore.Interop.dll
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\VMware.VimAutomation.ViCore.Types.dll

PowerCLI C:\> (Get-ChildItem -Path "C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI" -Recurse -Filter "*.dll" |
Select-Object -Property Fullname).Count
56

 
There are 56 of them. But how many of them are currently loaded in our PowerShell session ?

PowerCLI C:\> ([AppDomain]::CurrentDomain.GetAssemblies() | Where { $_.Location -like "*PowerCLI*" }).Count
46

 
We normally never need to load them manually but, if we really really want to, here is how we would load the assembly VMware.Security.CredentialStore.dll :

PowerCLI C:\> [Reflection.Assembly]::LoadFrom('C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\
VMware.Security.CredentialStore.dll')

GAC    Version        Location
---    -------        --------
False  v4.0.30319     C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\VMware.Security.Crede...

 
Now, let’s look into the types exposed by these PowerCLI assemblies :

PowerCLI C:\> $Assemblies = [AppDomain]::CurrentDomain.GetAssemblies() | Where { $_.Location -like "*PowerCLI*" }
PowerCLI C:\> $Assemblies.ExportedTypes | more

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    ICredentialStore
True     False    CredentialStoreFactory                   System.Object
True     False    DistinguishedName                        System.Object
True     False    DnKeyListSdk                             System.Object
True     False    LocalizableMessage                       System.Object
True     False    VimException                             System.ApplicationException
True     False    ViError                                  VMware.VimAutomation.Sdk.Types.V1.ErrorHandlin...
True     False    MethodFault                              VMware.VimAutomation.Sdk.Types.V1.ErrorHandlin...
True     False    ObnRecordProcessingFailedException       VMware.VimAutomation.Sdk.Types.V1.ErrorHandlin...
True     False    ServerObnFailureException                VMware.VimAutomation.Sdk.Types.V1.ErrorHandlin...
True     True     ErrorCategory                            System.Enum
True     True     VimExceptionSeverity                     System.Enum
True     False    ViServerConnectionException              VMware.VimAutomation.Sdk.Types.V1.ErrorHandlin...
True     False    NamedObject
True     False    Range                                    System.Object
True     False    SnapinVersion
True     False    VIObjectCore
True     False    VIObject
True     False    Task
True     True     TaskState                                System.Enum

 
We use the more command, otherwise the output would go on and on, for thousands of lines. There are thousands of types, so let’s break down the number of types by assembly to have an idea of which assemblies are exposing the most types :

PowerCLI C:\> $Assemblies | Select-Object -Property @{Label='Module';Expression={ $_.Modules }}, @{Label='TypesCount';Expression={ ($_.ExportedTypes).Count }} |
Sort-Object -Property TypesCount -Descending | Select-Object -First 15

Module                                    TypesCount
------                                    ----------
IntegritySoapService40.dll                      2986
SmsProxyService.dll                             2085
VMware.VimAutomation.ViCore.Types.dll            976
VMware.VimAutomation.ViCore.Impl.dll             537
VMware.VimAutomation.ViCore.Cmdlets.dll          415
IntegritySoapService40.XmlSerializers.dll        380
VMware.VimAutomation.ViCore.Interop.dll          220
VMware.VimAutomation.VROps.Views.dll             159
SpbmProxyService.dll                             159
VMware.VimAutomation.VROps.Schema.dll            146
ICSharpCode.SharpZipLib.dll                      101
VMware.VumAutomation.Types.dll                    69
VMware.VimAutomation.Sdk.Util10.dll               54
VMware.Binding.Ls2.dll                            53
VMware.VimAutomation.Vds.Types.dll                52

 
VMware.VimAutomation.ViCore.Types.dll looks interesting, we can check if it contains the type of some objects we use on a daily basis, like datastore and VMHost :

PowerCLI C:\> $Assemblies | Where-Object { $_.Location -like "*VMware.VimAutomation.ViCore.Types.dll" } |
Select-Object -ExpandProperty ExportedTypes |
Where-Object { $_.Name -eq "Datastore"  -or $_.Name -eq "VMHost" } |
Format-List -Property Name, Attributes

Name       : Datastore
Attributes : AutoLayout, AnsiClass, Class, Public, ClassSemanticsMask, Abstract

Name       : VMHost
Attributes : AutoLayout, AnsiClass, Class, Public, ClassSemanticsMask, Abstract

 
Indeed they are here.

By the way, there is better way to find which assembly is providing a given type :

PowerCLI C:\> $DatastoreType = (Get-Datastore -Name "ISCSI-1").gettype()

PowerCLI C:\> [reflection.assembly]::GetAssembly($DatastoreType) | Select-Object -ExpandProperty Location
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\VMware.VimAutomation.ViCore.Impl.dll

 
Whaaaat ??
We had seen earlier that the “datastore” type was defined in VMware.VimAutomation.ViCore.Types.dll, and now we are told that it is in VMware.VimAutomation.ViCore.Impl.dll. What is going on ?

PowerCLI C:\> (Get-Datastore -Name "ISCSI-1").gettype() | Select-Object -Property FullName

FullName
--------
VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.VmfsDatastoreImpl

 
Aha, Get-Datastore is returning objects of the type VmfsDatastoreImpl, not Datastore.

We can check if this assembly defines other datastore-related types, like so :

PowerCLI C:\> $Assemblies | Where-Object { $_.Location -like "*VMware.VimAutomation.ViCore.Impl.dll" } |
Select-Object -ExpandProperty ExportedTypes |
Where-Object { $_.Name -like "*datastore*" -and $_.BaseType } | Select-Object -Property Name,BaseType

Name                    BaseType
----                    --------
DatastoreItemImpl       VMware.VimAutomation.ViCore.Util10.VersionedObjectImpl
DatastoreFolderImpl     VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreItemImpl
DatastoreRootFolderImpl VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreFolderImpl
DatastoreImpl           VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.StorageResourceImpl
NasDatastoreImpl        VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreImpl
VmfsDatastoreImpl       VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreImpl
DatastoreFileImpl       VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreItemImpl
DatastoreVMDiskFileImpl VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreFileImpl
DatastoreServiceImpl    System.Object

 
Yes, it does.
We can actually see that it defines one type for VMFS datastores (VmfsDatastoreImpl) and one type for NFS datastores (NasDatastoreImpl), both of which are child types (derived classes in C# parlance) of the type DatastoreImpl.

Also, as we can see below, the “Datastore” type is an abstract class :

PowerCLI C:\> "Datastore" -as [type] | Select-Object -Property Name,Attributes

Name                                                              Attributes
----                                                              ----------
Datastore AutoLayout, AnsiClass, Class, Public, ClassSemanticsMask, Abstract

 
What is an abstract class ?

Basically, we cannot create instances of the class (objects) when the class is abstract. Generally abstract classes are used as a generic template for derived classes. Then, objects are created from a derived concrete class, concrete meaning we can create instances of it.

So we can assume that VMware.VimAutomation.ViCore.Types.dll is providing generic abstract classes for use by other PowerCLI assemblies, to create more specific classes from which we can create objects.

Refactoring PowerCLI scripts or functions for PowerActions

If Carlsberg is “probably the best beer in the world“, then PowerActions is more than probably the best VMware fling in the world.

It brings PowerCLI automation goodness directly within the vSphere Web Client in 2 ways : a PowerShell/PowerCLI console and the ability the extend the Web Client context menu actions with your custom actions based on your PowerCLI scripts.

This article is not intended as a general how-to guide for PowerActions, everything you need is here : presentation, video tutorial, documentation and installer download links.

In this article, we are going to focus on how to modify existing scripts or functions to add them as context menu actions in the Web Client.

Refactoring a script

 
Let’s say we have a basic PowerCLI script (a .ps1 file) to list all the VIBs installed on a ESXi host, its content looks like this :

param(
    [VMware.VimAutomation.ViCore.Impl.V1.Inventory.VMHostImpl]$VMhost
)

$EsxCli = Get-EsxCli -VMHost $VMhost
$EsxCli.software.vib.list() | Select-Object -Property Name,CreationDate,InstallDate,Version

 
We want the same functionality when we right-click on a ESXi host in the Web Client and we want to play with it like it’s a just-unboxed toy from the Christmas tree. So, let’s get to it.

From the Web Client home page, we click “PowerCLI Scripts” and we see this :

PowerCLI Scripts

We click on the icon with a green “+” to add a new script to the PowerActions script library. Then, we need to specify the type of object(s) that our custom action can target. This is very important because this defines the type of PowerCLI object that the script takes as input parameter. Our script targets ESXi hosts so we choose “Host”.

To make our custom action even more powerful, we can potentially act on multiple objects (of the same type) at once. In this case, we would check the box “Expects multiple objects”. As we can see above, our original script expect only one VMHost object so we don’t need to do this.

Argument type
 
In the next screen, we give our script a name and a optional description. More importantly, we choose an “Output format” : “Action” or “Report”. Essentially, we choose “Action” for scripts performing an action on (or modifying) one or more vSphere object(s) and we choose “Report” for scripts obtaining information (similar to a Get-* cmdlet). In our case, it obtains a list of VIBs, so we choose “Report”.

What we are presented with in the next screen is the basic structure of the script which will be used by PowerActions :

Add script

The most important point here is that the vSphere object on which we right-click in the Web Client will be used as the input parameter called vParam. In other words, the target of the context menu action will be stored as the variable $vParam in the script. Don’t try to change the parameter name, it has be $vParam to work with PowerActions.

You might be under the impression that we are limited to a single parameter, but it is not the case. Let’s add -Vendor as a mandatory parameter to filter the VIBs on their vendor. We will see later, when we run the custom action in the Web Client that we are prompted to enter a value for this parameter.

Here is our final PowerActions script :

<#
.MYNGC_REPORT
#>

param
(
   [Parameter(Mandatory=$true)]
   [VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost]
   $vParam,
   [Parameter(Mandatory=$true)]
   [string]$Vendor
);

$EsxCli = Get-EsxCli -VMHost $vParam
$VibList = $EsxCli.software.vib.list()
$FilteredVibList = $VibList | Where-Object { $_.Vendor -eq $Vendor }
$FilteredVibList | Select-Object -Property Name,CreationDate,InstallDate,Version

 
Now, let’s run it !
To do that, we right-click on a ESXi host in the Web Client, at the bottom of the context menu, we choose “PowerCLI”, and then “Execute Script” :

Execute Script
 
In the next screen we select our new script and OK. Now, we are prompted to enter values for any mandatory parameter (other than vParam) :

Vendor Parameter

The Web Client displays the result in a nice table :

Report Result
 

Refactoring a function

 
Now, let’s say we are good PowerShell citizens and we encapsulate our PowerCLI tools as advanced functions to facilitate reusability. How can we reuse our existing PowerCLI functions with PowerActions ?
It is very straightforward.

As an example, we are going to use the function Audit-vCenterOperation, available here. The idea is to be able to right-click a vSphere object in the Web Client to get a list of events and operations related the selected object. This could be a workaround for the “Export Events” feature not working well in the Web Client (http://kb.vmware.com/kb/2071612).

Looking at the param(...) block, we see that it has many parameters :

param(
    [string]$VIServer = "localhost",
    [Parameter(ValueFromPipeline = $True,Position=0)]
    [ValidateScript({ $($_[0].GetType()).Namespace -eq "VMware.VimAutomation.ViCore.Impl.V1.Inventory" })]
    $Entity,
    [string]$Username,
    [datetime]$Start,
    [datetime]$Finish,
    [ValidateSet("Create","Reconfigure","Remove","DRS","HA","Password","Migration","Snapshot","Power","Alarm","Datastore","Permission")]
    [string]$OperationType
    )

 
Fortunately, none of these parameters is mandatory so we can leave them as they are.

Looking at the line just above “$Entity,“, we see that the parameter Entity is designed to work with any type of vCenter inventory object. A given PowerActions script can take only one object type as input parameter, so we’ll need to choose what kind of vSphere object we want to target with our custom action. Let’s go for VMs.

So, we add a new script in PowerActions and this time, we choose “Virtual Machine” as the target type. For the output format, we select “Report” because the script is getting information, not modifying anything.

Again, in the next screen, we are presented with a blank script structure.
At the end of it, we add the whole function definition, unchanged. By itself, this whole function {...} block does nothing, it merely defines the function. So, to make our script actually run the code encapsulated in our function, we need to call the function.

Remember, the VM we right-click in the Web Client will be stored by PowerActions in the variable $vParam. So, all we have to do is to feed $vParam as the value for the -Entity parameter of our function :

<#
.MYNGC_REPORT
#>

param
(
   [Parameter(Mandatory=$true)]
   [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]
   $vParam
);

function Audit-vCenterOperation {

    # Content of the existing function

}
Audit-vCenterOperation -Entity $vParam

 
That’s it.
Now, we can run the custom action in the Web Client.
Aha, we see that our friend “RogueAdmin” has reconfigured the VM named “New-VM”.

Audit result
 
By the way, when we have this “Report Results” page, we can resize, reorder and sort the columns to our heart’s content.