Remove a VMFS datastore using powershell

Standard

Recently I had to remove a lot of VMFS datastores from our VMWare infrastructure as we had migrated to new storage and no longer needed them.
In order to remove a datastore from your vmware infrastructure correctly you must follow the instructions given in this article. http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=2004605
Basically it says you have to make sure nothing is using the datastore and then unmount and detach the datastore from each connected host. Since we work with large clusters with many ESXi hosts I didn’t enjoy the prospect of manually unmounting and detaching each datastore from each host. To avoid this I found this article http://blogs.vmware.com/vsphere/2012/01/automating-datastore-storage-device-detachment-in-vsphere-5.html which explains how to automate datastore removal. It’s a great article and includes a few functions which give you the ability to unmount and detach datastores like described above. I simply used these functions to create the script in this blog.

If the functions are already there then why use this script?

I wrote this script so that it does everything for you in one go. You don’t have to mess around with the functions or cmd-lets yourself. Also, I made a few changes to the mentioned functions which I feel improve them. Basically all you have to do is put in the name of the datastore and run the script.

What does the script do exactly?

1. The script gets the datastore info and then asks for confirmation before beginning the removal process.
2. The datastore is unmounted on each host connected to it.
3. The datastore is detached on each host connected to it.
4. At the end of the script run the datastore name and LUN canonical name is returned. This is useful as you will probably need this information when you have to delete the LUN in your storage system later on.

How do I run the script?

I. Go through the checklist “Unmounting a LUN checklist” here http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=2004605 and make sure your datastore is ready for removal.
II.Copy the script to your computer and open it with and editor like powershell_ISE or powerGUI.
III. In the #Parameters section enter the name of your vCenter server and the name of the datastore you wish to remove.
IV. Hit F5 to run the script.

The script itself

I’m not going to go into details about the code as you can see all that in the links to KB articles above. I have simply copied in the script for you here. Please leave a comment if you find the script useful. Enjoy!

#############################################################################################################
##script:			Unmount_Detach-Datastore.ps1
##
##Description:		Unmounts and detaches a given datastore from all its connected hosts.
#+					Before running make sure that the datastore doesn't have any live VMs or templates on it.
#Additional info:	http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=2004605
#Additional info:	http://blogs.vmware.com/vsphere/2012/01/automating-datastore-storage-device-detachment-in-vsphere-5.html
#Additional info:	https://communities.vmware.com/docs/DOC-18008
##Created by:		Noam Wajnman
##Creation Date:	August 4, 2014
##############################################################################################################
function Connect-Vcenter {
	#############################################################################################
	##Function:			Connect-Vcenter
	##
	##Description:		Loads the VMWare snap-in for powershell and connects to vcenter.
	##
	##Created by:		Noam Wajnman
	##Creation Date:	20 November, 2012
	##Updated:			31 July, 2014
	##############################################################################################
	param(
		$vcenter
	)
	#Load snap-in
	if (-not (Get-PSSnapin -Name VMware.VimAutomation.Core -erroraction "silentlycontinue" )) {
        Write-Host "Importing VMWare Snap-in VMware.VimAutomation.Core..."    
		Add-PSSnapin VMware.VimAutomation.Core
	}
    Set-PowerCLIConfiguration -DefaultVIServerMode Single -invalidCertificateAction "ignore" -confirm:$false | out-null
	#Connect to vCenter
	if ($global:DefaultVIServers.Count -lt 1) {
        Write-Host "Connecting to vcenter server $vcenter..."		
		Connect-VIServer $vCenter
	}
}
Function Get-DatastoreMountInfo {
	#function copied from https://communities.vmware.com/docs/DOC-18008
	[CmdletBinding()]
	Param (
		[Parameter(ValueFromPipeline=$true)]
		$Datastore
	)
	Process {
		$AllInfo = @()
		if (-not $Datastore) {
			$Datastore = Get-Datastore
		}
		Foreach ($ds in $Datastore) {  
			if ($ds.ExtensionData.info.Vmfs) {
				$hostviewDSDiskName = $ds.ExtensionData.Info.vmfs.extent[0].diskname
				if ($ds.ExtensionData.Host) {
					$attachedHosts = $ds.ExtensionData.Host
					Foreach ($VMHost in $attachedHosts) {
						$hostview = Get-View $VMHost.Key
						$hostviewDSState = $VMHost.MountInfo.Mounted
						$StorageSys = Get-View $HostView.ConfigManager.StorageSystem
						$devices = $StorageSys.StorageDeviceInfo.ScsiLun
						Foreach ($device in $devices) {
							$Info = '' | Select Datastore, VMHost, Lun, Mounted, State
							if ($device.canonicalName -eq $hostviewDSDiskName) {
								$hostviewDSAttachState = ''
								if ($device.operationalState[0] -eq "ok") {
									$hostviewDSAttachState = "Attached"							
								} elseif ($device.operationalState[0] -eq "off") {
									$hostviewDSAttachState = "Detached"							
								} else {
									$hostviewDSAttachState = $device.operationalstate[0]
								}
								$Info.Datastore = $ds.Name
								$Info.Lun = $hostviewDSDiskName
								$Info.VMHost = $hostview.Name
								$Info.Mounted = $HostViewDSState
								$Info.State = $hostviewDSAttachState
								$AllInfo += $Info
							}
						}
						
					}
				}
			}
		}
		$AllInfo
	}
}
Function Detach-Datastore {
	#function based on code found here https://communities.vmware.com/docs/DOC-18008
	[CmdletBinding()]
	Param (
		[Parameter(ValueFromPipeline=$true)]
		$Datastore
	)
	Process {
		if (-not $Datastore) {
			Write-Host "No Datastore defined as input"
			Exit
		}
		Foreach ($ds in $Datastore) {
			$hostviewDSDiskName = $ds.ExtensionData.Info.vmfs.extent[0].Diskname
			if ($ds.ExtensionData.Host) {
				$attachedHosts = $ds.ExtensionData.Host
				Foreach ($VMHost in $attachedHosts) {
					$hostview = Get-View $VMHost.Key
					$StorageSys = Get-View $HostView.ConfigManager.StorageSystem
					$devices = $StorageSys.StorageDeviceInfo.ScsiLun
					Foreach ($device in $devices) {
						if ($device.canonicalName -eq $hostviewDSDiskName) {
							#If the device is attached then detach it (I added this to the function to prevent error messages in vcenter when running the script)
							if ($device.operationalState[0] -eq "ok") { 
								$LunUUID = $Device.Uuid
								Write-Host "Detaching LUN $($Device.CanonicalName) from host $($hostview.Name)..."
								$StorageSys.DetachScsiLun($LunUUID);
							}
							#If the device isn't attached then skip it (I added this to the function to prevent error messages in vcenter when running the script)
							else {
								Write-Host "LUN $($Device.CanonicalName) is not attached on host $($hostview.Name)..."
							}
						}
					}
				}
			}
		}
	}
}
Function Unmount-Datastore {
	#function based on code found here https://communities.vmware.com/docs/DOC-18008
	[CmdletBinding()]
	Param (
		[Parameter(ValueFromPipeline=$true)]
		$Datastore
	)
	Process {
		if (-not $Datastore) {
			Write-Host "No Datastore defined as input"
			Exit
		}
		Foreach ($ds in $Datastore) {
			$hostviewDSDiskName = $ds.ExtensionData.Info.vmfs.extent[0].Diskname
			if ($ds.ExtensionData.Host) {
				$attachedHosts = $ds.ExtensionData.Host
				Foreach ($VMHost in $attachedHosts) {
					$hostview = Get-View $VMHost.Key
					$mounted = $VMHost.MountInfo.Mounted
					#If the device is mounted then unmount it (I added this to the function to prevent error messages in vcenter when running the script)
					if ($mounted -eq $true) {
						$StorageSys = Get-View $HostView.ConfigManager.StorageSystem
						Write-Host "Unmounting VMFS Datastore $($DS.Name) from host $($hostview.Name)..."
						$StorageSys.UnmountVmfsVolume($DS.ExtensionData.Info.vmfs.uuid);
					}
					#If the device isn't mounted then skip it (I added this to the function to prevent error messages in vcenter when running the script)
					else {
						Write-Host "VMFS Datastore $($DS.Name) is already unmounted on host $($hostview.Name)..."
					}
				}
			}
		}
	}
}
#VARIABLES
#Parameters 
$vcenter = "Some_Vcenter_Server"
$DSName = "Some_Datastore"
#SCRIPT MAIN
clear
Connect-Vcenter $vcenter
$datastore = Get-Datastore -Name $DSName
$CanonicalName = $datastore.ExtensionData.Info.Vmfs.Extent[0].DiskName
$GoAhead = Read-Host "Are you sure that you want to unmount and detach: `n$DSName `n$CanonicalName`nfrom all its connected hosts?"
if ($GoAhead -eq "yes" -or $GoAhead -eq "y" -or $GoAhead -eq "Y") {
	Write-Host "Unmounting datastore $DSName..."	
	$datastore | Unmount-Datastore
	Write-Host "Detaching datastore $DSName from hosts..."
	$datastore | Detach-Datastore
}
$DSName
$CanonicalName
Advertisements

Get CPU usage and CPU ready values for all VMs in a cluster

Standard

When working with VMWare it is crucial to monitor the performance of your HA clusters and VMs. It is important to be able to get performance data so you can make sure your VMs have the resources they need and to know if they are sized correctly. To determine this I often use the CPU usage and CPU ready performance counters.

– What are the cpu usage/ready perf. counters and what do they show us?

The CPU usage value is sort of like the value you see in the task manager in windows, the main difference being that this value is not measured by the VM’s OS but by the ESXi host it is running on. As in the task manager this value indicates how much CPU is being used by your VM.
The CPU ready value is the amount of time your VM’s CPU has been “ready”, meaning how much time it has been waiting, doing nothing, for CPU cycles to be assigned to it by the host. If this value is high it is often an indicator of cluster health problems and/or bad VM sizing. I urge you to search for information about these counters and learn about them as they are highly useful when managing a VMWare environment. You can read more about CPU ready time here:
http://blogs.totalcaos.com/understanding-rdy-cpu-ready/

– Why should I use the script found in this blog post?

Gathering data like the performance counters described above can be done with Vcenter Operations Manager or with Veeam if you have this. However these applications are complicated to use and configure (and cost a lot of money). The script I have written below will give you a simple list/csv of the VMs in a given cluster with their CPU usage and CPU ready values in percent. You can then review your VMs and see check for any high/problematic values you find and improve the efficiency of your infrastructure. Any VMs with an average cpu usage over 70 % should probably be assigned more resources. If there are any VMs with ready times of 10% or more it probably means that your cluster/host is overcommitted or that the VM is oversized.

– The script itself and how I wrote it

To simplify the structure of the script I have divided it into three sections variables, functions and script main.

Variables

#VARIABLES
$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath
$CSV = "$dir\Average_CPU_Usage_Peak_Hours.csv"
#Parameters
$vcenter = "Some_Vcenter_server" #Name of your vcenter server
$ClusterName = "Some_cluster" #CPU usage and CPU ready values will be collected for all VMs in this cluster.
$DaysBack = 14 #Number of days back to collect performance counters.
$PeakTimeStart = 8 #hour of the day in 24 hour format 
$PeakTimeEnd = 20 #hour of the day in 24 hour format
$rdy_interval = 7200 #interval of rdy time values aggregation/averaging in seconds. This value should be changed according to your vcenter statistics settings.

Before running the script you will need to change the values in the #parameters section according to your needs/environment. $vcenter should be the name of your vcenter server and $ClusterName the name of the cluster you wish to collect VM performance data from. $DaysBack is the amount of time you wish to collect performance from. E.g if it is set to 14 days you will collect performance data going back 14 days to the time you are running the script. Make sure you set this value to a valid number of days (your vcenter DB must retain the info for the amount of time specified).

I have found that performance data is more useful when it is gathered in business hours so the results show values reflecting a busy system. To gather performance counters from the relevant times of day you must change the values for $peakTimeStart and $peakTimeEnd to suit your needs.

The last parameter is the $rdy_interval. When gathering the cpu ready time performance counters the values are returned as a number of milliseconds. To convert this number correctly to a percent value it is necessary to take into account the aggregation interval (the time between the data points stored in the vcenter database). On my vcenter installation the ready time values are aggregated/averaged every two hours and therefore I have set the $rdy_interval to 7200 seconds. You can read more about this here:
http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=2002181

Now to the functions.

Functions

1. Connect-Vcenter

function Connect-Vcenter {
	param(
		$vcenter
	)
	#Load snap-in
	if (-not (Get-PSSnapin -Name VMware.VimAutomation.Core)) {
		Add-PSSnapin VMware.VimAutomation.Core
	}
	#Connect to vCenter
	if ($global:DefaultVIServers.Count -lt 1) {
		Connect-VIServer $vCenter
	}
}

Connect-Vcenter is a small function that simply adds the snap-in for VMware cmdlets and connects to a vcenter server making the powershell instance ready to fire commands.
2. Get-VMCPUAverage

function Get-VMCPUAverage {
    param (
        $VM
    )
	$Start = (get-date).AddDays(-$DaysBack)
	$Finish = get-date
    
	$stats = get-vm $VM | get-stat -MaxSamples "1000" -Start $Start -Finish $Finish -Stat "cpu.usage.average" | `
    ? { ($_.TimeStamp).Hour -gt $PeakTimeStart -and ($_.TimeStamp).Hour -lt $PeakTimeEnd }
	$aggr_stats = $stats | Measure-Object -Property Value -Average
	$avg = $aggr_stats.Average
	return $avg
}

This function uses the vmware cmdlet Get-Stat to collect the performance counter cpu.usage.average in the specified timeframe. The average value of all the collected data points are then returned.
3. Get-VMCpuRDY

function Get-VMCpuRDY {
	param (
        $VM
    )
	$Start = (get-date).AddDays(-$DaysBack)
	$Finish = get-date
	
	$stats = get-vm $VM | Get-Stat -MaxSamples "1000" -Start $Start -Finish $Finish -Stat Cpu.Ready.Summation | `
    ? { ($_.TimeStamp).Hour -gt $PeakTimeStart -and ($_.TimeStamp).Hour -lt $PeakTimeEnd -and $_.Instance -eq ""}	
	$aggr_stats = $stats | Measure-Object -Property Value -Average	
	$rdy = [Math]::Round(((($aggr_stats.Average)/1000)/$rdy_interval) * 100,1)
	return $rdy
}

Get-VMCpuRDY collects the average values of the performance counter Cpu.Ready.Summation in the specified timeframe. As explained earlier in the post this counter returns a summation of the milliseconds in which the CPU was waiting for resources. In order to convert this into a percentage value I first divide the amount of milliseconds by 1000 to convert to seconds. I divide this amount of seconds by the $rdy_interval, which is the amount of seconds between each data point, and then multiply by 100. I then use [Math]::Round to round off the value to one decimal which is the number returned by the function.

Script Main

#SCRIPT MAIN
clear
#Load the VMWare module and connect to vCenter
Connect-Vcenter -vcenter $vcenter
$AvgCPUValues = @() #Create array to hold the CPU usage and CPU ready values
Get-Cluster $ClusterName | Get-VM | ? {$_.PowerState -eq "PoweredOn"} | % {	#loop through all powered on VMs in the cluster
	$AvgCPUValue = "" | Select "VM","CpuAvg","CpuRdy" #create a custom object with these properties.
	$AvgCPUValue.VM = $_.Name
	$AvgCPUValue.CpuAvg = "{0:N2}" -f $(Get-VMCPUAverage -VM $_) #Get VM CPU usage and round to two decimals
	$AvgCPUValue.CpuRdy = Get-VMCpuRDY -VM $_
	$AvgCPUValues += $AvgCPUValue
}
$AvgCPUValues | Export-Csv $CSV -NoTypeInformation -Force

In the script main I first connect to the vcenter server using my function Connect-Vcenter. I then create an array $AvgCPUValues. The next step is to loop through all the VMs in the cluster using Get-Cluster $ClusterName | Get-VM. In the loop I create a custom object for each VM with the properties VM, CPUAvg and CPURdy. I use the functions Get-VMCPUAverage and Get-VMCpuRDY to get cpu usage and cpu ready values and then assign these to the corresponding properties on the custom object. The custom object is then added to the array $AvgCPUValues which I created in the beginning of the script main.
When the loop has completed I then export this array to CSV in the directory the script was run.

– That’s it! You’re ready to go!

I really hope you find this script useful. Remember to fill out the #parameters section before you run the script.
I have copied in the full script below.

#############################################################################################################
##script:			Get-VMCPUAverage.ps1
##
##Description:		Gets "CPU usage" and "cpu ready" for all VMs in a given cluster and exports the results
#+					to a CSV file in the script directory.
##Created by:		Noam Wajnman
##Creation Date:	March 11, 2014
##Updated on:		May 20, 2014
##############################################################################################################
#VARIABLES
$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath
$CSV = "$dir\Average_CPU_Usage_Peak_Hours.csv"
#Parameters
$vcenter = "Some_Vcenter_server" #Name of your vcenter server
$ClusterName = "Some_cluster" #CPU usage and CPU ready values will be collected for all VMs in this cluster.
$DaysBack = 14 #Number of days back to collect performance counters.
$PeakTimeStart = 8 #hour of the day in 24 hour format 
$PeakTimeEnd = 20 #hour of the day in 24 hour format
$rdy_interval = 7200 #interval of rdy time values aggregation/averaging in seconds. This value should be changed according to your vcenter statistics settings.
#FUNCTIONS
function Connect-Vcenter {
	param(
		$vcenter
	)
	#Load snap-in
	if (-not (Get-PSSnapin -Name VMware.VimAutomation.Core)) {
		Add-PSSnapin VMware.VimAutomation.Core
	}
	#Connect to vCenter
	if ($global:DefaultVIServers.Count -lt 1) {
		Connect-VIServer $vCenter
	}
}
function Get-VMCPUAverage {
    param (
        $VM
    )
	$Start = (get-date).AddDays(-$DaysBack)
	$Finish = get-date
    
	$stats = get-vm $VM | get-stat -MaxSamples "1000" -Start $Start -Finish $Finish -Stat "cpu.usage.average" | `
    ? { ($_.TimeStamp).Hour -gt $PeakTimeStart -and ($_.TimeStamp).Hour -lt $PeakTimeEnd }
	$aggr_stats = $stats | Measure-Object -Property Value -Average
	$avg = $aggr_stats.Average
	return $avg
}
function Get-VMCpuRDY {
	param (
        $VM
    )
	$Start = (get-date).AddDays(-$DaysBack)
	$Finish = get-date
	
	$stats = get-vm $VM | Get-Stat -MaxSamples "1000" -Start $Start -Finish $Finish -Stat Cpu.Ready.Summation | `
    ? { ($_.TimeStamp).Hour -gt $PeakTimeStart -and ($_.TimeStamp).Hour -lt $PeakTimeEnd -and $_.Instance -eq ""}	
	$aggr_stats = $stats | Measure-Object -Property Value -Average	
	$rdy = [Math]::Round(((($aggr_stats.Average)/1000)/$rdy_interval) * 100,1)
	return $rdy
}
#SCRIPT MAIN
clear
#Load the VMWare module and connect to vCenter
Connect-Vcenter -vcenter $vcenter
$AvgCPUValues = @() #Create array to hold the CPU usage and CPU ready values
Get-Cluster $ClusterName | Get-VM | ? {$_.PowerState -eq "PoweredOn"} | % {	#loop through all powered on VMs in the cluster
	$AvgCPUValue = "" | Select "VM","CpuAvg","CpuRdy" #create a custom object with these properties.
	$AvgCPUValue.VM = $_.Name
	$AvgCPUValue.CpuAvg = "{0:N2}" -f $(Get-VMCPUAverage -VM $_) #Get VM CPU usage and round to two decimals
	$AvgCPUValue.CpuRdy = Get-VMCpuRDY -VM $_
	$AvgCPUValues += $AvgCPUValue
}
$AvgCPUValues | Export-Csv $CSV -NoTypeInformation -Force

Set advanced settings on all VMs in a cluster

Standard

Recently I was tasked with modifying the VM advanced settings (or VMX settings) on all of our VMs. Since I didn’t want to manually edit the VMX files on 500+ VMs I resolved to make a script which could do the job instead. I eventually made a good and working script which I will go over below.
First off I want to say that I have based a great deal of this script on some code that I found here: https://communities.vmware.com/docs/DOC-18653. Many thanks to Alan Renouf for posting this article!
The script posted here has been improved a bit and is also more complete since it also connects to vcenter on its own. This should hopefully make this script useful even to admins who aren’t experienced with powershell.

###############################################################################
##set-VMAdvancedSettings.ps1
##
##Description:		sets/creates advanced settings on VMs based on CSV input
##Created by:		Noam Wajnman
##Creation Date:	March 11, 2014
###############################################################################
#VARIABLES
$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath
$PathToCSV = "$dir\vmsettings.csv"
$vcenter = "some_vcenter_server" #script will connect to this vcenter server
$clusterName = "Some_cluster" #advanced settings will be set on all VMs on this cluster
#FUNCTIONS
function ConnectToVcenter {
	param(
		$vcenter
	)
	#Load snap-in and connect to vCenter
	if (-not (Get-PSSnapin -Name VMware.VimAutomation.Core)) {
		Add-PSSnapin VMware.VimAutomation.Core
	}
	if ($global:DefaultVIServers.Count -ne 1) {
		Connect-VIServer $vCenter
	}
}
function Set-VMAdvancedConfiguration { 
	###############################################################################
	##Function set-VMAdvancedConfiguration
	##
	##Description:		sets/creates advanced settings on VMs
	##Created by:		Noam Wajnman
	##Creation Date:	March 11, 2014
	##Credits:			Based on code written by alan renouf found here: 
	#+					https://communities.vmware.com/docs/DOC-18653
	###############################################################################
	[CmdletBinding()]
	[OutputType([System.String])]
  	param(  
    	[Parameter(Mandatory=$true,ValueFromPipeline=$true)]$vm,  
      	[string]$PathToCSV
    )
	begin {		
		$vmConfigSpec = new-object VMware.Vim.VirtualMachineConfigSpec  
		$vmsettings = Import-Csv $PathToCSV -Header Key,Value
		$vmsettings | Foreach {  
        	$Value = new-object vmware.vim.optionvalue  
        	$Value.key = $_.key  
        	$Value.value = $_.value  
        	$vmConfigSpec.ExtraConfig += $Value  
        	Write-Host "Adding $($_.Key) = $($_.Value) to configuration specifications"  
		}
	}
  	process {    	  
    	$Task = ($vm.ExtensionData).ReconfigVM_Task($vmConfigSpec)  
      	Write-Host "Setting Advanced configuration for $($VM.Name)"    	 
  	}
	end {
		Write-Host "Finished applying advanced settings to VMs."
	}
}

#SCRIPT MAIN
ConnectToVcenter -vcenter $vcenter
Get-cluster $clusterName | get-vm | Set-VMAdvancedConfiguration -PathToCSV $PathToCSV

All you need to do to run the script is:
1. Create a CSV file in the same directory as the script. The CSV must have two “columns”, the name of the settings and the desired value. I did it in excel and my example CSV file looks like this:
vmsettings_in_excel
2. Edit two values in the #Variables section of the script. Set $vcenter to the name of your vcenter server and $clusterName to the name of your cluster (all VMs on this cluster will have their advanced settings changed according to what you wrote in the CSV file).

You can then run the script and see all the specified settings being changed automatically on your VMs. Yay 🙂

I hope you will find the script useful!!

Set VMTools time synchronization mode on all VMs in a cluster

Standard

When changing the time synchronization settings in your environment you may have to also make modifications in your VMWare infrastructure. Among other things it can be necessary to control if the VM should synchronize its time from the host server or not. Normally it’s a manual process where you have to click into the VM settings and go to the Options tab and select VMWare Tools and there check the box or not. This script will do all that for you and set the VMTools host time synchronization to the value you choose, on all VMs in a given cluster. This will save you a lot of time especially if you, like me, manage a VMWare infrastructure with hundreds of VMs.
Although the script is quite short I have divided it into three sections variables, functions and script main to simplify its structure.

Variables

#VARIABLES
$DebugPreference = "continue" #comment out to disable debug info
#Parameters
$vcenter = "some_vcenter_server" #your vcenter server name
$clusterName = "Some_Cluster" #advanced settings will be set on all VMs on this cluster
$TimeSync = $true #script will enable/disable time sync on host on the VMs based on this value. Valid values are $true and $false

Before you run the script you will need to fill out the parameters in the variables section. $clustername is the name of the cluster on which you want to the change the settings for your VMs. All VMs running on the cluster will have their VMTools host time sync value set to what you specify in $TimeSync.

Functions

#FUNCTIONS
function ConnectToVcenter {
	param(
		$vcenter
	)
	#Load snap-in and connect to vCenter
	if (-not (Get-PSSnapin -Name VMware.VimAutomation.Core)) {
		Add-PSSnapin VMware.VimAutomation.Core
	}
	if ($global:DefaultVIServers.Count -lt 1) {
		Connect-VIServer $vCenter
	}
}
function Set-SyncValue {
	[CmdletBinding()]
	[OutputType([System.String])]
	param(
		[Parameter(ValueFromPipeline=$true)]$VM,
		[bool]$SyncValue
	)
	begin {
		Write-Debug "Creating configuration specification object"
		$ConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec 
		$ConfigSpec.tools = New-Object VMware.Vim.ToolsConfigInfo 
		$ConfigSpec.tools.syncTimeWithHost = $SyncValue
	}
	process {
		#configure the VM
		Write-Debug "Setting VMTools time sync to $SyncValue on $($VM.Name)"
		$View = get-view -viewtype virtualmachine -Filter @{'name'=$VM.Name} 
		$View.ReconfigVM_task($ConfigSpec)
		sleep 3 #wait for the configuration to complete
		#Check if the VM was configured as desired.
		$AfterView = get-view -viewtype virtualmachine -Filter @{'name'=$VM.Name} 
		[bool]$result = [System.Convert]::ToBoolean($AfterView.Config.Tools.syncTimeWithHost)		
		if ($result -eq $SyncValue) {
			Write-Debug "Successfully set the Tools.syncTimeWithHost on $VM to $SyncValue"
		}
		else {
			Write-Debug "Error - $VM - Couldn't set the Tools.syncTimeWithHost to the desired value of $SyncValue"
		}
	}
	end {
		Write-Debug "Finished setting time sync values on VMs."
	}
}

The script uses two functions which I will go over now.
1. Function ConnectToVcenter
This function takes one parameter $vcenter which is the name of the vcenter server. If not already done the function adds the VMWare snap-in for powershell and then connects to vcenter to make ready for the commands we will run after.
2. Function Set-SyncValue
Set-SyncValue takes two parameters $VM and $SyncValue. $VM can be passed from the pipeline which makes it easy to use in combination with other VMWare commands. The function works by first creating a configuration specification object with the value given in $SyncValue. After this it loops though the given VMs and reconfigures them accordingly. After the command to configure a VM has been sent, the script waits 3 seconds and then checks if the setting was updated to the value you wanted.

Script Main

#SCRIPT MAIN
ConnectToVcenter -vcenter $vcenter
Get-Cluster $clusterName | Get-VM | Set-SyncValue -SyncValue $TimeSync 

The script main is very short and simple. First I connect to vcenter using the function ConnectToVcenter. The actual work is then done in a one liner where I simply pipe all the VMs from the Get-Cluster and Get-VM cmdlets to the Set-SyncValue function.

That’s it. I hope you find the script useful. I have copied the full version of the script in below.

#####################################################################################################
##Script:			Set-VMTools_TimeSync.ps1
##
##Description:		enables/disables the VMTools time sync mode on all VMs in a given cluster.
##Created by:		Noam Wajnman
##Credits:			Part of this script was based on this article: 
#+					https://psvmware.wordpress.com/tag/disable-vmware-tools-time-sync-using-powercli/
##Creation Date:	April 28, 2014
#####################################################################################################
#VARIABLES
$DebugPreference = "continue" #comment out to disable debug info
#Parameters
$vcenter = "some_vcenter_server"
$clusterName = "Some_Cluster" #advanced settings will be set on all VMs on this cluster
$TimeSync = $true #script will enable/disable time sync on host on the VMs based on this value. Valid values are $true and $false
#FUNCTIONS
function ConnectToVcenter {
	param(
		$vcenter
	)
	#Load snap-in and connect to vCenter
	if (-not (Get-PSSnapin -Name VMware.VimAutomation.Core)) {
		Add-PSSnapin VMware.VimAutomation.Core
	}
	if ($global:DefaultVIServers.Count -lt 1) {
		Connect-VIServer $vCenter
	}
}
function Set-SyncValue {
	[CmdletBinding()]
	[OutputType([System.String])]
	param(
		[Parameter(ValueFromPipeline=$true)]$VM,
		[bool]$SyncValue
	)
	begin {
		Write-Debug "Creating configuration specification object"
		$ConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec 
		$ConfigSpec.tools = New-Object VMware.Vim.ToolsConfigInfo 
		$ConfigSpec.tools.syncTimeWithHost = $SyncValue
	}
	process {
		#configure the VM
		Write-Debug "Setting VMTools time sync to $SyncValue on $($VM.Name)"
		$View = get-view -viewtype virtualmachine -Filter @{'name'=$VM.Name} 
		$View.ReconfigVM_task($ConfigSpec)
		sleep 3 #wait for the configuration to complete
		#Check if the VM was configured as desired.
		$AfterView = get-view -viewtype virtualmachine -Filter @{'name'=$VM.Name} 
		[bool]$result = [System.Convert]::ToBoolean($AfterView.Config.Tools.syncTimeWithHost)		
		if ($result -eq $SyncValue) {
			Write-Debug "Successfully set the Tools.syncTimeWithHost on $VM to $SyncValue"
		}
		else {
			Write-Debug "Error - $VM - Couldn't set the Tools.syncTimeWithHost to the desired value of $SyncValue"
		}
	}
	end {
		Write-Debug "Finished setting time sync values on VMs."
	}
}
#SCRIPT MAIN
ConnectToVcenter -vcenter $vcenter
Get-Cluster $clusterName | Get-VM | Set-SyncValue -SyncValue $TimeSync

Powershell – Set storage LUN multipath policy on VMWare Hosts

Standard

Setting the multipath policy on ESXi hosts can be long process as it must be configured on every LUN on every host. If one has a lot of storage LUNs used by hosts/VMs this can quickly become a huge amount of tedious and repetitive work. However this script solves that problem and will let you change the configuration on all ESXi hosts and LUNs with ease.
This script will configure all fibre channel LUNs on a given ESXi host to use the value “fixed” or “round robin” depending on what you specify. The script can also be set to do this on all hosts in your HA cluster if needed.
The script is fairly small but I have divided it into three sections variables, functions and script main to simplify the structure.

Variables

#VARIABLES
#parameters
$vCenter = "your_vcenter_server"
$ESXi = "ESXi_FQDN" #use if configuring a single host
$clusterName = "Some_Cluster" #use if configuring all the hosts in a cluster
$MultiPathPolicy = "RoundRobin" #usable values are "Fixed" and "RoundRobin"

You need to fill out the parameters section before running the script
$Vcenter should be the name of your vcenter server
$ESXi should be the name of the ESXi host that you want to configure.
$ClusterName should be the name of the cluster you want configure. All LUNs on the hosts on this cluster will be set to the chosen multipath policy.
$MultiPathPolicy should be set to the value you want to configure on the host/cluster.

Functions

1. Function Set-ESXiMultiPathPolicy

function Set-ESXiMultiPathPolicy {
	param (
		$ESXi #FQDN of ESXi host
	)
	get-VMHost "$ESXi" | Get-VMHostHba -Type "FibreChannel" | Get-ScsiLun -LunType "disk" `
	| where {$_.MultipathPolicy -ne $MultiPathPolicy} | Set-ScsiLun -MultipathPolicy $MultiPathPolicy
}

The script only has this one function which takes one parameter $ESXi which is the ESXi host to configure the path policy on. It loops through all the LUNs on the given host and sets the multipath policy to the chosen value if not already configured like this.

Script Main

#SCRIPT MAIN
clear
#Load snap-in and connect to vCenter
if (-not (Get-PSSnapin -Name "VMware.VimAutomation.Core")) {
	Add-PSSnapin VMware.VimAutomation.Core
}
if ($global:DefaultVIServers.Count -lt 1) {
	Connect-VIServer $vCenter
}
#use this line to only configure one host ($ESXi)
Set-ESXiMultiPathPolicy -ESXi $ESXi

#Use this line to configure all the hosts in the cluster $ClusterName
#Get-Cluster $clusterName | Get-VMHost | % { Set-ESXiMultiPathPolicy -ESXi $_.Name}

The script main first checks if the PS Snap-in VMWare.VimAutomation.Core is loaded. If not the snap-in and commands are loaded. The script then connects to vcenter if it is not already connected. the last couple of lines in the script are the actual commands which run the functions and do the work. Only one of the lines should be run (the other must be commented out) depending on whether you want to configure a single host or all the hosts on a cluster.
That’s it. Enjoy!!
I have copied the full script in below.

################################################################################################
##Script:			Set-ESXiMultiPathPolicy.ps1
##
##Description:		Script configures the multipath policy for all LUNs on a given host or 
#+					cluster.
##Created by:		Noam Wajnman
##Creation Date:	February 2, 2013
##Updated:			March 25, 2014
################################################################################################
#FUNCTIONS
function Set-ESXiMultiPathPolicy {
	param (
		$ESXi #FQDN of ESXi host
	)
	get-VMHost "$ESXi" | Get-VMHostHba -Type "FibreChannel" | Get-ScsiLun -LunType "disk" `
	| where {$_.MultipathPolicy -ne $MultiPathPolicy} | Set-ScsiLun -MultipathPolicy $MultiPathPolicy
}
#VARIABLES
#parameters
$vCenter = "your_vcenter_server"
$ESXi = "ESXi_FQDN" #use if configuring a single host
$clusterName = "Some_Cluster" #use if configuring all the hosts in a cluster
$MultiPathPolicy = "RoundRobin" #usable values are "Fixed" and "RoundRobin"
#SCRIPT MAIN
clear
#Load snap-in and connect to vCenter
if (-not (Get-PSSnapin -Name "VMware.VimAutomation.Core")) {
	Add-PSSnapin VMware.VimAutomation.Core
}
if ($global:DefaultVIServers.Count -lt 1) {
	Connect-VIServer $vCenter
}
#use this line to only configure one host ($ESXi)
Set-ESXiMultiPathPolicy -ESXi $ESXi

#Use this line to configure all the hosts in the cluster $ClusterName
#Get-Cluster $clusterName | Get-VMHost | % { Set-ESXiMultiPathPolicy -ESXi $_.Name}