Category Archives: PowerCLI

PowerCLI script: How to hide the speculative-execution control mechanism for VMs running on affected processors ?

Here is the latest KB update from VMware on Hypervisor-Assisted Guest Mitigation for branch target injection. I would highly recommend you to read this latest KB to understand all minute details. As per KB, it is recommended NOT to apply recently released ESXi patches i.e. ESXi650-201801402-BG, ESXi600-201801402-BG, or ESXi550-201801401-BG on servers based out of affected processors. However, if you have already applied aforementioned patches on affected servers/hosts, VMware recommends to do following in order to hide the speculative-execution control mechanism for virtual machines(quoting from KB)

Update 03/20: As per this KB, new ESXi/vCenter patches are available. With new patches released as on 03/20, this post is NO more valid. Once you start applying new patches, please start using either PowerCLI script for confirming both hypervisor and microcode patches discussed on my earlier blog post or my latest pyVmomi script for the same.

Quote started

For servers using affected Intel processors (see Table 1.) that have applied ESXi650-201801402-BG, ESXi600-201801402-BG, or ESXi550-201801401-BG VMware recommends the following::

On each affected ESXi host, add the following line in the /etc/vmware/config file:
cpuid.7.edx = “—-:00–:—-:—-:—-:—-:—-:—-”
This will hide the speculative-execution control mechanism for virtual machines which are power-cycled afterwards on the ESXi host.
This line will need to be removed after applying a future fixed microcode from Intel in order to enable the full guest OS mitigations for CVE-2017-5715.
When convenient, power-cycle virtual machines on the affected ESXi hosts; rebooting of the ESXi host is not required.

Quote ended

The way I wrote a PowerCLI script for confirming patch validation, I thought it would be really handy to have another quick PowerCLI script to hide the speculative-execution control mechanism for virtual machines as suggested by VMware in KB.

What this script does?

1. Get all the connected hosts from specified vCenter server datacenter.
2. Start iterating through each ESXi host.
3. Check whether ESXi host max supported EVC mode is “intel-haswell” or “intel-broadwell”. If yes, script will then check whether new cpuids are really exposed to these hosts.
4. If new cpuids are exposed on haswell or broadwell hosts, it picks that host for adding “cpuid.7.edx = “—-:00–:—-:—-:—-:—-:—-:—-” into “/etc/vmware/config” file.
5. Before updating above file, script does check whether SSH is enabled on picked (haswell or broadwell) host, if SSH is not enabled, it does enable it.
6. If SSH on picked host is already enabled, it will create SSH session & takes the backup of the file /etc/vmware/config, which gets named as per time stamp.
7. Once file is backed up, it will go ahead and add “cpuid.7.edx = “—-:00–:—-:—-:—-:—-:—-:—-” line at the end of file “/etc/vmware/config”
8. Once file edit operation is done, script does disable SSH on the host where it was not enabled before editing. It also confirms whether SSH is really disabled.
9. Above steps will be repeated only for intel-haswell or intel-broadwell based hosts, where new cpuids are exposed.
10. It keeps logging (on console) the hosts which are edited/not-edited
11. Finally it disconnects vCenter server session.

This script is available on my on my git repo: hideNewCpuids.ps1

After executing above script, it is critical to do VM power-cycle (PowerOFF the VM & PowerON) to get this change affected. Once you do powerCycle, you may wonder how to verify whether speculative-execution control mechanism for virtual machines is actually got hidden or not. When we say “hide the speculative-execution control mechanism for virtual machines”, as earlier blog post pointed, it exposes new CPUIDs to the VM i.e. cpuid.IBRS, cpuid.IBPB, cpuid.STIBP. So it is meant that after script execution, once user does VM power-cycle, these CPUIDs should NO more be exposed to VMs.

How to quickly verify these cpuids are hidden from VM? It can easily done using vCenter MOB (Managed object browser) as follows:

1. To check whether host is having new cpuid capability: please use vCenter MOB (https:// your VC IP/mob): https://10.192.10.20/mob/?moid=host-10&doPath=config (replace your VC IP , host moid and check for “featureCapability” property, you will get list of new cpuids this host is capable of.

Note: Patched ESXi hosts continue to show new CPUIDs (value as 1) even after editing /etc/vmware/config file. This is expected since update to file /etc/vmware/config just hides speculative-execution control mechanism for virtual machines.

2. To check whether new cpuids are hidden from VM:
https://10.192.10.20/mob/?moid=vm-19&doPath=runtime (replace your VC IP , vm moid and check for “featureRequirement” property.

Result:
Before editing file /etc/vmware/config: “”featureRequirement” property of VM shows new CPUIDs i.e. cpuid.IBRS, cpuid.IBPB, cpuid.STIBP

After editing file /etc/vmware/config : “”featureRequirement” property of VM does NOT show new CPUIDs (PowerCycle needed). i.e. cpuid.IBRS, cpuid.IBPB, cpuid.STIBP

How to restore the host to old state? i.e. before editing /etc/vmware/config file
– Before doing this, do make sure that you completely understand the impact it can have on your environment. KB #52345 gives more details.
– To restore, you can either remove added line from /etc/vmware/config or you can comment out using “#” as prefix. To verify this change, you will have to do powercycle. No host reboot required as specified in KB post file update.

Notes:
1. For the sake of simplicity I have hardcoded some values, please do change as per your environment.
2. If you have any comment/feedback on above script, please do provide.
3. I have tested this script on vCenter/ESXi 6.0 & it worked as expected for me.

Additional references:
1. If you are new to MOB, refer official doc

2. I would like to acknowledge this post on Posh-SSH module. I leveraged SSH part of it.

3. If you want to verify above change using PowerCLI, you can leverage nice script written by our own William Lam here.

4. I may write a python script using pyVmomi for verification similar to what we did using MOB above.

I hope this post was useful, please share as appropriate.

PowerCLI script : How to confirm a ESXi VMware hypervisor & microcode patches are applied?: Spectre vulnerability

Some time back, VMware has released one of important KBs on “Hypervisor-Assisted Guest Mitigation for branch target injection”. Please take a look at this KB here. In this KB, there is one important section on “how to confirm a host has both patched microcode and patched VMware hypervisor?” and below is what is posted on KB.

Confirmation of Correct Operation
To confirm a host has both patched microcode and patched VMware hypervisor, use the following steps:
1.Power on a Virtual Machine which is configured to use Virtual Hardware Version 9 or later.
2.Examine the vmware.log file for that VM and look for one of the following entries:
“Capability Found: cpuid.IBRS”
“Capability Found: cpuid.IBPB”
“Capability Found: cpuid.STIBP”
3. Any of the above log entries indicates that both the CPU microcode and hypervisor are properly updated.

I thought it would be really handy if we automate it using PowerCLI. Accordingly, I started looking for any existing cmdlets which can help me to retrieve the VM logs (specially vmware.log) and I could see PowerCLI guru Luc Dekens had written a article on retrieving VM logs long back. I leveraged the same and came up with one quick PowerCLI script to confirm whether a host has both patched microcode and patched VMware hypervisor or not. Here we go.

Update 03/20: As per this KB, new ESXi/vCenter patches are available, hence update (01/13) posted below is no more valid. Once you start applying new patches, please start using either PowerCLI script discussed in this blog post or my latest pyVmomi script for the same.

Update 01/13: As per latest update from VMware on KB, I have published another PowerCLI script, I recommend you to take a look at it before reading this post further.

Script execution steps:
i) It gets all the connected hosts inside the specified cluster. It can be easily modified to consider host from particular datacenter as mentioned above.
ii) It will then iterate through each host in sequence and create a dummyVM.
iii) DRS automation on created dummyVM will be disabled since we need to powerON the VM and if DRS is enabled on the cluster, as part of DRS initial placement workflow, it may place/powerON the VM on some other host inside the cluster.
iv) Now it will powerON the dummyVM
v) Download the vmware.log file at specified location
vi) Scan for the log lines specified in KB (posted above) & record the results.
vii) PowerOff the created VM (to avoid any disk space utilization, you can choose to delete this VM as well)
viii) Step iii) through viii) will repeated for every host inside the cluster.
xi) Finally log the result into one CSV file. CSV file will have HostName & Status (Patched or Un-Patched). Refer CSV sample below.

Below script is available on my git-hub repo as well

<# .SYNOPSIS PowerCLI script:For spectre vulnerability : "How to confirm whether ESXi hypervisor & CPU microcode patches are applied or not?" .NOTES Author: Vikas Shitole .NOTES Site: www.vThinkBeyondVM.com .NOTES Please add the vCenter server IP/credetails as per your environment .NOTES Relese notes: VC 6.5 U1g: https://docs.vmware.com/en/VMware-vSphere/6.5/rn/vsphere-vcenter-server-65u1g-release-notes.html VC 6.0 U3e: https://docs.vmware.com/en/VMware-vSphere/6.0/rn/vsphere-vcenter-server-60u3e-release-notes.html VC 5.5 U3h: https://docs.vmware.com/en/VMware-vSphere/5.5/rn/vsphere-vcenter-server-55u3h-release-notes.html #>

#vCenter Connection, please modify as per your env.

Connect-VIServer -Server 10.160.20.30 -Protocol https -User administrator@vsphere.local -Password VMware!32


function Get-VMLog{
<# .SYNOPSIS Retrieve the virtual machine logs .DESCRIPTION The function retrieves the logs from one or more virtual machines and stores them in a local folder .NOTES Author: Luc Dekens .PARAMETER VM The virtual machine(s) for which you want to retrieve the logs. .PARAMETER Path The folderpath where the virtual machines logs will be stored. The function creates a folder with the name of the virtual machine in the specified path. .EXAMPLE PS> Get-VMLog -VM $vm -Path "C:\VMLogs"
.EXAMPLE
	PS> Get-VM | Get-VMLog -Path "C:\VMLogs"
#>
 
	param(
	[parameter(Mandatory=$true,ValueFromPipeline=$true)]
	[PSObject[]]$VM,
	[parameter(Mandatory=$true)]
	[string]$Path
	)
 
	process{
		foreach($obj in $VM){
			if($obj.GetType().Name -eq "string"){
				$obj = Get-VM -Name $obj
			}
		}
		$logPath = $obj.Extensiondata.Config.Files.LogDirectory
		$dsName = $logPath.Split(']')[0].Trim('[')
		$vmPath = $logPath.Split(']')[1].Trim(' ')
		$ds = Get-Datastore -Name $dsName
		$drvName = "MyDS" + (Get-Random)
		New-PSDrive -Location $ds -Name $drvName -PSProvider VimDatastore -Root '\' | Out-Null
		Copy-DatastoreItem -Item ($drvName + ":" + $vmPath + "vmware.log") -Destination ($Path + "\" + $obj.Name + "\") -Force:$true
		Remove-PSDrive -Name $drvName -Confirm:$false
	}
}
$report = @()

#Location where vmware.log file gets downloaded
$drive="C:\"
$vmname="MyVM"

# Any of below lines must be found in vmware.log file to confirm microcode & VMware hypervisor patch
$pat1='Capability Found: cpuid.IBRS'
$pat2='Capability Found: cpuid.IBPB'
$pat3='Capabliity Found: cpuid.STIBP'

$clusterName="EVCCluster" #Your cluster name, script assumes that cluster is available
$cluster= Get-Cluster -Name $clusterName

#Get connected hosts from above cluster
$esxhosts = Get-Cluster $cluster | Get-VMHost -State Connected

#Counter used to give unique name to dummyvm, you can use any number of your choice.
$i=45

#Iterating through each host for VM creation and scanning vmware.log file
Foreach ($ESXHost in ($esxhosts)){
	$vm=$vmname+$i
#Creating dummy vm with below configuration
	New-VM -Name $vm -VMHost $ESXHost -ResourcePool $cluster -DiskGB 1 -MemoryGB 1 -DrsAutomationLevel Disabled -DiskStorageFormat Thin
	Start-VM -VM $vm -RunAsync -Confirm:$false  #DRS may powerON this VM on some other host inside the cluster
	$dest=$drive+$vm
	Get-VMLog -VM $vm -Path "C:\"
	if (Get-ChildItem -Path $dest -Filter "*.log" | Where {Get-Content -Path $_.FullName | Select-String -Pattern $pat1}){
 		$row = '' | select HostName, Status
 		$row.HostName = $($ESXHost.name)
 		$row.Status="Patched"
 		$report += $row
 		Write-Host "Matched pattern:"+$pat1+":"+$vm+":"+$($ESXHost.name) 
 }
 ElseIf (Get-ChildItem -Path $dest -Filter "*.log" | Where {Get-Content -Path $_.FullName | Select-String -Pattern $pat2}){
  	$row = '' | select HostName, Status
 	$row.HostName = $($ESXHost.name)
	$row.Status ="Patched"
    $report += $row
    Write-Host "Matched pattern:"+$pat2+":"+$vm+":"+$($ESXHost.name) 
 }
  ElseIf (Get-ChildItem -Path $dest -Filter "*.log" | Where {Get-Content -Path $_.FullName | Select-String -Pattern $pat3}){
   	$row = '' | select HostName, Status
  	$row.HostName = $($ESXHost.name)
 	$row.Status ="Patched"
 	$report += $row
   	Write-Host "Matched pattern:"+$pat3+":"+$vm+":"+$($ESXHost.name) 
 }
 Else{
  	$row = '' | select HostName, Status
 	$row.HostName = $($ESXHost.name)
	$row.Status ="Un-Patched"
  	$report += $row
 	Write-Host "Nothing matched on for VM on host:"$($ESXHost.name) 
 }
 Stop-VM -VM $vm -RunAsync -Confirm:$false
 #you can delete this VM as well to avoid any disk space consumption or minimize the disk size
 $i++
}
#Log the report into this CSV file, you can provide your name
$report | Sort HostName | Export-Csv -Path "D:PatchStatus.csv"

#Disconnect the vCenter server
 Disconnect-VIServer -Confirm:$false
 

Sample CSV file result:
#TYPE Selected.System.String
“HostName”,”Status”
“10.20.30.20”,”Patched”
“10.20.30.21”,”Un-Patched”

Notes:
1. For the sake of simplicity I have hardcoded some values, please do change as per your environment.
2. If you have any comment/feedback on above script, please do provide.
3. There can be other ways to automate the same. Since I started with this approach, I continued. I haven’t yet thought about other approach (probably we may be able to just use datastore browser APIs to navigate/scan vmware.log file instead of downloading it outside).
4. I would recommend you to use this script as sample and make changes as needed. Let me know what changes you did or probably any other way you found.
5. I have tested this script on vCenter/ESXi 6.5 & it worked as expected for me. I will update this article as I update above script

Note: I would like you to understand “vMotion and EVC Information” section from KB, specially below lines. Let me know if you have any doubts I can share my understanding.

“In order to maintain this compatibility the new features are hidden from guests within the cluster until all hosts in the cluster are properly updated”

Some must read links:
1. VC 6.5 U1g release notes
2. VC 6.0 U3e release notes
3. VC 5.5 U3h release notes
4. Microsoft has released similar validation script for their Guest OS

I hope this will be helpful.

Editing Storage DRS VM overrides : Java vSphere SDK and PowerCLI script

Recently I had an opportunity to work on one of customers requirement with respect to Storage DRS. Their requirement was to edit/remove Storage DRS “VM overrides” settings using vSphere API. As part of this exercise, I had written scripts both in PowerCLI as well as using vSphere Java SDK. I thought it is good to share with you as well. Before discussing about these scripts, let us first understand what exactly is Storage DRS VM overrides?

When user selects Storage DRS cluster (SDRS POD) as a storage for a VM, Storage DRS takes care of placing that VM on right datastore among all the datastores inside Storage DRS cluster. Once SDRS places that VM, all the default SDRS cluster level configuration gets applied on that VM such as ‘Storage DRS automation level’, ‘Keeps VMDK together’ etc. “VM overrides” settings comes to into picture if user wants to override some of Storage DRS cluster level settings. Ex. SDRS cluster level settings for “Automation level” is “Manual” and now for particular VM, user wants to have it as “Fully Automated” or user wants to disable SDRS on particular VM or user just wants to disable default affinity rule “Keeps VMDK together” for specific VM. This can be achieved using SDRS VM overrides settings. Let us look at some of vSphere web client screenshot on how to do it.

Above screenshot shows how to traverse to the “VM overrides” workflow using web client. You could see, currently there is NO VM for which SDRS settings are overridden. You can click on add button in order to override SDRS settings for specific VM as shown below.

Apart from this, there is another reason VM can get listed under “VM orderride” section i.e. When user is creating a VM and user wants to place that VM on a particular datastore among datastores in SDRS cluster instead of relaying on SDRS itself for initial placement, it does mean that user would like to take control of this VM , hence SDRS gets disabled on such VM and gets listed under “VM override” section. Take a look at below screenshot to understand the same.

You could see in above screenshot that if user wants to place the VM on a particular datastore from SDRS cluster instead of relaying on SDRS, we can select specific datastore only when we select checkbox for disabling SDRS. Once VM creation is completed, you could see that VM gets listed under “VM overrides” section as shown below.

Now that we understood, what is VM overrides? and how to configure it? Please take a look at scripts for editing/removing these Storage DRS VM overrides. Below are script locations.

1. Java SDK script on my github repo and on VMware Sample Exchange

2. PowerCLI equivalent on my github repo and on VMware Sample Exchange

If you have still not setup your YAVI JAVA Eclipse environment:Getting started tutorial

Important tutorials to start with: Part I & Part II

If you want to understand Storage DRS, take a look at this whitepaper

Let me know if you have any comments.

Some of useful VMware PowerCLI scripts added into VMware Sample Exchange

Recently while working on couple of customer cases, I had to write some useful vSphere PowerCLI scripts. I had submitted these scripts on VMware Sample Exchange, I thought to share on blog as well. If you haven’t still explored VMware Sample Exchange , please have your “My VMware” account created (Mandatory to have My VMware account to contribute or request) and enjoy the single pane of glass for VMware API samples across any product, any programming language and any platform (including CLIs). You can browse already existing samples as well as request new API sample that you are looking for. This is really cool site for learning VMware APIs, it is a must book-mark if you ask me. Here is the VMTN Sample Exchange community for more details.

Here we go for the PowerCLI scripts that I am talking about.

Below are the use-cases these script will help you on.

1. Report on vSphere compute cluster usage/capacity data on CPU, Memory and Storage.
2. Report on DRS VM-VM affinity rules associated with each VMs in the cluster.
3. Solution 1 : Report on VMs with “multi-writer” flag enabled.
4. Solution 2: Report on VMs with “multi-writer” flag enabled.

1. Report on vSphere compute cluster usage/capacity data on CPU, Memory and Storage. Sample exchange script location


< #
.SYNOPSIS Getting Cluster usage/capacity data on CPU, Memory and Storage 
.NOTES  Author:  Vikas Shitole
.NOTES  Site:    www.vThinkBeyondVM.com
.NOTES  Reference: http://vthinkbeyondvm.com/category/powercli/
.NOTES Please add the vCenter server IP/credentails as per your environment
#>

Connect-VIServer -Server 10.92.166.82 -User Administrator@vsphere.local -Password xyz!23

$report = @()

Write-host "Report Generation is in Progress..."

foreach ($cluster in Get-Cluster   ){

  $row = '' | select ClusterName,CpuCapacity , CpuUsed, MemCapacity,MemUsed ,StorageCapacity,StorageUsed
  
$cluster = Get-Cluster -Name $clusterName
$cluster_view = Get-View ($cluster)
$resourceSummary=$cluster_view.GetResourceUsage()
$row.ClusterName =$cluster_view.Name
$row.CpuCapacity =$resourceSummary.CpuCapacityMHz
$row.CpuUsed =$resourceSummary.CpuUsedMHz
$row.MemCapacity =$resourceSummary.MemCapacityMB
$row.MemUsed =$resourceSummary.MemUsedMB
$row.StorageCapacity =$resourceSummary.StorageCapacityMB
$row.StorageUsed =$resourceSummary.StorageUsedMB

$report += $row
}
$report | Sort  ClusterName | Export-Csv -Path "D:Clusterstats.csv" #Please change the CSV file location

Write-host "Report Generation is completed, please chekc the CSV file"		
	

2. Report on DRS VM-VM affinity rules associated with each VMs in the cluster. Sample Exchange script location


< #
.SYNOPSIS Getting DRS VM-VM affinity rules associated with each VMs in the cluster.
.NOTES  Author:  Vikas Shitole
.NOTES  Site:    www.vThinkBeyondVM.com
.NOTES  Reference: http://vthinkbeyondvm.com/category/powercli/ 
.NOTES Please add the vCenter server IP/credetails as per your environment

#>

Connect-VIServer -Server 10.192.x.y -User Administrator@vsphere.local -Password xyz!23

$clusterName = "BLR" #Your cluster name
$cluster = Get-Cluster -Name $clusterName
$vms = Get-View ($cluster| Get-VM)
$cluster_view = Get-View ($cluster)

$report = @()

Write-host "Report Generation is in Progress..."

foreach ($vm in $vms  ){

  $row = '' | select VMName, Rules
  $rules=$cluster_view.FindRulesForVm($vm.MoRef)
  $ruleNameArray=" "
  #There can be more than one rule assciated with single VM
  foreach($rule in $rules){
  $ruleNameArray+=$rule.Name
  $ruleNameArray+=","
  }
  $row.VMName =$vm.Name
  $row.Rules = $ruleNameArray
  $report += $row
  }
$report | Sort Name | Export-Csv -Path "D:VMsRules.csv" #Please change the CSV file location

Write-host "Report Generation is completed, please chekc the CSV file"
	
	

3. Solution 1 : Report on VMs with “multi-writer” flag enabled. Sample exchange script location

< #
.SYNOPSIS: This script first downloads all the VMX files (per host/cluster/datacenter/VC)at specified file location
.Once downloaded, it will scan each VMX file one by one to get "multi-writer" entry inside VMX
.Finally it will list all matching VMX file names into specified file location. 
.As it downloads all the VMX files, it is going to take more time, that should be fine. 
.It is all right to download the VMX file when VM is up and running.
.If name of the VM is changed, VMX file can be different from VM display name (visible from inventory). 
.There are 2 file locations you need to specify. 1. Directory where VMX file will be downloaded 2. Output file.

.NOTES  Author:  Vikas Shitole
.NOTES  Site:    www.vThinkBeyondVM.com
.NOTES  Reference: http://vthinkbeyondvm.com/category/powercli/ and https://communities.vmware.com/message/2269363#2269363
.NOTES Please add the vCenter server IP/credetails as per your environment
.NOTES Alternatively you can use this script where API properties are used. https://github.com/vThinkBeyondVM/vThinkBVM-scripts/blob/master/Powershell-PowerCLI/VMMultiWriterReport.ps1
#>


Write-host "Connecting to vCenter server.."
Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false -DisplayDeprecationWarnings:$false -Scope User
Connect-VIServer -Server 10.192.67.143 -User administrator@vsphere.local -Password Admin!23

$tgtFolder = "C:\Temp\VMX\"  #Create this directory as per your environment
#$tgtString = 'scsi0:0.sharing="multi-writer"'
$tgtString = '"multi-writer"'
foreach ($vm in Get-VM ){

Get-VM -Name $vm.get_Name() | %{
  $dsName,$vmxPath = $_.ExtensionData.Config.Files.VmPathName.Split()
  $dsName = $dsName.Trim('[]')
  $ds = Get-Datastore -Name $dsName
  New-PSDrive -Location $ds -Name DS -PSProvider VimDatastore -Root "\" | Out-Null
  Copy-DatastoreItem -Item "DS:$vmxPath" -Destination $tgtFolder
  Remove-PSDrive -Name DS -Confirm:$false
}

}
Get-ChildItem -Path $tgtFolder -Filter "*.vmx" | Where {Get-Content -Path $_.FullName | Select-String -Pattern $tgtString} | Select Name | Out-File C:\test1.txt
Write-host "Execution is done... please check the file with all VMX file names."

# Below cmdlet can help to scan VMs per Datacenter/Datastore/Host/Cluster. Please modify the script as required. 

#$myDatacenter = Get-Datacenter -Name "MyDatacenter"
#Get-VM -Location $myDatacenter

#$myDatastore = Get-Datastore -Name "MyDatastore"
#Get-VM -Datastore $myDatastore

#$myHost=Get-VMHost -Name "HostName"
#Get-VM -Location $myHost

#$myCluster=Get-Cluster -Name "ClusterName"
#Get-VM -Location $myCluster

	

4. Solution 2 : Report on VMs with “multi-writer” flag enabled. Sample Exchange script location


< #
.SYNOPSIS: This script connects to the vCenter Server and prepares a report on All VMs with at-least one disk enabled with "Multi-Writer" sharing.
.Report will be generated as CSV file with "VM Name". This can be modified to add some more columns as needed
.VC IP/UserName/Password are hardcoded below, please change them as per your environment
.By default this script scans all the VMs/VMDK in the vCenter Server. It can be easily twicked to scan VM per cluster or host or datacenter

.NOTES  Author:  Vikas Shitole
.NOTES  Site:    www.vThinkBeyondVM.com
.NOTES  Reference: http://vthinkbeyondvm.com/category/powercli/
.NOTES Please add the vCenter server IP/credetails as per your environment

Alternate solution by scanning VMX file is here: https://github.com/vThinkBeyondVM/vThinkBVM-scripts/blob/master/Powershell-PowerCLI/VMMultiWriterReport2.ps1

#>

Write-host "Connecting to vCenter server.."
Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false -DisplayDeprecationWarnings:$false -Scope User
Connect-VIServer -Server 10.192.x.y -User administrator@vsphere.local -Password xyz@123


$report = @()
Write-host "Report Generation is in Progress..."

foreach ($vm in Get-VM  ){
$view = Get-View $vm
$settings=Get-AdvancedSetting -Entity $vm
   if ($view.config.hardware.Device.Backing.sharing -eq "sharingMultiWriter" -or $settings.value -eq "multi-writer"){
   $row = '' | select Name
      $row.Name = $vm.Name
   $report += $row
   }
   
}
$report | Sort Name | Export-Csv -Path "D:MultiWriter.csv" #Please change the CSV file location

Write-host "Report is generated successfully, please check the CSV file at specified location"

#If you want to generate report per Datacenter/Datastore/Host/Cluster, modify script using below code.

#$myDatacenter = Get-Datacenter -Name "MyDatacenter"
#Get-VM -Location $myDatacenter

#$myDatastore = Get-Datastore -Name "MyDatastore"
#Get-VM -Datastore $myDatastore

#$myHost=Get-VMHost -Name "HostName"
#Get-VM -Location $myHost

#$myCluster=Get-Cluster -Name "ClusterName"
#Get-VM -Location $myCluster