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

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}