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:

11 September 2011

Find VM by NIC MAC Address with PowerShell -- Fast Like!

There is the occasion on which it would be handy to find a VM based on a known MAC address. Say, for example, there is a duplicate IP address on the network, and the network team informs you of the offending MAC addresses. Unfortunately, there is not a simple/easy way to go about this.

There are some places on the web that discuss this topic, but none were satisfactory. Some were overly complex, storing objects and data without need. Others were written with the ease of writing being considered. While these will work, they do not suffice if the size of the environment is larger. Memory usage climbs in the PowerShell session, and the time-to-run grows far past what is necessary and desirable.

Yet another problem was the reliance of some scripts on VMware Tools to get the information. Granted, VMs would ideally have Tools current and running, but, we all know that in the real world, that just does not always happen.

So, we wrote a little nugglet that will get the VM(s) that have VirtualEthernetCard(s) with the given MAC address:
## Script function: find all VMs with one or more NIC with the given MAC address
## Author: vNugglets.com -- Aug 2011

## MAC addr of NIC(s) to find
$strMacToFind = "00:50:56:83:00:69"
## return the .NET View object(s) for the VM(s) with the NIC w/ the given MAC
Get-View -Viewtype VirtualMachine -Property Name, Config.Hardware.Device | ?{$_.Config.Hardware.Device | ?{($_ -is [VMware.Vim.VirtualEthernetCard]) -and ($_.MacAddress -eq $strMacToFind)}}

This is pretty straight forward to use: just set the variable for the MAC address to find, and then run the next line of code. This returns the .NET View object(s) for the VM(s) that have a NIC with the given MAC address. One could then do as they pleased with the objects.

To just report the info about the VM(s) with a NIC with the given MAC address, one could add a simple Select-Object statement. The following returns a list of matching VMs' names and their associated NICs' MAC addresses:
Get-View -Viewtype VirtualMachine -Property Name, Config.Hardware.Device | ?{$_.Config.Hardware.Device | `
?{($_ -is [VMware.Vim.VirtualEthernetCard]) -and ($_.MacAddress -eq $strMacToFind)}} | `
Select name, @{n="MAC(s)"; e={($_.Config.Hardware.Device | ?{($_ -is [VMware.Vim.VirtualEthernetCard])} | %{$_.MacAddress}) -join ","}}

The output would be something like:
Name     MAC(s)
----     ------
myVM     00:50:56:83:00:69,00:0c:29:00:08:01

PS C:\>

As for speed: this method of finding VMs with a given MAC address took around 25 seconds when run in a test environment of about 3200 VMs. Fast. Enjoy.

Update (30 Oct 2011 -- response to comment):
To use a similar technique to find a VMHost with a given MAC address, you can look at the Config.Network.Pnic property of a HostSystem .NET View object.  To find a VMHost with a given MAC, and quick like, use:
## to find a VMHost with given MAC address:
$strMacOfNICOnVMHostToFind = "00:0e:0c:00:00:01"

Get-View -ViewType HostSystem -Property Name, Config.Network.Pnic | ?{$_.Config.Network.Pnic | ?{$_.Mac -eq $strMacOfNICOnVMHostToFind}} | select Name, @{n="MAC(s)"; e={($_.Config.Network.Pnic | %{$_.Mac}) -join ","}}

The output is similar to the output above, but the Name will be of the given VMHost, and MAC(s) will be that host's MAC addresses. Enjoy, "Anonymous" commenter.

04 August 2011

Find Duplicate VM NIC MAC Addresses Using PowerShell

We were having issues with some VMs and their networking. Unexpected, unexplained things were happening, and eventually someone thought that there might be some duplicate MAC addresses in the mix, causing grief. Some of the VMs had been copied from other vCenters, among other things.

Side note by AC: the unique ID of a vCenter server helps dictate the range of assigned MAC addresses for VMs in that vCenter. Therefore, in order to avoid MAC address duplicates, one should ensure that no vCenters within the same network have the same unique ID.

So, to find if there were any duplicate MAC addresses in use by VM NICs, we whipped up the following:
## Script function: find duplicate MAC addresses, and list the VMs/addresses involved
## Author: vNugglets.com

## get VirtualMachine .NET views where the items is not marked as a Template
$colDevMacAddrInfo = `
Get-View -ViewType VirtualMachine -Property Name,Config.Hardware.Device -Filter @{"Config.Template" = "False"} | %{
$strVMName = $_.Name
$_.Config.Hardware.Device | Where-Object {$_ -is [VMware.Vim.VirtualEthernetCard]} | %{
New-Object -Type PSObject -Property @{VMName = $strVMName; MacAddr = $_.MacAddress}
} ## end foreach-object
} ## end foreach-object

## are all of the MAC addresses unique?
($colDevMacAddrInfo | Select-Object -unique MacAddr).Count -eq $colDevMacAddrInfo.Count

## get the non-unique MAC addresses, returning objects with the count of the duplicates, the duplicate MAC, and the VM names that have the duplicate MAC
$colDevMacAddrInfo | Group-Object MacAddr | Where-Object {$_.count -gt 1} | Select-Object Count,@{n="DuplicateMAC"; e={$_.Name}},@{n="VMNames"; e={($_.Group | %{$_.VMName}) -join ","}}

It uses the Get-View cmdlet with the -Property parameter to ensure minimum memory usage and maximum speed.

Line 14: quick check to determine if there are any duplicate MAC addresses present -- compares the count of unique addresses to the total count of addresses

If there are duplicate MAC addresses, line 17 returns some nice information about the given MAC address, the count of VM NICs that have it, and the names of the VMs involved.

Once one finds the offending VMs, they can use something like the script in the vNugglets post Setting MAC Address for VM NICs using PowerShell to change the necessary MAC addresses and return things to standard working order. Hurray!

27 July 2011

Backup/Export Full DRS Rule Info via PowerShell

Updated 22 Jan 2015
Luc Dekens and I got together on a new PowerShell module for managing/exporting/importing DRS Rules and Groups. See our posts at http://www.lucd.info/2015/01/22/drsrule-drs-rules-and-groups-module and http://www.vnugglets.com/2015/01/drsrule-new-powershell-module-for-drs.html for all of the juicy details. Going forward, that DRSRule module is the way to go. I am leaving this post intact for posterity. Enjoy!
End of update - 22 Jan 2015

We, like many, had the need for a way to backup our DRS rules. Searches turned up many pages, all of which had the same basic recipe: use the Get-DrsRule to grab the rules, get a few properties, and export to CSV.

The problem: Addressing the task getting all of the pertinent info for rules of type ClusterVmHostRuleInfo, or, "VM-to-Host" rules. Rules of this type do not have the VM IDs in a property of the rule, but rather have the VMGroupName and HostGroupNames properties. From these properties, and the .NET view object of the given cluster, one can retrieve the VMs' and hosts' info.

So, we did a little work, and wrote the following to, for all DRS rule types, get the information that would be required if one wanted to recreate the rules:
## Script function: Gather/export DRS rule info, including VMGroup and HostGroup members
## Author: vNugglets.com

## file to which to write the DRS rules' info
$strOutputFilespec = "e:\someFolder\DRSrules_export.csv"

## get cluster .NET view objects, which hold the info about the DRS rules
Get-View -ViewType ClusterComputeResource -Property Name, ConfigurationEx | %{
    ## if the cluster has any DRS rules
    if ($_.ConfigurationEx.Rule -ne $null) {
        $viewCurrClus = $_
        $viewCurrClus.ConfigurationEx.Rule | %{
            $oRuleInfo = New-Object -Type PSObject -Property @{
                ClusterName = $viewCurrClus.Name
                RuleName = $_.Name
                RuleType = $_.GetType().Name
                bRuleEnabled = $_.Enabled
                bMandatory = $_.Mandatory
            } ## end new-object

            ## add members to the output object, to be populated in a bit
            "bKeepTogether,VMNames,VMGroupName,VMGroupMembers,AffineHostGrpName,AffineHostGrpMembers,AntiAffineHostGrpName,AntiAffineHostGrpMembers".Split(",") | %{Add-Member -InputObject $oRuleInfo -MemberType NoteProperty -Name $_ -Value $null}

            ## switch statement based on the object type of the .NET view object
            switch ($_){
                ## if it is a ClusterVmHostRuleInfo rule, get the VM info from the cluster View object
                #   a ClusterVmHostRuleInfo item "identifies virtual machines and host groups that determine virtual machine placement"
                {$_ -is [VMware.Vim.ClusterVmHostRuleInfo]} {
                    $oRuleInfo.VMGroupName = $_.VmGroupName
                    ## get the VM group members' names
                    $oRuleInfo.VMGroupMembers = (Get-View -Property Name -Id ($viewCurrClus.ConfigurationEx.Group | ?{($_ -is [VMware.Vim.ClusterVmGroup]) -and ($_.Name -eq $oRuleInfo.VMGroupName)}).Vm | %{$_.Name}) -join ","
                    $oRuleInfo.AffineHostGrpName = $_.AffineHostGroupName
                    ## get affine hosts' names
                    $oRuleInfo.AffineHostGrpMembers = if ($_.AffineHostGroupName -ne $null) {(Get-View -Property Name -Id ($viewCurrClus.ConfigurationEx.Group | ?{($_ -is [VMware.Vim.ClusterHostGroup]) -and ($_.Name -eq $oRuleInfo.AffineHostGrpName)}).Host | %{$_.Name}) -join ","}
                    $oRuleInfo.AntiAffineHostGrpName = $_.AntiAffineHostGroupName
                    ## get anti-affine hosts' names
                    $oRuleInfo.AntiAffineHostGrpMembers = if ($_.AntiAffineHostGroupName -ne $null) {(Get-View -Property Name -Id ($viewCurrClus.ConfigurationEx.Group | ?{($_ -is [VMware.Vim.ClusterHostGroup]) -and ($_.Name -eq $oRuleInfo.AntiAffineHostGrpName)}).Host | %{$_.Name}) -join ","}
                    break;
                } ## end block
                ## if ClusterAffinityRuleSpec (or AntiAffinity), get the VM names (using Get-View)
                {($_ -is [VMware.Vim.ClusterAffinityRuleSpec]) -or ($_ -is [VMware.Vim.ClusterAntiAffinityRuleSpec])} {
                    $oRuleInfo.VMNames = if ($_.Vm.Count -gt 0) {(Get-View -Property Name -Id $_.Vm | %{$_.Name}) -join ","}
                } ## end block
                {$_ -is [VMware.Vim.ClusterAffinityRuleSpec]} {
                    $oRuleInfo.bKeepTogether = $true
                } ## end block
                {$_ -is [VMware.Vim.ClusterAntiAffinityRuleSpec]} {
                    $oRuleInfo.bKeepTogether = $false
                } ## end block
                default {"none of the above"}
            } ## end switch

            $oRuleInfo
        } ## end foreach-object
    } ## end if
} | Export-Csv -NoTypeInformation $strOutputFilespec

This bit of code, using the Get-View cmdlet for max speed and min memory footprint, starts off by getting a couple of select properties for all clusters. If the cluster has any DRS rules defined, then the code, for each rule:
  • creates a new PSObject to hold the pertinent info about the rule
  • includes basic rule info, the rule type, and info about the objects involved with the rule, including the VM group names/members and the host group names/members (for both Affine- and AntiAffine host groups)
  • returns the object
  • exports all of the gathered info to CSV for later consumption

Arnim van Lieshout wrote a good post with more information about DRS rules themselves at Managing VMware DRS rules using PowerCLI, and included a few functions for creating VMGroups ("Virtual Machines DRS Groups" in the vSphere client) and HostGroups ("Host DRS Groups"), and for creating new VM-to-Host rules. We will likely be using said functions for our "recreate the DRS rules/groups" work.

25 July 2011

Setting MAC Address for VM NICs using PowerShell

A bit ago, there were some questions in the VMware VMTN community forums about setting explicit MAC addresses for VMs' NICs.

While this can be done via PowerCLI using the Set-NetworkAdapter cmdlet, the MAC address range is restricted to 00:50:56:00:00:00 - 00:50:56:3F:FF:FF when using the cmdlet, which is only a subset of the range that VMware uses.

An example came up where one might need to set the MAC address to something in a valid VMware range (with VMware OUIs like "00:50:56" or "00:0C:29") outside of the subset above. This can be done using the Web Services API, as it does not have the same range limitation as the cmdlet. The well-commented code below shows how to do so using the API:

## script function:  set the MAC address of a VM's NIC
## Author: vNugglets.com

## the name of the VM whose NIC's MAC address to change
$strTargetVMName = "myVM01"
## the MAC address to use
$strNewMACAddr = "00:50:56:90:00:01"

## get the .NET view object of the VM
$viewTargetVM = Get-View -ViewType VirtualMachine -Property Name,Config.Hardware.Device -Filter @{"Name" = "^${strTargetVMName}$"}
## get the NIC device (further operations assume that this VM has only one NIC)
$deviceNIC = $viewTargetVM.Config.Hardware.Device | Where-Object {$_ -is [VMware.Vim.VirtualEthernetCard]}
## set the MAC address to the specified value
$deviceNIC.MacAddress = $strNewMACAddr
## set the MAC address type to manual
$deviceNIC.addressType = "Manual"

## create the new VMConfigSpec
$specNewVMConfig = New-Object VMware.Vim.VirtualMachineConfigSpec -Property @{
   ## setup the deviceChange object
   deviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec -Property @{
       ## the kind of operation, from the given enumeration
       operation = "edit"
       ## the device to change, with the desired settings
       device = $deviceNIC
   } ## end New-Object
} ## end New-Object

## reconfigure the "clone" VM
$viewTargetVM.ReconfigVM($specNewVMConfig)


The ReconfigVM() method does not care if the new MAC address is a valid VMware MAC address. It does not even seem to care if the new MAC address string is a valid hex MAC address. So, use with care -- one would not want to break networking on their precious VM by setting its NIC to have an invalid MAC address, or a duplicate, etc.

Other info: the valid VMware OUIs can be found many places, like at the IEEE.org Public Listing search site.


Update 25 Nov 2011:
Do note that changing the MAC address of a VM NIC to something outside of the "accepted" range will cause other PowerCLI cmdlets and the vSphere client to report that the NIC's MAC is "not in the valid range", and may prevent further operations on that VM's NIC with those tools.  Thank-you to the anonymous commenter here and to LucD (in the VMware Communities thread at http://communities.vmware.com/message/1869800#1869800) for highlighting this point.


Update 31 Dec 2011:
The -Filter parameter for the Get-View call on line 10 was using a regular expression that had the possibility of returning more than one VM View object.  I have updated the regular expression to only match VMs of exactly the given name.  Thanks go to Joep Piscaer for pointing out the issue.  The code in this post has been updated.

31 January 2011

Enable SIOC On All Datastores

After reading what Duncan at Yellow-Bricks.com had to say about enabling SIOC (Storage I/O Control) on all datastores here and here, we were inspired to write a script to avoid having to do it by hand on the 370 datastores in our environment. Of course, LucD already has a SIOC script but it was written before VMware fixed the StorageResourceManager bug in PowerCLI 4.1.1 that he had to use a workaround for. LucD's script allows you to both query and set values--we just wanted one that enabled SIOC on all datastores that supported it as quickly as possible. In addition, we wanted to schedule it (this script is not schedule-ready) to run on a regular basis so that it can enable SIOC on newly created datastores without admins having to worry about it.

This is what we ended up with:
## Script function: Enable SIOC (Storage I/O Control) on all datastores that support it and do not already have it enabled.
## Author: vNugglets.com


## Set up specification to enable SIOC
$spec = New-Object VMware.Vim.StorageIORMConfigSpec
$spec.Enabled = $True

## Create an array of datastore view objects that are SIOC capable and that do not already have it enabled
$arrSIOCableDatastores = @(Get-View -ViewType Datastore -Property Capability.StorageIORMSupported,IormConfiguration,Name -Filter @{"Capability.StorageIORMSupported" = "True"; "IormConfiguration.Enabled" = "[^(True)]"})

## Loop through each SIOC-ready datastore view (if any), then on the first run grab the "view" of the StorageResourceManager (this is needed to use the ConfigureDatastoreIORM_Task method to enable/disable SIOC) and finally, enable SIOC on the datastore
$arrSIOCableDatastores | ForEach-Object -Begin {$viewStorageRM = Get-View -Id "StorageResourceManager-StorageResourceManager"} {
$viewStorageRM.ConfigureDatastoreIORM_Task($_.MoRef, $spec)
}

Of course, if you want to disable SIOC on all of your datastores you need to only modify two pieces of code:

Line 7: Simply change $True to $False.
Line 10: Remove the caret (^) on the second part of the filter so it finds datastores that already have SIOC enabled.

Depending on what you are looking for, you may choose LucD's script over this one, or maybe you'll use both since his contains two functions and gives you more granular control on which datastores to enable/disable SIOC.

19 January 2011

Re-Registering VMs Removed from Inventory

The cache module on the array controller failed on a host, and ESXi was wasted. The host stopped responding in vCenter, but did not fail hard enough to trigger HA. Most of the VMs on it were still up and their OSes were responsive to ping requests. Eventually we had to remove the host from vCenter. At that point, all VMs registered on it also are removed from inventory.

I grabbed the info for the guests that were in "disconnected" state _BEFORE_ removing the affected host from vCenter, then removed the host, then re-registered VMs and started them as such:
## Script function:  re-register VMs after host removal from vCenter
## Author: vNugglets.com
## Jan 2011

## cluster in which troubled host resides
$strAffectedCluster = "myCluster"

## get array BEFORE removing bad host from vCenter!!! Else, will not be able to get this info in this manner, since disconnected VMs are removed from inventory at that time, too

## get an array of VM .Net View objects where their OverallStatus is "gray"; optimized for low memory consumption
$arrVMsToHandle = Get-View -ViewType VirtualMachine -SearchRoot (Get-Cluster $strAffectedCluster | Get-View).MoRef -Property Name,Parent,LayoutEx.File,Summary.OverallStatus -Filter @{"Summary.OverallStatus" = "gray"}

## get the hosts in the cluster, to be used for re-registering VMs
$arrHostsInAffectedCluster = Get-Cluster $strAffectedCluster | Get-VMHost

## for each set of VM name, parent folder ID, and .VMX file path, add VM to inventory via New-VM cmdlet; add to random host within the cluster
$arrVMsToHandle | Select Name, Parent, @{n="vmxFilePath"; e={($_.layoutex.File | ? {$_.name -like "*.vmx"}).name}} | %{
New-VM -VMFilePath $_.vmxFilePath -VMHost ($arrHostsInAffectedCluster | Get-Random) -Location (Get-VIObjectByVIView $_.Parent) -RunAsync
} ## end ForEach-Object

## start all of the VMs (may cause a boot storm if huge number of VMs -- split up by groups of x VMs as necessary)
$arrVMsToHandle | %{Start-VM $_.Name -RunAsync}

You could also have put the "Start-VM" call inside of the "ForEach-Object" loop (say, before line 19), but that makes the process more linear than quick. This would require the removal of the "-RunAsync" from the "New-VM" call on line 18.

This could come in handy for you someday. Enjoy!