Identifying orphoned virtual machines and hard disks in vSphere

Connect to the shell of the ESXi host system and browse to location of the datastore (/vmfs/volumes/{datastore name}) to which you want to identity orphoned virtual machine hard disks and invoke the following command. This will return all virtual machine hard disks to which the modified date is seven days in the past (-mtime +7) and return the filename pattern match (find -name “*flat.vmdk*) with the modified date timestamp (-exec stat -c “%n %y” {} \;).

find -iname "*flat.vmdk" -mtime +7 -exec stat -c "%n %y" {} \;

This approach assumes that if the virtual machine hard disk has not been touched in the specified timespan the virtual machine hard disk is orphoned. This may not be accurate, as the virtual machine may be in a powered off state for a period that exceeds the timespan and the virtual machine may be valid. The output should be similar to the below:

./vmfs/volumes/424b26ec-4294ea41/ubuntu-01/ubuntu-01-flat.vmdk 2017-01-29 10:52:03.000000000
./vmfs/volumes/424b26ec-4294ea41/ubuntu-02/ubuntu-02-flat.vmdk 2017-01-29 10:17:46.000000000
./vmfs/volumes/424b26ec-4294ea41/ubuntu03/ubuntu03-flat.vmdk 2017-01-29 11:04:18.000000000
./vmfs/volumes/424b26ec-4294ea41/ubuntu04/ubuntu04-flat.vmdk 2017-01-29 10:17:49.000000000
./vmfs/volumes/424b26ec-4294ea41/ubuntu05/ubuntu05-flat.vmdk 2017-01-29 10:52:03.000000000

An alternative method, is using RVTools which can identify orphoned virtual machines and hard disks (categorised as ‘zombie’) from the inventory scan of a vCenter Server System or ESXi Host System. On completion of the inventory scan, the vHealth tab will display any virtual machines and hard disks identified and may be exported for reference.

Advertisements

PowerCLI: Retrieving DRS rules

I was recently looking to retrieve a list of DRS rules in each cluster managed by a single vCenter server and to export the output using PowerCLI.

The script will be required to be invoked as a scheduled task and therefore we will specify two paramaters. Firstly the VI Server we will be required to establish a connection to, where by default this will use the host name and require a mandtory parameter for the output directory for exporting the output.

Param ([string] $vCenter = ([System.Net.Dns]::GetHostByName(($env:computerName))).HostName, [parameter(Mandatory=$true)][string] $Output) 

Now, we will load the vSphere PowerCLI snap-in to the current powershell session and establish a connection to the VI Server.

if (-not (Get-PSSnapin VMware.VimAutomation.Core -ErrorAction SilentlyContinue)) 
    {
    Add-PSSnapin VMware.VimAutomation.Core | Out-Null 
    }

Connect-VIServer $vCenter | Out-Null

Once a connection to the VI Server has been established we will retrieve all the DRS rules from each cluster and store this as a collection.

$DRSRules = Get-Cluster | Get-DrsRule

For each DRS rule returned in the collection we will retrieve the following property values and store the results in a variable to later export to a CSV file.

  • Cluster Name
  • DRS Name
  • DRS Enabled
  • DRS Type
  • VM Names

In order to retrieve the VM names, we will require to retrieve the name from the retrieved VM Id, by splitting the property value and for each VM invoking the Get-VM cmdlet to retrieve the name value and then join each returned VM name to a single string.

$Results = ForEach ($DRSRule in $DRSRules)
     {
    "" | Select-Object-Property @{N="Cluster";E={(Get-View- Id $DRSRule.Cluster.Id).Name}},
    @{N="Name";E={$DRSRule.Name}},
    @{N="Enabled";E={$DRSRule.Enabled}},
    @{N="DRS Type";E={$DRSRule.KeepTogether}}, 
    @{N="VMs";E={$VMIds=$DRSRule.VMIds -split "," 
      $VMs = ForEach ($VMId in $VMIds) 
        { 
        (Get-View -Id $VMId).Name
        } 
      $VMs -join ","}}
     }

Finally, we will export the output to a CSV file and close the connection to the VI Server.

$Results | Export-Csv -NoTypeInformation -Path ($Output + "\" + $vCenter+ "_DRS Rules.csv") -Force
Disconnect-VIServer -Server $vCenter -Confirm:$False

PowerCLI: Configure Log rotation and size options for vmware.log

By default, the virtual machine log file (vmware.log) is rotated as a result of the virtual machine’s Power On or Power Off operation. , In order to limit the total size this can grow to you may configure the log you may configure a log rotation size and level of rotation.

Also, this can add a level of protection where denial of service attack could write information to a log entry where these limits are not configured,as uncontrolled logging could lead to denial of service due to the datastore’s being filled.

In the below example, I am going to configure each VM to have a log rotate size of ‘1,000’ KB (the configuration value is in bytes, so 1,024,000) and set the level of rotation to ’10’. This can be achieved by adding the following to each VMs configuration parameters (VM Options > Advanced).

log.KeepOld = 10
log.rotatesize = 1024000

As I want to configure these settings for a large number of VMs, I can achieve this by using PowerCLI to build a collection of VMs at a cluster object level and  then add these settings.

Firstly, we will connect to our vCenter server and build a collection of VMs from a single cluster by invoking the Get-VM cmdlet.

if (-not (Get-PSSnapin VMware.VimAutomation.Core-ErrorAction SilentlyContinue)) 
    {
    Add-PSSnapin VMware.VimAutomation.Core | Out-Null 
    }

Connect-VIServer server1.domain.local 
$VMs = Get-Cluster cluster1 | Get-VM

For Each VM returned in the collection, we will now add the configuration settings as above, by invoking the cmdlet New-AdvancedSetting specifying the name and value, where the ‘Confirm’ parameter value is False to prevent the script block requiring user interaction.

ForEach ($VM in $VMs)
    { 
    Get-VM $VM.Name | New-AdvancedSetting -Name "log.keepOld" -value "10" -Confirm:$false 
    Get-VM $VM.Name | New-AdvancedSetting-Name "log.rotatesize" -value"1024000" -Confirm:$false 
    }

 

Retrieving VM CDDrive information using PowerCLI

I was recently looking at retrieving a number of VMs where the CD Drive was connected to a Datastore ISO file.  This is possible be using the Get-CDDrive cmdlet to retrieve the device type information. To retreive information for a single VM we can use the following:

Get-VM VM1 | Get-CDDrive

The below information will be retrieved, where the IsoPath value is the Datastore ISO filename and Parent is the name of the VM.

IsoPath         : [********] SW_DVD9_SQL_Svr_Enterprise_Edtn_2008_R2_English_MLF_X16-29540.ISO
HostDevice      :
RemoteDevice    :
ParentId        : VirtualMachine-vm-15465
Parent          : vm1
Uid             : /VIServer=domain\user1@server1:443/VirtualMachine=VirtualMachine-vm-15465/CDDrive=3002/
ConnectionState : Connected, GuestControl, StartConnected
ExtensionData   : VMware.Vim.VirtualCdrom
Id              : VirtualMachine-vm-15465/3002
Name            : CD/DVD drive 1
Client          : VMware.VimAutomation.ViCore.Impl.V1.VimClient

For a number of VMs, we can retrieve theses as a collection and pass these to the Get-CDDrive cmdlet. In this instance I wanted to return all VMs where the CD Drive was connected to the Datastore ISO file ‘SW_DVD9_SQL_Svr_Enterprise_Edtn_2008_R2_English_MLF_X16-29540.ISO’.

The Datastore ISO file value can be retrieved from the IsoPath property, in the below example we will filter the retrieved information to return only the above file and include only the name of the VM and the IsoPath in the output.

Get-VM | Get-CDDrive  | Select Parent, IsoPath |  Where-Object {$_.IsoPath -like "*SW_DVD9_SQL_Svr_Enterprise_Edtn_2008_R2_English_MLF_X16-29540.ISO"}

We may also choose to remove the connected Datastore ISO file by using the output from the above and passing to the Set-CDDrive cmdlet to remove the media.

Get-VM | Get-CDDrive |  Where-Object {$_.IsoPath -like "*SW_DVD9_SQL_Svr_Enterprise_Edtn_2008_R2_English_MLF_X16-29540.ISO"} | Set-CDDrive -NoMedia -Confirm:$False

Calculate vCPU to pCPU ratio for Hosts using PowerCLI

In some capacity planning scenarios, statistical information is not taken into account and the management of resources through best efforts can become a  rule of thumb scenario. In the case, of CPU resource the concept of vCPU to CPU ratio has been discussed as a possible method.

Using PowerCLI I was able to produce the ratio of vCPU to pCPU ratio by retrieving Host and VM information and combing this with math.

Firstly we will connect to the VI Server and create a collection of Hosts.

Connect-VIServer server1.domain.local
$VMHosts = Get-VMHost

For each host returned in the collection I will retrieve the number of vCPUs for all VMs on that particular host by returning the NumCPU of each VM, joining each value returned with a ‘+’ and invoking an expression agaisnt the string to produce the total number of vCPUS on the host.

ForEach ($VMHost in $VMHosts)
    {
    $vCPU = Invoke-Expression ((Get-VMHost $VMHost.Name | Get-VM).NumCPU -join '+')

Now we will produce an informational message to show the vCPU to pCPU ratio, we can return the number of physical cores on the Host by returning the ‘Hardware.CPUInfo.NumCPUCores’ value and then dividing the total number of vCPUS on the Host by this value and use the Math.Round method to round the calculated value to one decimal place.

"The vCPU to pCPU ratio for Host '" + $VMHost.Name + "' is " + ([math]::round($vCPU / $VMHost.Hardware.CPUInfo.NumCpuCores,1)) + ":1"
    } 
    

Running the above will return the following output:

The vCPU to pCPU ratio for Host 'esxi1.domain.local' is 3:1
The vCPU to pCPU ratio for Host 'esxi2.domain.local' is 6.6:1
The vCPU to pCPU ratio for Host 'esxi3.domain.local' is 2.4:1
The vCPU to pCPU ratio for Host 'esxi4.domain.local' is 2.2:1

Whilst this will generate the current vCPU to pCPU ratio, this alone shouldn’t be used as a capacity planning/sizing method, from using management tools and/or retrieving  the statistical information (Get-Stat) for both Hosts and VMs you can generate a far more accurate picture of your environment.

The main reason for me generating the above was to get a quick overview whilst killing a bit of down time with PowerCLI.

Retrieving VM inventory information using PowerCLI

Following on from collecting VM Host inventory information (http://wp.me/p15Mdc-mC), I am now looking into returning inventory information for all the VMs where the following would be retrieved:

  • Name
  • Host
  • vCPUs
  • Memory (MB)
  • CPU Reservation
  • CPU Limit
  • CPU Share Allocation
  • Memory Reservation
  • Memory Limt
  • Memory Share Allocation
  • Operating System
  • Annotations

In order to invoke the PowerCLI cmdlets  we will need to add the snap-ins to the current powershell session and connect to the vCenter server (in the below example this is named ‘server.domain.local’).

if (-not (Get-PSSnapin VMware.VimAutomation.Core -ErrorAction SilentlyContinue)) 
   { 
   Add-PSSnapin VMware.VimAutomation.Core > $null
   } 

Connect-VIServer server.domain.local

Firstly, we will create an array to hold a list of items and build of a collection of VMs using the Get-VM cmdlet:

[array]$VMIs=@()
ForEach ($VM in Get-VM) 

For each VM returned in the collection we will create a new powershell object and store this as a variable to export the returned data to a CSV file and add properties to return the above information:

{
$VMI = New-Object PsObject
Add-Member -InputObject $VMI -MemberType NoteProperty -Name Name -Value $VM.Name
Add-Member -InputObject $VMI -MemberType NoteProperty -Name "Host" -Value $VM.Host
Add-Member -InputObject $VMI -MemberType NoteProperty -Name "vCPUs" -Value $VM.NumCPU
Add-Member -InputObject $VMI -MemberType NoteProperty -Name "Memory (MB)" -Value $VM.MemoryMB
Add-Member -InputObject $VMI -MemberType NoteProperty -Name "CPU Reservation" -Value $VM.ExtensionData.ResourceConfig.CpuAllocation.Reservation
Add-Member -InputObject $VMI -MemberType NoteProperty -Name "CPU Limit" -Value $VM.ExtensionData.ResourceConfig.CpuAllocation.Limit
Add-Member -InputObject $VMI -MemberType NoteProperty -Name "CPU Share Allocation" -Value $VM.ExtensionData.ResourceConfig.CpuAllocation.Shares.Level
Add-Member -InputObject $VMI -MemberType NoteProperty -Name "Memory Reservation" -Value $VM.ExtensionData.ResourceConfig.MemoryAllocation.Reservation
Add-Member -InputObject $VMI -MemberType NoteProperty -Name "Memory Limit" -Value $VM.ExtensionData.ResourceConfig.CpuAllocation.Limit
Add-Member -InputObject $VMI -MemberType NoteProperty -Name "Memory Share Allocation" -Value $VM.ExtensionData.ResourceConfig.MemoryAllocation.Shares.Level
Add-Member -InputObject $VMI -MemberType NoteProperty -Name "Operating System" -Value $VM.Guest.OSFullName

Part of my requirement is to return all the virtual machine type annotations, we will do this by returning all the available Annotations keys and create a column for each and display the data associated with that key.

ForEach ($CustomField in $VM.Customfields)
   {
   Add-Member -InputObject $VMI -MemberType NoteProperty -Name $CustomField.Key -Value $CustomField.Value
   }

Finally, we will export the data collected in the array into a CSV file, as  I want the output to create a unique filename each time the inventory script is invoked to the output folder D:\Output,  I will use the Guid.NewGuid method when specifying a filename.

$VMIs+=$VMI
 }
$VMIs | Export-Csv -NoTypeInformation -UseCulture -Path ("D:\Output\" + [guid]::NewGuid() + "-inv_vms.csv")

 

Create VM annotation based on deployed template name

I was recently looking to create a virtual machine annotation type to include the template name to which a virtual machine was deployed from.

I was able to achieve this by using the Get-VIEvent cmdlet to return a match for a virtual machine where the event was equal to “VmBeingDeployedEvent” and formatting the output string to only include the template name.

As I plan to run the above as a scheduled task, I will load the PowerCLI snap-in in the current powershell session and connect to a vcenter server (in this example, server1.domain.local).

If (-not (Get-PSSnapin VMware.VimAutomation.Core -ErrorAction SilentlyContinue)) 
   {
   Add-PSSnapin VMware.VimAutomation.Core
   } 

Connect-VIServer server1.domain.local

Firstly, I wanted to return a collection of virtual machines and to check from the first virtual machine if the annotation ‘Template’ exists and if not create this object

$VMS = Get-VM 
$VM = $VMs | Select -First 1
If (-not $vm.CustomFields.ContainsKey("Template")) 
   {
   New-CustomAttribute -TargetType VirtualMachine -Name Template | Out-Null
   }

Now, that we have created the annotation if this currently did not exist, we will now query each virtual machine in the collection to determine if an event is returned for “VmBeingDeployedEvent”.

ForEach ($VM in $VMs)
   { 
   $Event = $VM | Get-VIEvent -Types Info | Where-Object {$_.Gettype().Name-eq "VmBeingDeployedEvent"}

The FullFormatMessage property returned will have a string relating to the “VmBeingDeployedEvent” which will identity the template used for the VM, the string is expected as below:

"Deploying <virtual machine> on host <Host> in <Cluster> from template <template name>"

If the $Event.FullFormatMessage contains ‘template’ in the returned string, the string will be manipulated to only return characters after the word template, which will allow for the template name to be stored as a variable. The returned variable will then be used to set the value for the annotation using the Set-CustomField cmdlet.

If ($Event.FullFormattedMessage -like "*template*") 
   { 
   $Template = $Event.FullFormattedMessage  | select-string -pattern '(?<=template)(.*)'  |  select -expa matches | select -expa value |  % { $_.trim() }
   $VM | Set-CustomField -Name "Template" -Value $Template
   } 
}