Tag Archives: PowerCLI

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.

Auditing events and changes in a vCenter environment

When we get a customer opening a support case telling : “… was working before and now it is not working anymore”.

The first question which pops up is :

“What changed ? What happened before it stopped working ?”

Quite often, the customer is not sure. Or, some customers have a selective memory and tend to “forget” configuration changes for various reasons.

Also, there are cases where we know something happened but we want to know if it was a user-initiated action. Or, the customer might even want us to retrieve by who a specific action was initiated (probably to engage in some finger-pointing/blame-assigning fun).

The vSphere client being limited to display a maximum of 1000 events, this corresponds generally to only a few days worth of events. Besides, the “Export Events” functionality of the C# client and the Web client doesn’t work well (http://kb.vmware.com/kb/2071612).
As a result, we cannot rely on the vSphere client/web client for auditing purposes.

You might think : “all this information is in the tables VPX_EVENT and VPX_EVENT_ARG in the vCenter database, I can just query it”. I would answer with 3 points :

  • This data is not user-friendly and requires further processing to become consumable information
  • A kitten dies every time you query the VCDB
  • If you want to keep 2, 3, or 5 years of events for compliance purposes, the vCenter database is a typical OLTP system and should be used as such , not as a Data Warehouse !

To make querying this kind of information easier, I wrote a function Audit-vCenterOperation .

This is essentially a wrapper around the PowerCLI cmdlet Get-VIEvent with a bit of intelligence to filter out irrelevant events and 2 additional parameters : one to select actions initiated by a specific user and one to select actions of a specific type.

Let’s see the stuff we can do with it.

To track when the configuration of the cluster “Test Cluster” was changed and by who :

vCenterOperation Cluster Reconfigure
 
Notice that here, we sent the cluster object from the pipeline. In fact, we can throw any vCenter inventory object at it, through the pipeline, and the object will be bound to the -Entity parameter of the function.

The parameter -OperationType allows to filter the operations down to a specific type or topic.
The possible values are : Create, Reconfigure, Remove, DRS, HA, Password, Migration, Snapshot, Power, Alarm, Datastore, Permission.
Don’t worry, we don’t have to know all the possible values, we can just make use of tab completion.

To track all the migrations (vMotion and Storage vMotion) for all VMs over the last 6 hours :

C:\> Audit-vCenterOperation -OperationType Migration -Start (Get-Date).AddHours(-6)


Key               : 10624
CreatedTime       : 11/25/2015 9:57:58 AM
UserName          : User
Datacenter        : Test-DC
ComputeResource   : Test Cluster
Host              : 192.168.1.187
Vm                : Test-VM
Message           : Migration of virtual machine Test-VM from 192.168.1.187, iscsi-lefthand-1 to 192.168.1.188, iscsi-lefthand-1 completed
NewConfigSettings :

Key               : 10632
CreatedTime       : 11/25/2015 10:01:16 AM
UserName          : VSPHERE.LOCAL\Administrator
Datacenter        : Test-DC
ComputeResource   : Test Cluster
Host              : 192.168.1.187
Vm                : New-VM
Message           : Task: Migrate virtual machine
NewConfigSettings :

Key               : 10633
CreatedTime       : 11/25/2015 10:01:17 AM
UserName          : VSPHERE.LOCAL\Administrator
Datacenter        : Test-DC
ComputeResource   : Test Cluster
Host              : 192.168.1.187
Vm                : New-VM
Message           : Migrating New-VM from 192.168.1.187, ISCSI-2 to 192.168.1.188, ISCSI-2 in
                    Test-DC
NewConfigSettings :

 
We can easily see the VM name, the source host/datastore and the destination host/datastore.

What is this empty property named NewConfigSettings ?
It is populated only for “reconfigure” operations on VMs. It contains the VM setting(s) which have been changed and their new value :

PowerCLI C:\> Get-VM "New-VM" | Audit-vCenterOperation -OperationType Reconfigure -Start (Get-Date).
AddDays(-7)


Key               : 10376
CreatedTime       : 11/24/2015 7:59:08 AM
UserName          : VSPHERE.LOCAL\Administrator
Datacenter        : Test-DC
ComputeResource   : Test Cluster
Host              : 192.168.1.188
Vm                : New-VM
Message           : Reconfigured New-VM on 192.168.1.188 in Test-DC
NewConfigSettings : @{ChangeVersion=2015-11-13T16:22:54.801558Z;
                    Files=VMware.Vim.VirtualMachineFileInfo; MemoryMB=3072; NumCPUs=2;
                    VmProfile=VMware.Vim.VirtualMachineProfileSpec[]}



PowerCLI C:\> Get-VM "New-VM" | Audit-vCenterOperation -OperationType Reconfigure -Start (Get-Date).
AddDays(-7) | Select-Object -ExpandProperty NewConfigSettings | Format-List *


ChangeVersion : 2015-11-13T16:22:54.801558Z
Files         : VMware.Vim.VirtualMachineFileInfo
MemoryMB      : 3072
NumCPUs       : 2
VmProfile     : {VMware.Vim.VirtualMachineEmptyProfileSpec}

 
In this case, the number of vCPUs and the memory amount were changed. Unfortunately, there is no easy way to get the original values of the changed settings, so it gets only the new values.

If we have an ESXi host which have been rebooted and we want to know if it was a user-initiated action (and if yes, by who) :

vCenterOperation power
 
Aha ! It was the (suspiciously named) user “RogueAdmin”.

To track down snapshot operations which happened today on a VM named “New-VM”, here is how to do it :

vCenterOperation Snapshot
 
This “RogueAdmin” user is definitely doing weird things. Let’s track down everything he has done (output cut for brevity) :

PowerCLI C:\> Audit-vCenterOperation -Username "VCLOUD\RogueAdmin"


Key               : 13157
CreatedTime       : 11/26/2015 10:03:30 AM
UserName          : VCLOUD\RogueAdmin
Datacenter        :
ComputeResource   :
Host              :
Vm                :
Message           : User VCLOUD\RogueAdmin@192.168.1.10 logged in as VMware vim-java 1.0
NewConfigSettings :

Key               : 13162
CreatedTime       : 11/26/2015 10:04:58 AM
UserName          : VCLOUD\RogueAdmin
Datacenter        : Test-DC
ComputeResource   : Test Cluster
Host              : 192.168.1.188
Vm                :
Message           : User logged event: Because I can
NewConfigSettings :

 
That’s it.

This Audit-vCenterOperation is in a module available here. It requires Powershell 3.0 or later and PowerCLI 5.5 or later.

Configure ESXi host RAMdisks with PowerCLI

The symptoms of a full RAMdisk on a ESXi host can be pretty nasty and diverse. The possible causes are also very diverse (search “ramdisk full” in the VMware Knowledge Base, you will see many articles). Also, it can be affecting the RAMdisk “root”, “tmp”, or even the RAMdisk “hostdstats”, depending on the cause, so this is not easy to troubleshoot.

To help prevent this type of issues, we can increase the size of ESXi RAMdisks by increasing their memory reservation, memory limit and set their reservation as “expandable”, just like in resource pools.

This corresponds to settings in “System Resource Allocation” :

System resources allocation

Let’s see how we can configure this in a more automated way, with PowerCLI.

PS C:\> $ESXiHosts = Get-VMHost
PS C:\> $Spec = New-Object VMware.Vim.HostSystemResourceInfo

Here, we save all our ESXi Hosts into a variable for later use, because we want to configure all the ESXi hosts in the vCenter. We also create a new, empty HostSystemResourceInfo object, which we are going to populate with the memory settings we want.

Now, the tricky part is to use the appropriate key, depending on the RAMdisk we want to configure. This can be one of 3 possible RAMdisks that we might want to configure, so this is a good candidate for a Switch statement :

PS C:\> $RamDisk = "tmp"

PS C:\> switch ($RamDisk) {
             'tmp' {$Spec.Key = "host/system/kernel/kmanaged/visorfs/tmp"}
             'root' {$Spec.Key = "host/system/kernel/kmanaged/visorfs/root"}
             'hostdstats' {$Spec.Key = "host/system/kernel/kmanaged/visorfs/hostdstats"}
         }

As an example, we are going to configure the “tmp” RAMdisk.
Then, we create a new, empty ResourceConfigSpec object and store it into our Config property :

PS C:\> $Spec.Config = New-Object VMware.Vim.ResourceConfigSpec
PS C:\> $Spec.Config


Entity           :
ChangeVersion    :
LastModified     :
CpuAllocation    :
MemoryAllocation :
LinkedView       :
DynamicType      :
DynamicProperty  :


Even though, the CPU allocation is not applicable to a RAMdisk, we need to create one and assign it to the CpuAllocation property of our ResourceConfigSpec. Why ? Because the vSphere API won’t let us apply the ResourceConfigSpec to a host, if the CpuAllocation or the MemoryAllocation property is null.

PS C:\> $Spec.Config.cpuAllocation = New-Object VMware.Vim.ResourceAllocationInfo

Now, let’s set the memory reservation to 30 MB, the limit to 400 MB and the reservation as expandable. Expandable reservation means that more than the reservation can be allocated to the RAMdisk if there are available resources in the parent resource pool.

PS C:\> $Spec.Config.memoryAllocation = New-Object VMware.Vim.ResourceAllocationInfo
PS C:\> $Spec.Config.memoryAllocation.Reservation = 30
PS C:\> $Spec.Config.memoryAllocation.Limit = 400
PS C:\> $Spec.Config.memoryAllocation.ExpandableReservation = $True

Now, it’s time to apply the configuration to each individual ESXi host :

Foreach ($ESXiHost in $ESXiHosts) {
    $Spec.Config.ChangeVersion = $ESXiHost.ExtensionData.SystemResources.Config.ChangeVersion
    $ESXiHost.ExtensionData.UpdateSystemResources($Spec)
}

What is this ChangeVersion business ?

We get the version identifier of the current ESXi host configuration and we make sure the ChangeVersion property in our ResourceConfigSpec matches with it. This is to prevent problems in case the ESXi host configuration was changed between the moment we last read it and the moment we apply a new ResourceConfigSpec to it. For more information, you can refer to this documentation page.

Lastly, we apply the resource allocation settings contained in our $Spec, using the method UpdateSystemResources of our HostSystem view (we used the ExtensionData property above, but it is the same as a view).

Putting it all together :

Using these techniques, I wrote a function called Set-VMHostRamDisk and packaged it in a module available here.

As you can see below, it is fully parameterized and accepts one or multiple ESXi hosts from the pipeline :

Set-VMHostRamDisk

I took the time to write a proper comment-based help, so if you need more information on how to use the function, Get-Help is your BFF.

A PowerCLI alternative to the Storage Reports feature

As you may know, the Storage Views and Storage Reports features have been removed from vSphere 6. Here is the official (and laconic) statement from the vSphere 6.0 release notes :

“vSphere Web Client. The Storage Reports selection from an object’s Monitor tab is no longer available in the vSphere 6.0 Web Client.
vSphere Client. The Storage Views tab is no longer available in the vSphere 6.0 Client.

 
Quite a few customers were unhappy and asking what we were offering as a replacement/alternative.

To ease the pain of some customers, I wrote a PowerCLI alternative for the defunct Storage Reports feature. It is also a good way to showcase PowerCLI capabilities because it is a very typical usage of PowerCLI : extracting the information you need from different objects and grouping these pieces of information into custom objects.

The resulting PowerCLI module, Get-StorageViewsReport.psm1, made its way to a public knowledge base article with examples and screenshots of its usage. So, all you need to know to use this module is in the KB article and in the module Help accessible via Get-Help.

It obtains storage capacity and utilization information by datastore, by VM or by ESXi host. It provides the same information as the Storage Views reports.

It requires PowerCLI 5.5 or later and Powershell 3.0 or later.

You can download the module from the KB article but it is not up-to-date, so I would recommend to get it from GitHub to get the latest version. This version adds support for PowerCLI 6.0.

What’s the difference between ToolsVersionStatus and ToolsVersionStatus2

Recently, I had a customer who wanted to check if the VMware Tools were installed and up-to-date using PowerCLI.

An relatively easy way to do this is with a VirtualMachine view or the ExtensionData of a VM object :

PS C:\> $VMView = Get-VM -Name Test-VM | Get-View

PS C:\> $VMView.Summary.Guest

GuestId             :
GuestFullName       :
ToolsStatus         : toolsNotRunning
ToolsVersionStatus  : guestToolsCurrent
ToolsVersionStatus2 : guestToolsCurrent
ToolsRunningStatus  : guestToolsNotRunning
HostName            :
IpAddress           :
DynamicType         :
DynamicProperty     :

Looking at the above output, his question was :

“Should I use ToolsVersionStatus or ToolsVersionStatus2 ? What is the difference between these two ?”

He was using vSphere 5.5, so a good place to start is the vSphere API Reference Documentation for vSphere 5.5.
But what are we looking for ?
To search in the API documentation, we first need to know the type of the object we are investigating :

PS C:\> $VMView.Summary.Guest.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    VirtualMachineGuestSummary               VMware.Vim.DynamicData

 

So, in our case we are going to look for the data object type : VirtualMachineGuestSummary.

API documentation

After locating VirtualMachineGuestSummary in the long list of data object types, we can see the properties for this type :

toolsVersionStatus :
Deprecated. As of vSphere API 5.0 use toolsVersionStatus2. Current version status of VMware Tools in the guest operating system, if known. Since vSphere API 4.0

toolsVersionStatus2 :
Current version status of VMware Tools in the guest operating system, if known.
Since vSphere API 5.0

So “toolsVersionStatus” is deprecated and its younger brother should be used if we use the vSphere API 5.0 or later.

This leads to yet another question : What if the vCenter Server is 5.5 but it has some old 4.x ESXi hosts and I might connect PowerCLI directly to these hosts ? How can I verify which version of the vSphere API I am querying ?

It is pretty simple because we already have a VirtualMachine view ($VMView) and PowerCLI views have a property named “Version” which is nested in “Client” and provides the API version for the current view :

PS C:\> $VMView.Client

Version          : Vim55
VimService       : VimApi_55.VimService
ServiceContent   : VMware.Vim.ServiceContent
ServiceUrl       : https://192.168.1.10/sdk
ServiceTimeout   : 100000
CertificateError : System.EventHandler`1[VMware.Vim.CertificateErrorEventArg]

 

So to check if the tools are up-to-date or not, taking into account a possible ESXi 4.x host, we could do something like this :

If ( $($VMView.Client.Version.ToString()) -like "Vim4*" ) {

    $Props = @{'Name'=$VM.Name
               'ToolsVersionStatus'=$VM.ExtensionData.Summary.Guest.ToolsVersionStatus}
}
Else {
        $Props = @{'Name'=$VM.Name
               'ToolsVersionStatus'=$VM.ExtensionData.Summary.Guest.ToolsVersionStatus2}
}
$CustomObj = New-Object -TypeName psobject -Property $Props

 

Backup/restore vCenter tags and tag assignments

Tags were introduced in vSphere 5.5, they are very versatile and are gaining more and more adoption. Because of this, resetting the Inventory Service database is becoming more and more problematic.
Remember, whenever you reset the Inventory Service database, you lose all tags.

I tested this in my lab : I reset the Inventory Service DB using my Powershell Script : Reset-ISDatabase .

Then, I checked back in the Web Client, and indeed, the tags, tag categories and tag assignments (which vCenter objects the tags are associated to) are all gone :

Tags are deleted

So, we need a solution to backup tags, categories and tag assignments to be able to restore them if we lose them all.
This script is great but it doesn’t take into account the tag assignments (it’s not its purpose).

For the sake of reference, here is the current state of the tags, categories and assignments in my test environment:

Tags and Assignments

Now, let’s see how we can export this information.

$TagCategories = Get-TagCategory
$Tags = Get-Tag
$TagAssignments = Get-TagAssignment

# Grouping the tag categories, the tags and the tag assignments into an array
$ExportArray = @($TagCategories,$Tags,$TagAssignments)

Export-Clixml -InputObject $ExportArray -Path Exported.xml

We group the tag categories, tags and tag assignments into an array because even though the cmdlet Export-Clixml seems to take multiple objects without complaining, according to Get-Help, it is supposed to only take one input object at a time.

We chose to export these objects to XML, rather than CSV because XML format is better suited for complex objects : objects with multi-valued properties or properties containing nested properties.

Now, let’s see how to use this XML file to restore all the categories, tags and assignments :

We import the data from the XML file back into PowerShell objects, like so :

$Import = Import-Clixml -Path Exported.xml

Remember it is an array, so we need to access the first element in the array to get the categories, the second element to get the tags and the third element to get the assignments.
For example, here is how to get the categories :

$Import[0] | fl *


Description : 
Cardinality : Single
EntityType  : {Datastore, DatastoreCluster}
Id          : InventoryServiceCategory-5ec0a856-78a4-4c3c-afb3-1c7d62b92537
Name        : Storage
Uid         : /VIServer=administrator@192.168.1.10:443/TagCategory=InventoryServiceCategory-5ec0a856-78a4-4c3c-afb3-1c7d62b92537/
Client      : VMware.VimAutomation.ViCore.Impl.V1.VimClient

Description : 
Cardinality : Single
EntityType  : {VApp, VirtualMachine}
Id          : InventoryServiceCategory-39c5fc34-4d8c-441a-b991-cd18390753b5
Name        : Business Impact
Uid         : /VIServer=administrator@192.168.1.10:443/TagCategory=InventoryServiceCategory-39c5fc34-4d8c-441a-b991-cd18390753b5/
Client      : VMware.VimAutomation.ViCore.Impl.V1.VimClient

So, here is how to create the categories from the imported XML data :

Foreach ( $category in $Import[0] ) {

    New-TagCategory -Name $category.Name -Description $category.Description `
    -Cardinality $category.Cardinality -EntityType $category.EntityType
}

To re-create the tags, we use the same technique :

Foreach ( $tag in $Import[1] ) {

    New-Tag -Name $tag.Name -Category (Get-TagCategory -Name $tag.Category) `
    -Description $tag.Description
}

We have to use Get-TagCategory -Name $tag.Category to convert the value of $tag.Category back to an object of the type : “VMware.VimAutomation.ViCore.Types.V1.Tagging.TagCategory”.
Why ? Because the parameter -Category of New-Tag can only take a value of that type (Get-Help New-Tag can confirm that) and the specific type for this value was lost during the export to XML.

Now, let’s restore the tags assignments :

Foreach ( $assignment in $Import[2] ) {

    $AssignTag = (Get-Tag -Name $assignment.Tag.Name)
    $AssignEntity = Get-VIObjectByVIView -MORef ($assignment.Entity.Id)

    New-TagAssignment -Tag $AssignTag -Entity $AssignEntity
}

The tricky part here is the $AssignEntity. The parameter -Entity of the cmdlet New-TagAssignment expects a VIObject, which could be a VM, a host, a datastore … any kind of vCenter object and our imported data represents the entity Id (aka MORef) as a string :

PS C:\> $Import[2].Entity.Id

Datastore-datastore-26
VirtualMachine-vm-62
StoragePod-group-p64

So we need to convert that back to the original VIObjects and that’s what Get-VIObjectByVIView allows us to do with the parameter -MORef.

Now, we have the same tags, categories and assignments as before but they have new UIDs so from vCenter point of view, they are different objects. This might have an impact on other sofware relying on vCenter tags, like vCloud Director for example. So, as always, test this in a lab before messing with your production environment !

Based on that, I made 2 functions Export-TagAndAssignment and Import-TagAndAssignment, which I packaged in a nice little module called vCenterTagging.psm1.
The module is available here.