<#
.SYNOPSIS Datastore Functions
.DESCRIPTION A collection of functions to manipulate datastore Mount + Attach status
.EXAMPLE Get-Datastore | Get-DatastoreMountInfo | Sort Datastore, VMHost | FT -AutoSize
.EXAMPLE Get-Datastore IX2ISCSI01 | Unmount-Datastore
.EXAMPLE Get-Datastore IX2ISCSI01 | Get-DatastoreMountInfo | Sort Datastore, VMHost | FT -AutoSize
.EXAMPLE Get-Datastore IX2iSCSI01 | Mount-Datastore
.EXAMPLE Get-Datastore IX2iSCSI01 | Get-DatastoreMountInfo | Sort Datastore, VMHost | FT -AutoSize
.EXAMPLE Get-Datastore IX2iSCSI01 | Detach-Datastore
.EXAMPLE Get-Datastore IX2iSCSI01 | Get-DatastoreMountInfo | Sort Datastore, VMHost | FT -AutoSize
.EXAMPLE Get-Datastore IX2iSCSI01 | Attach-datastore
.EXAMPLE Get-Datastore IX2iSCSI01 | Get-DatastoreMountInfo | Sort Datastore, VMHost | FT -AutoSize
.NOTES Written by Alan Renouf, originally published at https://blogs.vmware.com/vsphere/2012/01/automating-datastore-storage-device-detachment-in-vsphere-5.html
.NOTES May 2017: Modified by Jason Coleman (virtuallyjason.blogspot.com), to improve performance when dealing with a large number of hosts and datastores
#>
Function Get-HostViews {
	[CmdletBinding()]
	Param (
		$Datastore
	)
	Begin{
		$allDatastores = @()
	}
	Process {
		$allDatastores += $Datastore
	}
	End {
		#Build the array of Datastore Objects
		if (-not $Datastore) {
			$allDatastores = Get-Datastore
		}
		$allDatastores = $allDatastores | ? {$_.pstypenames -contains "VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreImpl"}
		if (-not $allDatastores){
			Throw "No Datastores found.`nIs ""$Datastore"" a Datastore Object?"
		}
		$allHosts = @()
		$DShostsKeys = $allDatastores.extensiondata.host.key.value | sort-object | get-unique -asstring
		$DShosts = foreach ($thisKey in $DShostsKeys) {($allDatastores.extensiondata.host | ? {$_.key.value -eq $thisKey})[0]}
		$i = 1
		foreach ($DSHost in $DSHosts){
			write-progress -activity "Collecting ESXi Host Views" -status "Querying $($dshost.key)..." -percentComplete ($i++/$DSHosts.count*100)
			$hostObj = "" | select keyValue,hostView,storageSys
			$hostObj.hostView = get-view $DSHost.key
			$hostObj.keyValue = $DSHost.key.value
			$hostObj.storageSys = Get-View $hostObj.hostView.ConfigManager.StorageSystem
			$allHosts += $hostObj
		}
		write-progress -activity "Collecting ESXi Host Views" -completed
		$allHosts
	}
}

Function Get-DatastoreMountInfo {
	[CmdletBinding()]
	Param (
		[Parameter(ValueFromPipeline=$true)]
		$Datastore
	)
	#Roll back up an unrolled array from a pipeline
	Begin{
		$allDatastores = @()
	}
	Process {
		$allDatastores += $Datastore
	}
	End {
		$AllInfo = @()
		#Build the array of Datastore Objects
		if (-not $Datastore) {
			$allDatastores = Get-Datastore
		}
		$allDatastores = $allDatastores | ? {$_.pstypenames -contains "VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreImpl"}
		if (-not $allDatastores){
			Throw "No Datastores found.`nIs ""$Datastore"" a Datastore Object?"
		}
		$allDatastoreNAAs = foreach ($ds in $allDatastores) {$ds.ExtensionData.Info.vmfs.extent[0].diskname}

		#Build the array of custom Host Objects
		$allHosts = Get-HostViews -datastore $allDatastores
		$output = @()
		$i = 1
		foreach ($dsHost in $allHosts){
			write-progress -activity "Checking Datastore access" -status "Checking $($dshost.hostview.name)..." -percentComplete ($i++ / $allHosts.count * 100)
			#Get all devices on the host that match the list of $allDatastoreNAAs
			$devices = $dsHost.storagesys.StorageDeviceInfo.ScsiLun
			foreach ($device in $devices){
				if ($allDatastoreNAAs -contains $device.canonicalName){
					#Record information about this device/host combo
					$thisDatastore = $alldatastores | ? {$_.ExtensionData.Info.vmfs.extent[0].diskname -eq $device.canonicalName}
					$hostviewDSAttachState = ""
					if ($device.operationalState[0] -eq "ok") {
						$hostviewDSAttachState = "Attached"
					} elseif ($device.operationalState[0] -eq "off") {
						$hostviewDSAttachState = "Detached"
					} else {
						$hostviewDSAttachState = $device.operationalstate[0]
					}
					$Info = "" | Select Datastore, VMHost, Lun, Mounted, State
					$Info.VMHost = $dsHost.hostview.name
					$Info.Datastore = $thisDatastore.name
					$Info.Lun = $device.canonicalName
					$Info.mounted = ($thisDatastore.extensiondata.host | ? {$_.key.value -eq $dshost.keyvalue}).mountinfo.mounted
					$Info.state = $hostviewDSAttachState
					$output += $info
				}
			}
		}
		write-progress -activity "Checking Datastore access" -completed
		$output
	}
}

Function Detach-Datastore {
	[CmdletBinding()]
	Param (
		[Parameter(ValueFromPipeline=$true)]
		$Datastore
	)
	Begin{
		$allDatastores = @()
	}
	Process {
		$allDatastores += $Datastore
	}
	End {
		$allDatastores = $allDatastores | ? {$_.pstypenames -contains "VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreImpl"}
		if (-not $allDatastores){
			Throw "No Datastores found.`nIs ""$Datastore"" a Datastore Object?"
		}
		$allDatastoreNAAs = foreach ($ds in $allDatastores) {$ds.ExtensionData.Info.vmfs.extent[0].diskname}
		$allHosts = Get-HostViews -datastore $allDatastores
		$j = 1
		foreach ($dsHost in $allHosts){
			#Get all devices on the host that match the list of $allDatastoreNAAs
			write-progress -id 1 -activity "Detaching Datastores" -status "Removing device(s) from $($dsHost.hostview.name)" -percentComplete ($j++ / $allHosts.count * 100)
			$devices = $dsHost.storagesys.StorageDeviceInfo.ScsiLun | ? {$allDatastoreNAAs -contains $_.canonicalName}
			$i = 1
			foreach ($device in $devices){
				write-progress -parentid 1 -activity "Detaching Datastores" -status "Removing device: $(($allDatastores | ? {$_.ExtensionData.Info.vmfs.extent[0].diskname -eq $device.canonicalName}).name)" -percentComplete ($i++ / $allDatastoreNAAs.count * 100)
				$LunUUID = $Device.Uuid
				$dsHost.storageSys.DetachScsiLun($LunUUID);
			}
		}
		write-progress -activity "Detaching Datastores" -completed
	}
}

Function Attach-Datastore {
	[CmdletBinding()]
	Param (
		[Parameter(ValueFromPipeline=$true)]
		$Datastore
	)
	Begin{
		$allDatastores = @()
	}
	Process {
		$allDatastores += $Datastore
	}
	End {
		$allDatastores = $allDatastores | ? {$_.pstypenames -contains "VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreImpl"}
		if (-not $allDatastores){
			Throw "No Datastores found.`nIs ""$Datastore"" a Datastore Object?"
		}
		$allDatastoreNAAs = foreach ($ds in $allDatastores) {$ds.ExtensionData.Info.vmfs.extent[0].diskname}
		$allHosts = Get-HostViews -datastore $allDatastores
		$j = 1
		foreach ($dsHost in $allHosts){
			#Get all devices on the host that match the list of $allDatastoreNAAs
			write-progress -id 1 -activity "Attaching Datastores" -status "Attaching devices to $($dsHost.hostview.name)" -percentComplete ($j++ / $allHosts.count * 100)
			$devices = $dsHost.storagesys.StorageDeviceInfo.ScsiLun
			$i = 1
			foreach ($device in $devices){
				write-progress -parentid 1 -activity "Attaching Datastores" -status "Attaching device: $($Device.Uuid)" -percentComplete ($i++ / $devices.count * 100)
				if ($allDatastoreNAAs -contains $device.canonicalName){
					$LunUUID = $Device.Uuid
					$dsHost.storageSys.AttachScsiLun($LunUUID);
				}
			}
		}
		write-progress -activity "Attaching Datastores" -completed
	}
}

Function Unmount-Datastore {
	[CmdletBinding()]
	Param (
		[Parameter(ValueFromPipeline=$true)]
		$Datastore
	)
	Begin{
		$allDatastores = @()
	}
	Process {
		$allDatastores += $Datastore
	}
	End {
		$allDatastores = $allDatastores | ? {$_.pstypenames -contains "VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreImpl"}
		if (-not $allDatastores){
			Throw "No Datastores found.`nIs ""$Datastore"" a Datastore Object?"
		}
		$allHosts = Get-HostViews -datastore $allDatastores
		$j = 1
		foreach ($dsHost in $allHosts){
			write-progress -id 1 -activity "Unmounting Datastores" -status "Unmounting devices from $($dsHost.hostview.name)" -percentComplete ($j++ / $allHosts.count * 100)
			$i = 1
			foreach ($ds in $allDatastores){
				write-progress -parentid 1 -activity "Unmounting Datastores" -status "Unmounting device: $($ds.name)" -percentComplete ($i++ / $allDatastores.count * 100)
				$dsHost.storageSys.UnmountVmfsVolume($DS.ExtensionData.Info.vmfs.uuid);
			}
		}
		write-progress -activity "Unmounting Datastores" -completed
	}
}

Function Mount-Datastore {
	[CmdletBinding()]
	Param (
		[Parameter(ValueFromPipeline=$true)]
		$Datastore
	)
	Begin{
		$allDatastores = @()
	}
	Process {
		$allDatastores += $Datastore
	}
	End {
		$allDatastores = $allDatastores | ? {$_.pstypenames -contains "VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreImpl"}
		if (-not $allDatastores){
			Throw "No Datastores found.`nIs ""$Datastore"" a Datastore Object?"
		}
		$allHosts = Get-HostViews -datastore $allDatastores
		$j = 0
		foreach ($dsHost in $allHosts){
			write-progress -activity "Mounting Datastores" -status "Mounting devices to $($dsHost.hostview.name)" -percentComplete ($j++ / $allHosts.count * 100)
			$i = 1
			foreach ($ds in $allDatastores){
				write-progress -activity "Mounting Datastores" -status "Mounting device: $($DS.ExtensionData.Info.vmfs.uuid)" -percentComplete ($i++ / $allDatastores.count * 100)
				$dsHost.storageSys.MountVmfsVolume($DS.ExtensionData.Info.vmfs.uuid);
			}
		}
		write-progress -activity "Mounting Datastores" -completed
	}
}
