05 August 2012

Indy VMUG Demo Day 2012 PowerCLI Lab

We, as VMUG members in Indianapolis, IN, volunteered to create a PowerCLI lab for this year's Demo Day event.  This post is not to talk about the number of hours we put into it, or how awesome it was (that goes without saying), but is to talk just a bit about the lab guide, make it available, and to share a bit of the on-the-spot code we wrote to manage the lab environment.

The goal of this lab was to continue on from last year's lab, the PowerCLI lab from Demo Day 2011.

This year's lab starts out with some 101/102-level types of exercises and info, and then progresses into 200-level PowerCLI (and maybe a bit beyond).  I cut off the lab guide at thirty (30) pages -- there is so much to cover, and thirty pages got us through about 75% of the material we outlined to discuss.  Now we have leftovers for next time.

This year's lab guide is available at:

http://static.vnugglets.com/vmugdd2012/

and, if you want it for your offline reading pleasure, it is available for download at:

http://static.vnugglets.com/files/VMUGDemoDays2012-PowerCLILabGuide.zip

Have a look.  There are some lab-environment-specific items in the guide, like vCenter server names, VM Host names, VM names, etc., but the knowledge still carries/shines through.  If there are things that do not make sense or on which you have questions, hit us up -- let us know.

The environment the day of the event:  there was definite room for improvement.  The infrastructure was not put in until the day before the event, which makes building it out a bit of a rush, and does not provide for much capacity/performance testing.  Unfortunately, the user experience suffered due to sub-optimal infrastructure performance.

As for managing the lab -- setting up vApps, configuring the View desktops from which users accessed their individual labs, resetting lab environments when users were done with their lab, etc., we had a running doc in which we put code snippets that we used throughout the day.  I have posted it below.

Keep in mind, this is not a polished set of code, not a top-shelf stack of command, not a suggestion on how to run your show -- it is what evolved the day of the event.  We are posting it here for the entertainment/educational value, to show the answer to the "how did you do that on the day of the event?".  So, for fun, here is the mostly-raw code we came up with and used.  Enjoy:
## various code snipplets used in course of managing lab env.
## vNugglets, Jul 2012

## get the vC admin creds
$credVCAdmin = Get-Credential administrator

Connect-VIServer vmug-vc01 -Credential $credVCAdmin


## to start SSH on VMHosts
Get-VMHost | Get-VMHostService | ?{$_.Key -eq "TSM-SSH"} | ?{$_.Running -eq $false} | Start-VMHostService -Confirm:$false
## to stop SSH on VMHosts
Get-VMHost | Get-VMHostService | ?{$_.Key -eq "TSM-SSH"} | ?{$_.Running -eq $true} | Stop-VMHostService -Confirm:$false


## get creds for ESXi hosts
$credESXiRoot = Get-Credential root

## arr of all VMHosts names
$arrVMHostNames = Get-VMHost | %{$_.Name}

## cmd to add the given line to the config file on VMHosts
$strCmdToRun = "echo 'vhv.allow = ```"TRUE```"' >> /etc/vmware/config"
## cmd to echo out the config file
#$strCmdToRun = "cat /etc/vmware/config"
$arrVMHostNames | %{plink.exe -l root -pw rootPasswd $_ $strCmdToRun}



## added add'l portgroup to the std vSwitch0
Get-Cluster | Get-VMHost | Get-VirtualSwitch -Name vSwitch0 | %{New-VirtualPortGroup -VirtualSwitch $_ -Name PowerCLI-32 -VLanId 132 -Confirm:$false}
Get-Cluster | Get-VMHost | Get-VirtualSwitch -Name vSwitch0 | %{New-VirtualPortGroup -VirtualSwitch $_ -Name SRM-32 -VLanId 232 -Confirm:$false}


###############
## clone vApps
## name of cluster in which to make vApps
$strVDIAndMgmtClustername = "VDI and Management"
## prefix name of the vApp
#$strVAppNamePrefix = "SRM"
$strVAppNamePrefix = "PowerCLI"
## the name of the source vApp to clone
$strSourceVAppName = "base_PowerCLI_done"
#$strSourceVAppName = "base_SRM-00_CLONE"

## clone the vApp
21..25 | %{
    $strNewVAppName = "$strVAppNamePrefix-{0:d2}" -f $_
    #$dstToUse = Get-Datastore vapp-vmfs* | sort FreeSpaceMB -Descending:$true | select -First 1
    $dstToUse = Get-Datastore vapp-vmfs* | Get-Random
    New-VApp -Name $strNewVAppName -Location (Get-Cluster $strVDIAndMgmtClustername | Get-VMHost | Get-Random) -Datastore $dstToUse -VApp $strSourceVAppName -RunAsync
} ## end foreach-object

## when clones are done, can move the vApps to the given folder
$strFolderToWhichToMoveVApp = "${strVAppNamePrefix}_vApps"    ## vApp inventory folder is PowerCLI_vApps or SRM_vApps
$oDestFolder = Get-Folder $strFolderToWhichToMoveVApp
Get-VApp "$strVAppNamePrefix*" | ?{$_.ExtensionData.ParentFolder.ToString() -ne $oDestFolder.Id} | Move-VApp -Destination $oDestFolder

## set proper NetworkName for NIC on VMs in the vApps (portgroup names match the vApp names); -NetworkName param is cASeSensitIVe!
21..25 | %{
    $intLabNumber = $_
    $strVAppName = "$strVAppNamePrefix-{0:d2}" -f $intLabNumber
    Get-VApp $strVAppName | Get-VM | Get-NetworkAdapter | Set-NetworkAdapter -NetworkName $strVAppName -Confirm:$false 
} ## end foreach-object

## end clone vApps
###############



###############
## prep vApps
## take snapshots of vApp VMs -- do in PoweredOn or PoweredOff state?
#$strVAppNamePrefix = "SRM"
$strVAppNamePrefix = "PowerCLI"
21..25 | %{
    ## make vApp name
    $strNewVAppName = "$strVAppNamePrefix-{0:d2}" -f $_
    Get-VApp $strNewVAppName | Get-VM | New-Snapshot -Name "0" -Description "Initial state" -Memory -Quiesce -Confirm:$false -RunAsync
} ## end foreach-object

21..25 | %{$strNewVAppName = "$strVAppNamePrefix-{0:d2}" -f $_; Get-VApp $strNewVAppName | Start-VApp -Confirm:$false -RunAsync}
## for the PowerCLI stuff -- restart the ESXi VMs so that vC shows them as powered on
#21..25 | %{$strNewVAppName = "$strVAppNamePrefix-{0:d2}" -f $_; Get-VApp $strNewVAppName | Get-VM esxi* | Restart-VM -Confirm:$false}
21..22 | %{
    ## make vApp name
    $strNewVAppName = "$strVAppNamePrefix-{0:d2}" -f $_
    Get-VApp $strNewVAppName | Get-VM | New-Snapshot -Name "1" -Description "Powered on" -Memory -Quiesce -Confirm:$false -RunAsync
} ## end foreach-object
## end prep vApps
###############


$strVDIVMsBaseName = "PowerCLI"
21..25 | %{Get-VM $("vm-${strVDIVMsBaseName}-{0:d2}" -f $_)} | %{
#Get-VM "vm-${strVDIVMsBaseName}-*" | %{
    $strVMName = $_.Name
    Get-NetworkAdapter -VM $_ -Name "Network Adapter 2" | Set-NetworkAdapter -NetworkName $strVMName.Trim("vm-") -Confirm:$false -WhatIf
} ## end foreach-Object
#-- CHECK ON POWERCLI-06, SRM-03, SRM07 -- not sure 2nd nic is on right virtual network


###############
## reset vApp for next user -- revert all VMs in the vApp to snapshot
$strVAppToReset = "PowerCLI-03"
$strSnapshotName = "1"

$vappToReset = Get-VApp $strVAppToReset
## stop VMs in vApp
#$vappToReset | Get-VM | Stop-VM -Confirm:$false
$vappToReset | Get-VM | Set-VM -Snapshot $strSnapshotName -Confirm:$false

## start the vApp if reverting to snapshot 0 (if snapshot 1, the VMs were on)
#$vappToReset | Start-VApp -Confirm:$false
Get-VM "vm-$strVAppToReset" | Restart-VMGuest -Confirm:$false
## end reset vApp -- revert all VMs in the vApp to snapshot
###############


## reporting stuff
## show snapshots for each vApp
11..20 | %{Get-VApp powercli-$_} | %{$strVAppName = $_.Name; Get-VM -Location $_ | Get-Snapshot | Select @{n="vapp";e={$strVAppName}},Name,VM}

And, other guys besides Allen and Matt of vNugglets.com and the VMUG team that put in work to make the labs happen:  big Joe Filippello (@joefilippello, filippello.com) and Jaron Hilger (@hilgertech).  Thanks, guys.

02 July 2012

VMware View reporting -- PowerShell without Broker-based cmdlets -- Part 0

The need:  reportability/manageability of VMware View environments
The tool from VMware:  PowerShell Integration with VMware View -- a PowerShell snapin provided with VMware View.

Some problems:
  • must be used on the Broker server on which VMware View Connection Server software is running (local or via PSRemoting)
  • slow
  • has not been updated since View 4.5 release
  • slow!
So, I checked around for alternatives, and were eventually put onto the local ADAM instance that runs on each Connection Server.  After some inspection/investigation (thanks, Sysinternals AD Explorer), I found how to use the data in the ADAM database to return valuable VMware View environment information in a timely fashion.

LDAP and Quest's AD cmdlets?  Sounds good.

Overview:  by using Quest's Get-QAD* cmdlets and querying the local ADAM database on a Connection Server, most any VMware View info can be had.  As for speed -- with the proper planning, the reporting we are doing is far faster than using the standard View cmdlets on a Connection server -- in some cases, an order of magnitude faster, in some cases, an even greater increase!  A quick example of the amount of time for getting pools, their template, and their description for an environment with about 75 pools by each method:
using standard View cmdlets on the Brokerabout 70 seconds
querying LDAP to grab the desired infoless than one (1) second!

Obviously, some serious speed gains to be had.  More on those results later.

To dig in:  A couple of key organizational units ("OUs") and basic object types/properties that help along the way to being able to mine this info:
DescriptionValue
pool objects' OU distinguished nameOU=Applications,DC=vdi,DC=vmware,DC=int
pool objectClasspae-DesktopApplication
pool property that specifies the pae-ServerPool object which contains additional pool settings<poolObject>.{pae-Servers}
pool property that specifies the pool's display name<poolObject>.{pae-DisplayName}

Further info about row three -- the "pae-ServerPool" LDAP objects hold further info about the actual View pool with which they are associated.

And, so as to show the ADAM DB object types and OUs for some items, I labeled a snippet of a screenshot from the View Admin console:
labeled View Admin web console screenshot snippet

So, to get some pool info (display name, description, template/parent VM name) for VDI pools using Get-QADObject:
## some juicy properties to retrieve with the pae-ServerPool (Pool additional info) objects
$arrIncludedProperties = "pae-DisplayName,description,pae-VmTemplateName,pae-SVIVmParentVM,Name".Split(",")

## use Get-QADObject to connect to the given ADAM instance (on the given Connection Server), retrieve objects and return info
Get-QADObject -Service myConnectionServer.domain.com -DontUseDefaultIncludedProperties -IncludedProperties $arrIncludedProperties -LdapFilter "(objectClass=pae-ServerPool)" -SizeLimit 0 | Sort-Object "pae-DisplayName" | Select-Object Name, "pae-SVIVmParentVM"

which returns some info like:
...
Name                pae-SVIVmParentVM
----                -----------------
testPool            /datacenter2/vm/XP/Imgs/templV101
testPool2           /datacenter2/vm/XP/Imgs/4v101
test-2011           /datacenter0/vm/XP/Imgs/hangarV101
jamPool             /datacenter0/vm/XP/Imgs/slaughterv101
...
Note: "pae-SVIVmParentVM" is the path to the parent VM for a linked-clone pool

If you are not quite following the -LdapFilter parameter in the code above, it is, as the parameter name suggests, a way to filter the objects returned in the query.  In this case, it helps to return just the items that are the "additional info about pools and their settings" objects.  A quick but good bit about LDAP query syntax is available in the Search and Compare section about LDAP at wikipedia.org.

Ok, good start. Now, put that into a function, and try a few other calls:
## Script purpose:  function to get pool info (display name, description, template/parent VM name) for VDI pools
## Author:  vNugglets.com -- Feb 2012
function Get-VNVDIPoolTemplateInfo {
    #Requires –PsSnapIn Quest.ActiveRoles.ADManagement
    param (
        ## one of the connection servers of the given VDI environment
        [string]$connectionServerName_str = "someBroker.domain.com"
    ) ## end param

    $arrIncludedProperties = "pae-DisplayName,description,pae-VmTemplateName,pae-SVIVmParentVM,Name".Split(",")
    Get-QADObject -Service $connectionServerName_str -DontUseDefaultIncludedProperties -IncludedProperties $arrIncludedProperties -LdapFilter "(objectClass=pae-ServerPool)" -SizeLimit 0 | Sort-Object "pae-DisplayName" | Select-Object $arrIncludedProperties
} ## end fn
Some sample usage:
## Gets template-based pools and their description and template info
PS C:\> Get-VNVDIPoolTemplateInfo -connectionServer someBroker.domain.com | ?{$_."pae-VmTemplateName"} | select pae-DisplayName,pae-VmTemplateName

pae-DisplayName             pae-VmTemplateName
---------------             ------------------
Sharepoint Crew             /datacenter0/vm/XP/Templates/myOldTemplate0
Developer POC               /datacenter0/vm/XP/Templates/myTemplate1
...
## gets VDI pools whose linked clone "parent" property is like "*v101", gives pool ID and the linked clone parent VM info
PS C:\> Get-VNVDIPoolTemplateInfo -connectionServer someBroker.domain.com | ?{$_."pae-SVIVmParentVM" -like "*v101"} | select Name,pae-SVIVmParentVM,description

Name                pae-SVIVmParentVM                       Description
----                -----------------                       -----------
testPool            /datacenter2/vm/XP/Imgs/templV101       Temporary testing pool...
testPool2           /datacenter2/vm/XP/Imgs/4v101           Old "next-gen" pool for..
...

As for those speed gains reported above -- part of why querying the LDAP directory (ADAM DB) is soo much faster than the standard View cmdlets is the focus/scope of the queries.  Using the View cmdlets pulls back a prescribed amount of data about the objects in question, pools in this example. Cheers to VMware for providing the cmdlets.  But, there is some work to be done with them, still.  Meanwhile, specifying just the necessary properties/objects/attributes to get the desired info helps keep things fast (FaF).  The actual results of the speed tests performed:
## on broker console, native cmdlet
PS C:\> Measure-Command {Get-Pool | select pool_id,parentVMPath,description}
...
TotalSeconds      : 72.4010917
...
## on remote machine using vNugglets function
PS C:\> Measure-Command {Get-VNVDIPoolTemplateInfo -connectionServerName connectionServer.somedomain.com | select Name,pae-SVIVmParentVM,description}
...
TotalSeconds      : 0.6809141
...

What's that?  100+ times faster?  Nice.  Again, the speed gain is thanks to the planning and the upfront cost of writing the specific ADAM queries -- it is not pure magic (just only a little).

And, some of the other places on the web that talk about alternative ways to mine View data (mostly without using [slow] cmdlets local to the View Broker) that may be of interest:
Wait, there is more! But, for the sake of having a reasonable length for this post, I will save those for now. Upcoming pieces in this series include further discussion about the OUs/objects in the View ADAM DB and how to use them for looking at pool entitlees, finding out what pools use a given parent / template VM, and reporting on pool VMs and their assigned users.

06 February 2012

Change VM Boot Order via PowerShell

There was a recent question in the VMware PowerCLI community about changing the boot order for a VM (Change Boot Sequence with new Extensiondata option).  In looking into this, I found that the VirtualMachineBootOptions item has a BootOrder property with which one can change the VM's boot order (new in vSphere API 5.0).

Upon searching about to see if there were already examples of using this property for changing boot order, I found none.  There are places that talk about changing/limiting what boot devices a VM may use in order to boot from the given device, but none that dealt with changing the actual boot order on the VM.  So, I whipped out an example (that person was wanting to use the ExtensionData property of a VM object).

The BootOrder property can have four (4) different types of devices (which are VirtualMachineBootOptionsBootableDevice objects): CDRom, disk, ethernet, and floppy.

The CDRom boot device does not correspond to any particular CDRom on the VM -- per the docs, when the boot order specifies CDRom, the first CDRom with bootable media found is used.

The disk and ethernet boot devices are related to the actual VirtualDisk and VirtualEthernetCard devices on the VM by specifying the corresponding Keys for the given devices.

Here is an example of setting the boot order for a VM to NIC2, Disk1, CD:
## the VM to configure
$strVMName = "myVM0"
## the device name of the NIC to which to boot
$strBootNICDeviceName = "Network adapter 2"
## the device name of the hard disk to which to boot
$strBootHDiskDeviceName = "Hard disk 1"
## get the .NET View object for the VM, with a couple of select properties
$viewVM = Get-View -ViewType VirtualMachine -Property Name, Config.Hardware.Device -Filter @{"Name" = "^$strVMName$"}

## get the VirtualEthernetCard device, and then grab its Key (DeviceKey, used later)
$intNICDeviceKey = ($viewVM.Config.Hardware.Device | ?{$_.DeviceInfo.Label -eq $strBootNICDeviceName}).Key
## bootable NIC BootOption device, for use in setting BootOrder (the corresponding VirtualEthernetCard device on the VM has PXE enabled, assumed)
$oBootableNIC = New-Object -TypeName VMware.Vim.VirtualMachineBootOptionsBootableEthernetDevice -Property @{"DeviceKey" = $intNICDeviceKey}

## get the VirtualDisk device, then grab its Key (DeviceKey, used later)
$intHDiskDeviceKey = ($viewVM.Config.Hardware.Device | ?{$_.DeviceInfo.Label -eq $strBootHDiskDeviceName}).Key
## bootable Disk BootOption device, for use in setting BootOrder (the corresponding VirtualDisk device is bootable, assumed)
$oBootableHDisk = New-Object -TypeName VMware.Vim.VirtualMachineBootOptionsBootableDiskDevice -Property @{"DeviceKey" = $intHDiskDeviceKey}

## bootable CDROM device (per the docs, the first CDROM with bootable media found is used)
$oBootableCDRom = New-Object -Type VMware.Vim.VirtualMachineBootOptionsBootableCdromDevice

## create the VirtualMachineConfigSpec with which to change the VM's boot order
$spec = New-Object VMware.Vim.VirtualMachineConfigSpec -Property @{
    "BootOptions" = New-Object VMware.Vim.VirtualMachineBootOptions -Property @{
        ## set the boot order in the spec as desired
        BootOrder = $oBootableNIC, $oBootableHDisk, $oBootableCDRom
    } ## end new-object
} ## end new-object

## reconfig the VM to use the spec with the new BootOrder
$viewVM.ReconfigVM_Task($spec)

To display the VM's BIOS boot order after the update (not terribly obvious from the output):
PS C:\> ## get the updated View info about the given property
PS C:\> $viewVM.UpdateViewData("Config.BootOptions.BootOrder")
PS C:\> ## display the BootOrder
PS C:\> $viewVM.Config.BootOptions

BootDelay        :
EnterBIOSSetup   :
BootRetryEnabled :
BootRetryDelay   :
BootOrder        : {4001, 2000, VMware.Vim.VirtualMachineBootOptionsBootableCdromDevice}
DynamicType      :
DynamicProperty  :

...where 4001 is the DeviceKey of the second virtual NIC in the VM, and 2000 is the DeviceKey for the first virtual disk.

Original BIOS Boot settings:

BIOS Boot settings after updating them with the script:

Notes about setting the boot order in this manner:  once you have done so on a VM, it appears that the boot order can only be changed via script.  Notice in the first screenshot that one may use keys to navigate between items, whereas the second screenshot states that "All items on this menu cannot be modified in user mode". Something to keep in mind.  Additionally, this method does not seem to have control over where the "Removable Devices" item appears in the boot order.

Enjoy!

16 January 2012

Show Currently Connected vCenter Servers in PowerCLI 5.0.1

PowerCLI 5.0.1 was recently released and vNuggs decided to check it out. Almost immediately we noticed something was missing. PowerCLI was no longer reporting in the title bar the vCenter server(s) to which we were connected.

Example of a single connected server before PowerCLI 5.0.1:


Example of multiple connected servers before PowerCLI 5.0.1:


AC decided to make a quick post to the PowerCLI community to see if others had noticed it and was hoping the PowerCLI team would respond as to whether this was intentional or not and what the future plans are for this functionality.  So far, no word from the team, but others have noticed the change as well.

However, in the meantime, we decided to solve the problem ourselves with some functions.  The first function is the one that does all the real work--ChangeTitleBar:

## Function to change title bar to reflect currently connected vCenters
## Author: vNugglets.com -- Jan 2012

function Change-TitleBar() {
    ## check to see if there are any currently connected servers
    if ($global:DefaultVIServers.Count -gt 0) {
        ## since there is at least one connected server, modify the window title variable accordingly
        $strWindowTitle = "[PowerCLI] Connected to {0} server{1}:  {2}" -f $global:DefaultVIServers.Count, $(if ($global:DefaultVIServers.Count -gt 1) {"s"}), (($global:DefaultVIServers | %{$_.Name}) -Join ", ")
    }
    else {
        ## since there are no connected servers, modify the window title variable to show "not connected"
        $strWindowTitle = "[PowerCLI] Not Connected"
    }
    ## perform the window title change
    $host.ui.RawUI.WindowTitle = $strWindowTitle
}

In line 6 the function merely checks the Count property of the array DefaultVIServers to ensure that you are connected to at least one server, then updates a variable with the new window title text in line 8. Note that we are omitting the name of the connected user.  We went this route to keep the function simple and because even VMware didn't include this info when connected to multiple servers (see screen shots above) and in the end, it is not something we find all that important/useful. It could simply be added by using the User property of the DefaultVIServers variable.

In line 12 (the "else" portion of the "if" statement) we just set the variable to what we selected as our default window title text.  Change this to your own preference (note that it doesn't bother reporting the PowerCLI version either, but that's easy enough with the Get-PowerCLIVersion cmdlet).

Finally, line 15 actually sets the window title to the defined value.  Now you just need to place this function in your PowerShell profile and make calls to it when you connect to/disconnect from VI servers.  You can do this by writing custom functions for Connect-VIServer and Disconnect-VIServer as follows:

## Modified "Connect- and Disconnect-VIServer" functions that call "Change-TitleBar" function
## Author: vNugglets.com -- Jan 2012

## Function for the "Connect-VIServer" cmdlet
function ConnectServer([string] $strVCenterName) {
    Connect-VIServer -Server $strVCenterName
    Change-TitleBar
}

## Function for the "Disconnect-VIServer" cmdlet
function DisconnectServer([string] $strVCenterName = "*") {
    Disconnect-VIServer -Server $strVCenterName -Confirm:$false
    Change-TitleBar
}

There is absolutely nothing fancy about these functions and they are not intended as direct feature-for-feature replacements of the actual Connect- and Disconnect-VIServer cmdlets.  The ConnVIServer function simply takes a string parameter and calls the actual Connect-VIServer cmdlet with that string specified as the server to connect to.  It then calls the Change-TitleBar function to do the title bar magic.  The DisconnVIServer function does the same, except in this case we've set a default parameter value of "*" just for convenience when you want to disconnect from all connected servers.  You'd merely type DisconnVIServer and it would immediately disconnect from all servers.

Lastly, it is probably a good idea to make a call to Change-TitleBar somewhere after the function definitions in your PowerShell profile.  Then it'll set the "default" title bar text as you launch PowerShell since the initial text with PowerCLI 5.0.1's initialization script just shows the name of the product and version.

29 November 2011

Speed Up First PowerCLI 5 cmdlet -- Precompile XMLSerializers

Updated for v5.1 - 02 Oct 2012 -- see below

On 30 Jun 2011 there was a post on the official PowerCLI Blog about How to speed-up the execution of the first PowerCLI cmdlet.  They talked about how the first PowerCLI cmdlet run in a PowerShell session is considerably slower than all other PowerCLI cmdlet calls in the same session, and how that was "due to the fact that the .NET framework compiles the underlying code on first use."

So, they listed out the commands for precompiling some assemblies, so that the first PowerCLI cmdlet call would not be extra slow due to just-in-time compilation.  The commands add the compiled code to the "native image cache" on the local machine, and so only need performed once for the computer on which they are run. Note:  that is once per computer per version, so when a new PowerCLI version comes out and you install the new version, you would compile the assembly for the new version.

I wanted to keep the speed alive when I upgraded to PowerCLI v5, but found no mention of doing this for the new release.  So, I looked into it, and the commands for precompiling the XmlSerializers used by PowerCLI are roughly the same as those found at the PowerCLI Blog post, just with updating the version-specific items:
PS vN:\> C:\Windows\Microsoft.NET\Framework\v2.0.50727\ngen.exe install "VimService50.XmlSerializers, Version=5.0.0.0, Culture=neutral, PublicKeyToken=10980b081e887e9f"

And, if running on a 64-bit OS, also run:
PS vN:\> C:\Windows\Microsoft.NET\Framework64\v2.0.50727\ngen.exe install "VimService50.XmlSerializers, Version=5.0.0.0, Culture=neutral, PublicKeyToken=10980b081e887e9f"

These can both be run from PowerShell or a cmd prompt -- the Native Image Generator (ngen.exe) is doing the work.  The VimService50.XmlSerializers info can be found in the assembly folder at C:\Windows\assembly\.  There, one can get its Version and Public Key Token info used in the commands.

* Update - 02 Oct 2012:
With the release of PowerCLI v5.1 Release 1, there is a new XmlSerializers version to pre-compile to keep the speed streak alive.  This new one is named VimService51.XmlSerializers, and the version is "5.1.0.0".  So, here is another example of the speed difference of before and after using ngen.exe to precompile the XmlSerializers, along with the updated commandlines to run:

Speed results before:
PS vN:\> Measure-Command {Connect-VIServer myVcenter.dom.com}
...
TotalSeconds      : 14.4464282
TotalMilliseconds : 14446.4282

PS vN:\> Measure-Command {Get-VMHost}
...
TotalSeconds      : 11.859625
TotalMilliseconds : 11859.625

PS vN:\> Measure-Command {Get-VMHost}
...
TotalSeconds      : 0.0677823
TotalMilliseconds : 67.7823
The pre-compile commands for PowerCLI v5.1:
PS vN:\> C:\Windows\Microsoft.NET\Framework\v2.0.50727\ngen.exe install "VimService51.XmlSerializers, Version=5.1.0.0, Culture=neutral, PublicKeyToken=10980b081e887e9f"
 
If running on a 64-bit OS, also run:
PS vN:\> C:\Windows\Microsoft.NET\Framework64\v2.0.50727\ngen.exe install "VimService51.XmlSerializers, Version=5.1.0.0, Culture=neutral, PublicKeyToken=10980b081e887e9f"
 

Speed results after precompiling:
PS vN:\> Measure-Command {Connect-VIServer myVcenter.dom.com}
...
TotalSeconds      : 2.6289308
TotalMilliseconds : 2628.9308

PS vN:\> Measure-Command {Get-VMHost}
...
TotalSeconds      : 0.4143238
TotalMilliseconds : 414.3238

PS vN:\> Measure-Command {Get-VMHost}
...
TotalSeconds      : 0.0890656
TotalMilliseconds : 89.0656
End of update - 02 Oct 2012

Further info abut using NGen can be found at the MSDN site http://msdn.microsoft.com/en-us/library/6t9t5wcf%28v=VS.100%29.aspx.  There you can read all about:
  • how the precompilation helps with faster application startup 
    • loads faster, requires smaller initial working set
  • displaying the Native Image Cache (seeing one/all compiled native images)
  • uninstalling native images that have been compiled/installed
  • a "Summary of Usage Considerations" -- pros and cons of using native images
The speed increases for the first PowerCLI cmdlet call in a PowerShell session after compiling were on par with those reported in the PowerCLI blog post for previous versions.  Definitely worth running, to help keep away the slow-sies (SAS)!

11 November 2011

Get HP Firmware Info for HP VMware Hosts Using PowerShell

In trying to keep our HP hosts current on firmware, we had the need to find what the hosts' current firmware levels were. So, we whipped up a quick spot of code to retrieve such info.  It relies on only Get-View to get the data, and we were sure to specify only the properties we needed, so you know it will be FAF:
## Script function: quickly get BIOS date, Smart Array FW version, and iLO FW version for HP hosts in a given location (folder, cluster, datacenter, etc.)
## Author: vNugglets.com -- Sep 2011

## folder in which hosts in which we are interested reside
#$strHostsFolderName = "myFolder"
#Get-View -ViewType HostSystem -Property Name, Runtime.HealthSystemRuntime.SystemHealthInfo.NumericSensorInfo -SearchRoot (Get-View -ViewType Folder -Property Name -Filter @{"Name" = "^$([RegEx]::escape($strHostsFolderName))$"}).MoRef | %{
## cluster in which hosts in which we are interested reside
$strHostsClusterName = "myCluster"
Get-View -ViewType HostSystem -Property Name, Runtime.HealthSystemRuntime.SystemHealthInfo.NumericSensorInfo -SearchRoot (Get-View -ViewType ClusterComputeResource -Property Name -Filter @{"Name" = "^$([RegEx]::escape($strHostsClusterName))$"}).MoRef | %{
    $arrNumericSensorInfo = @($_.Runtime.HealthSystemRuntime.SystemHealthInfo.NumericSensorInfo)
    # HostNumericSensorInfo for BIOS, iLO, array controller
    $nsiBIOS = $arrNumericSensorInfo | ? {$_.Name -like "*System BIOS*"}
    $nsiArrayCtrlr = $arrNumericSensorInfo | ? {$_.Name -like "HP Smart Array Controller*"}
    $nsiILO = $arrNumericSensorInfo | ? {$_.Name -like "Hewlett-Packard BMC Firmware*"}
    New-Object PSObject -Property @{
        VMHost = $_.Name
        "SystemBIOS" = $nsiBIOS.name
        "HPSmartArray" = $nsiArrayCtrlr.Name
        "iLOFirmware" = $nsiILO.Name
    } ## end new-object
} ## end Foreach-Object

Lines 5-6 can be used instead of 8-9, in the case that one wants to get info on hosts in a particular inventory folder, instead of in a particular cluster.  Or, one can remove the -SearchRoot portion altogether, to get firmware info for all hosts in the given vCenter.

Note:  This technique relies on the HP CIM provider being installed and running properly on the VMware hosts.

An example of running the script and selecting a couple of the properties (for the sake of display width here) would look something like:
PS C:\> \\path\to\getHPHostFirmwareScript.ps1 | ft -a VMHost,SystemBIOS,HPSmartArray

VMHost            SystemBIOS                                  HPSmartArray
------            ----------                                  ------------
host0.domain.com  HP System BIOS P61 2009-07-10 00:00:00.000  HP Smart Array Controller HPSA1 Firmware 6.86
host1.domain.com  HP System BIOS P61 2009-07-10 00:00:00.000  HP Smart Array Controller HPSA1 Firmware 6.86
host2.domain.com  HP System BIOS P61 2010-10-25 00:00:00.000  HP Smart Array Controller HPSA1 Firmware 7.22
...

And, as for the speed:  for 175 hosts (some of which are in WAN-connected datacenters), this took about 16 seconds.  Not bad, not bad!

09 November 2011

Evacuate Templates from a Host -- on the Quick!

We recently had the need to move templates off a VMHost in a cluster. As you may know, the Move-Template cmdlet in PowerCLI does not allow for moving a template from one host to another. The cmdlet only allows for the -Destination to be a Datacenter or a VM Folder (a.k.a. "blue" inventory folders in the vSphere client).

There are posts out there that discuss ways to move templates to different datastores, how to move them to different inventory folders in vCenter, how to move templates to a particular host, etc. (links to a few such items are provided at the end of this post), but none fit the bill for our need.

So, with speed in mind, I wrote the following script that is careful to stay fast and low maintenance -- move all templates off of the given host, placing them on the other hosts in the given cluster.

The script:
## Script function: migrate templates from one VMhost to the rest of the hosts in the cluster (at random, for good dispersion)
## Author: vNugglets.com -- Aug 2011

## full name of VMHost from which to move templates
$strSourceVMHost = "someVMHost.domain.com"
## name of resource pool in the cluster into which to migrate the template (default ResPool name is "Resources")
$strDestResPoolName = "Resources"
#### end config section

## .NET View object of VMHost on which templates reside
$viewSourceVMHost = Get-View -ViewType HostSystem -Property Parent -Filter @{"Name" = [RegEx]::escape($strSourceVMHost)}
## .NET View object of cluster in which the VMHost (and, so, templates) reside
$viewTemplsCluster = Get-View -Id $viewSourceVMHost.Parent -Property Name

## get the view object of the destination resource pool
$viewDestResPool = Get-View -ViewType ResourcePool -Property Name -SearchRoot $viewTemplsCluster.MoRef -Filter @{"Name" = [RegEx]::escape($strDestResPoolName)}

## array of .NET View objects of templates to move
$arrTemplViewsToMove = Get-View -ViewType VirtualMachine -Property Name -SearchRoot $viewSourceVMHost.MoRef -Filter @{"Config.Template" = "true"}
## array of .NET View objects of VMHosts to which to move templates
$arrDestHostViews = Get-View -ViewType HostSystem -Property Name -SearchRoot $viewTemplsCluster.MoRef | ?{$_.Name -ne $strSourceVMHost}

## move the templates to other hosts
$arrTemplViewsToMove | %{
    ## MigrateVM_Task() not supported on templates (does not work), so go this route:
    ## mark template as a VM, putting the template on a different host in the process (takes advantage of the Host param to change the Host on which the template resides in the process of marking it as a VM)
    #   http://pubs.vmware.com/vsphere-50/topic/com.vmware.wssdk.apiref.doc_50/vim.VirtualMachine.html?path=5_0_2_5_12_4#markAsVirtualMachine
    $_.MarkAsVirtualMachine($viewDestResPool.MoRef, ($arrDestHostViews | Get-Random).MoRef)

    ## mark VM as template again
    #   http://pubs.vmware.com/vsphere-50/topic/com.vmware.wssdk.apiref.doc_50/vim.VirtualMachine.html?path=5_0_2_5_12_3#markAsTemplate
    $_.MarkAsTemplate()
} ## end foreach-object

This takes advantage of the second parameter of the MarkAsVirtualMachine() method that is the VMHost on which to place the VM.  When it is time for the target host to be done with its maintenance, moving the templates back is almost easier. Use:
## move them back to their original host
$arrTemplViewsToMove | %{
    ## MigrateVM_Task() not supported on templates (does not work), so go this route:
    ## mark template as a VM, putting the template on a different host in the process (takes advantage of the Host param to change the Host on which the template resides in the process of marking it as a VM)
    $_.MarkAsVirtualMachine($viewDestResPool.MoRef, $viewSourceVMHost.MoRef)

    ## mark VM as template again
    $_.MarkAsTemplate()
} ## end foreach-object

and the templates that were evacuated from the host are migrated back to that host. As you can see, that is going to be FaF.

Some of the other posts/ways previously mentioned, for reference: