Detaching Devices from a Cluster

When working with block storage we occasionally need to remove a storage device from the hosts it is connected to. When using the Web Client we can detach devices on a per-host basis, but there is nothing to detach from all its attached hosts.

What we do have is a helpful KB article on how to remove/detach a datastore – and a PowerCLI file that contains some functions. However the functions revolve around managing actions on datastores as opposed to devices that are connected and could be used for RDMs.

Most LUNs are masked to all the hosts in a cluster so that, whether the device is an RDM or has a VMFS datastore on it, VMs can run on any host in the cluster. To deal with this use case, I use a script that will detach a device from every host in a cluster. If the device does not exist on  a host then an error is displayed and the script moves on to the next device/host.

Before we can detach a device we need to ensure the following:

  • It is not used for a datastore.
  • It is not in use as a RDM.
  • The perennially reserved flag is not set for the device on the host.

The last point comes from the KB article above – if the flag is set (and it should be for any RDMs) then it could prevent the device from being unmounted successfully. So although clearing the flag is not required, clearing it ensures the best chance of success.

So, our script will start off with the following – we will accept a cluster to unmount/detach the device(s) from, an array of device identifiers, and a switch that will allow us to do checks without actually detaching anything.

	[Parameter(Mandatory=$true,ValueFromPipeline)][VMware.VimAutomation.ViCore.Impl.V1.Inventory.ClusterImpl] $Cluster,
	[Parameter(Mandatory=$true)][String[]] $LUNIdentifiers,
	[switch]$CheckUsage = $false

For the sake of time, we will make the assumption that all devices are masked to all hosts in the cluster – so when checking what the device is used for we will only check on the first host in the cluster. The following code will get the VMs in the cluster (for checking RDM usage in the next step) and the host that we will use for checking.

$vms = $Cluster | Get-VM;
$cluster_host = $Cluster | Get-VMHost | sort Name | select -First 1

$LUNs_To_Detach = @();
$LUNIdentifiers | % {
	$lunid = $_;
$lun = get-scsilun -VMHost $cluster_host | ? { $_.CanonicalName -eq $lunid }

Finally we will use the Get-ESXCLI cmdlet to check if the perennially reserved flag is set.

$myesxcli = get-esxcli -VMHost ESXhost

When all our checks are complete, we can loop through the hosts in the cluster and attempt to detach the LUN.

	$Cluster | get-vmhost | sort Name | % {
		write-host "Detaching LUNs from $($_.Name)" -Foreground Yellow
		$esx = $_;
		$storSys = Get-View $_.Extensiondata.ConfigManager.StorageSystem
		foreach ($lunid in $LUNs_To_Detach){				
			$lun = Get-ScsiLun -VmHost $esx | ? { $_.CanonicalName -eq $lunid }
			if ($lun -ne $null) {
				# Ensure the LUN is attached 
				if ($lun.ExtensionData.OperationalState -eq "ok") {
					write-host "Detaching LUN $lun" # from $($esx.Name)"
					write-host "Detach Complete" -Foreground Green
				} elseif ($lun.ExtensionData.OperationalState -eq "off") {
					Write-Host "LUN is already unmounted on this host." -Foreground Green;
				} else {
					Write-Host "OperationalState is $($lun.ExtensionData.OperationalState)";
			} else {
				Write-Host "Could not find LUN $lun on host $esx.Name";

The entire script is available from my github here. As always, feel free to fork and make pull requests if you find a mistake.