Cody.PureStorage.FlashArray.VMware.psm1

function get-faVolumeNameFromVvolUuid{
    <#
    .SYNOPSIS
      Connects to vCenter and FlashArray to return the FA volume that is a VVol virtual disk.
    .DESCRIPTION
      Takes in a VVol UUID to identify what volume it is on the FlashArray. If a VVol UUID is not specified it will ask you for a VM and then a VMDK and will find the UUID for you.
    .INPUTS
      FlashArray connection and VVol UUID.
    .OUTPUTS
      Returns volume name.
    .NOTES
      Version: 1.1
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 01/31/2019
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    #Import PowerCLI. Requires PowerCLI version 6.3 or later. Will fail here if PowerCLI cannot be installed
    #Will try to install PowerCLI with PowerShellGet if PowerCLI is not present.

    [CmdletBinding()]
    Param(
            [Parameter(Position=0)]
            [string]$purevip,

            [Parameter(Position=1,ValueFromPipeline=$True)]
            [Microsoft.PowerShell.Commands.WebRequestSession]$faSession,

            [Parameter(Position=2,mandatory=$true)]
            [string]$vvolUUID,

            [Parameter(Position=3,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray
    )
    if ($vvolUUID -eq "")
    {
        throw "You must enter a VVol UUID"
    }
    if ($purevip -ne "")
    {
        Write-Warning -Message "purevip will be deprecated in a future version. Please use new-pfaarray and pass in only the flasharray parameter." 
    }
    if ($null -ne $faSession)
    {
        Write-Warning -Message "faSession will be deprecated in a future version. Please use new-pfaarray and pass in only the flasharray parameter."
    }
    if (($null -eq $flasharray) -and ($purevip -eq ""))
    {
        throw "Please use new-pfaarray and pass in a FlashArray connection."
    }
    $ErrorActionPreference = "stop"
    if ($null -eq $faSession)
    {
        if ($null -eq $flasharray)
        {
            if ($global:faRestSession -ne "")
            {
                $faSession = $global:faRestSession
            }
            else {
                throw "Please pass in a FlashArray connection using new-pfaarray"
            }
        }
        else {
            $faSession = new-pureflasharrayRestSession -flasharray $flasharray 
            $purevip = $flasharray.EndPoint
        }
    }
    else {
        if ($null -ne $flasharray)
        {
            $purevip = $flasharray.EndPoint
        }
    }
   #Pull tags that match the volume with that UUID
   add-type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
    public bool CheckValidationResult(
        ServicePoint srvPoint, X509Certificate certificate,
        WebRequest request, int certificateProblem) {
        return true;
    }
}
"@

    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
    $volumeTags = Invoke-RestMethod -Method Get -Uri "https://$($purevip)/api/1.14/volume?tags=true&filter=value='${vvolUUID}'" -WebSession $faSession -ErrorAction Stop
    $volumeName = $volumeTags |where-object {$_.key -eq "PURE_VVOL_ID"}
    if ($null -ne $volumeName)
    {
        write-host
        return $volumeName.name
    }
    else {
        throw "The VVol was not found on this FlashArray" 
    }
}
function new-pureflasharrayRestSession {
     <#
    .SYNOPSIS
      Connects to FlashArray and creates a REST connection.
    .DESCRIPTION
      For operations that are in the FlashArray REST, but not in the Pure Storage PowerShell SDK yet, this provides a connection for invoke-restmethod to use.
    .INPUTS
      FlashArray connection or FlashArray IP/FQDN and credentials
    .OUTPUTS
      Returns REST session
    .NOTES
      Version: 1.1
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 01/31/2019
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>

    [CmdletBinding()]
    Param(
            
        [Parameter(Position=0)]
        [string]$purevip,
        
        [Parameter(Position=1,ValueFromPipeline=$True)]
        [System.Management.Automation.PSCredential]$faCreds,

        [Parameter(Position=2,ValueFromPipeline=$True)]
        [PurePowerShell.PureArray]$flasharray
    )
    if ($purevip -ne "")
    {
        Write-Warning -Message "purevip will be deprecated in a future version. Please pass in only the flasharray parameter."
    }
    if ($null -ne $faCreds)
    {
        Write-Warning -Message "faCreds will be deprecated in a future version. Please use new-pfaarray and pass in only the flasharray parameter."
    }
    if ($null -eq $flasharray)
    {
        if (($purevip -eq "") -or  ($null -eq $faCreds))
        {
            throw "Please use new-pfaarray and pass in the flasharray parameter. "
        }
    }
    #Connect to FlashArray
add-type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
    public bool CheckValidationResult(
        ServicePoint srvPoint, X509Certificate certificate,
        WebRequest request, int certificateProblem) {
        return true;
    }
}
"@

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
    
#Create FA session to obtain token
if ($null -eq $flasharray)
{
    $flasharray = New-PfaArray -EndPoint $purevip -Credentials $faCreds -IgnoreCertificateError
}
#Create FA REST session
    $SessionAction = @{
        api_token = $flasharray.ApiToken
    }
    Invoke-RestMethod -Method Post -Uri "https://$($flasharray.Endpoint)/api/$($flasharray.apiversion)/auth/session" -Body $SessionAction -SessionVariable Session -ErrorAction Stop |Out-Null
    $global:faRestSession = $Session
    return $global:faRestSession
}
function remove-pureflasharrayRestSession {
    <#
    .SYNOPSIS
      Disconnects a FlashArray REST session
    .DESCRIPTION
      Takes in a FlashArray Connection or session and disconnects on the FlashArray.
    .INPUTS
      FlashArray connection or session
    .OUTPUTS
      Returns success or failure.
    .NOTES
      Version: 1.1
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 01/31/2019
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0)]
            [string]$purevip,

            [Parameter(Position=1,ValueFromPipeline=$True)]
            [Microsoft.PowerShell.Commands.WebRequestSession]$faSession,

            [Parameter(Position=2,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray
    )
    if ($purevip -ne "")
    {
        Write-Warning -Message "purevip will be deprecated in a future version. Please pass in only the flasharray parameter."
    }
    if (($purevip -eq "") -and ($null -eq $flasharray))
    {
        throw "Please pass in the flasharray parameter."
    }
    if ($null -ne $flasharray)
    {
        $purevip = $flasharray.endpoint
        $apiVersion = $flasharray.ApiVersion
    }
    else {
        $apiVersion = 1.12
    }
    if ($null -eq $faSession)
    {
        $faSession = $global:faRestSession
        if ($null -eq $faSession)
        {
            throw "No REST session found. You must enter in a REST session to disconnect."
        }
    }
     #Delete FA session
    Invoke-RestMethod -Method Delete -Uri "https://${purevip}/api/${apiVersion}/auth/session"  -WebSession $faSession -ErrorAction Stop |Out-Null
}
function get-vmdkFromWindowsDisk {
    <#
    .SYNOPSIS
      Returns the VM disk object that corresponds to a given Windows file system
    .DESCRIPTION
      Takes in a drive letter and a VM object and returns a matching VMDK object
    .INPUTS
      VM, Drive Letter
    .OUTPUTS
      Returns VMDK object
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 08/24/2018
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$false,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]$vm,

            [Parameter(Position=1,mandatory=$false)]
            [string]$driveLetter
    )
    if ($null -eq $global:defaultviserver)
    {
       throw "There is no PowerCLI connection to a vCenter, please connect first with connect-viserver."
    }
    if ($null -eq $vm)
    {
        try {
            $vmName = Read-Host "Please enter in the name of your VM" 
            $vm = get-vm -name $vmName -ErrorAction Stop 
        }
        catch {
            throw $Global:Error[0]
        }
    }
    try {
        $guest = $vm |Get-VMGuest
    }
    catch {
        throw $Error[0]
    }
    if ($guest.State -ne "running")
    {
        throw "This VM does not have VM tools running"
    }
    if ($guest.GuestFamily -ne "windowsGuest")
    {
        throw "This is not a Windows VM--it is $($guest.OSFullName)"
    }
    try {
        $advSetting = Get-AdvancedSetting -Entity $vm -Name Disk.EnableUUID -ErrorAction Stop
    }
    catch {
        throw $Error[0]
    }
    if ($advSetting.value -eq "FALSE")
    {
        throw "The VM $($vm.name) has the advanced setting Disk.EnableUUID set to FALSE. This must be set to TRUE for this cmdlet to work."    
    }
    if (($null -eq $driveLetter) -or ($driveLetter -eq ""))
    {
        try {
            $driveLetter = Read-Host "Please enter in a drive letter" 
            if (($null -eq $driveLetter) -or ($driveLetter -eq ""))
            {
                throw "No drive letter entered"
            }
        }
        catch {
            throw $Global:Error[0]
        }
    }
    try {
        $VMdiskSerialNumber = $vm |Invoke-VMScript -ScriptText "get-partition -driveletter $($driveLetter) | get-disk | ConvertTo-CSV -NoTypeInformation"  -WarningAction silentlyContinue -ErrorAction Stop |ConvertFrom-Csv
    }
    catch {
            throw $Error[0]
        }
    if (![bool]($VMDiskSerialNumber.PSobject.Properties.name -match "serialnumber"))
    {
        throw ($VMdiskSerialNumber |Out-String) 
    }
    try {
        $vmDisk = $vm | Get-HardDisk |Where-Object {$_.ExtensionData.backing.uuid.replace("-","") -eq $VMdiskSerialNumber.SerialNumber}
    }
    catch {
        throw $Global:Error[0]
    }
    if ($null -ne $vmDisk)
    {
        return $vmDisk
    }
    else {
        throw "Could not match the VM disk to a VMware virtual disk"
    }
}
function new-faHostFromVmHost {
    <#
    .SYNOPSIS
      Create a FlashArray host from an ESXi vmhost object
    .DESCRIPTION
      Takes in a vCenter ESXi host and creates a FlashArray host
    .INPUTS
      FlashArray connection, a vCenter ESXi vmHost, and iSCSI/FC option
    .OUTPUTS
      Returns new FlashArray host object.
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 09/09/2018
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost]$esxi,

            [Parameter(Position=1,mandatory=$true)]
            [string]$protocolType,

            [Parameter(Position=2,mandatory=$true,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray
    )
    if (($protocolType -ne "FC") -and ($protocolType -ne "iSCSI"))
    {
        throw 'No valid protocol entered. Please make sure $protocolType is set to either "FC" or "iSCSI"'
    }
    if ($null -eq $global:defaultviserver)
    {
       throw "There is no PowerCLI connection to a vCenter, please connect first with connect-viserver."
    }
    if ($protocolType -eq "iSCSI")
    {
        $iscsiadapter = $esxi | Get-VMHostHBA -Type iscsi | Where-Object {$_.Model -eq "iSCSI Software Adapter"}
        if ($null -eq $iscsiadapter)
        {
            throw "No Software iSCSI adapter found on host $($esxi.NetworkInfo.HostName)."
        }
        else
        {
            $iqn = $iscsiadapter.ExtensionData.IScsiName
        }
        try
        {
            $newFaHost = New-PfaHost -Array $flasharray -Name $esxi.NetworkInfo.HostName -IqnList $iqn -ErrorAction stop
            return $newFaHost
        }
        catch
        {
            Write-Error $Global:Error[0]
            return 
        }
    }
    if ($protocolType -eq "FC")
    {
        $wwns = $esxi | Get-VMHostHBA -Type FibreChannel | Select-Object VMHost,Device,@{N="WWN";E={"{0:X}" -f $_.PortWorldWideName}} | Format-table -Property WWN -HideTableHeaders |out-string
        $wwns = (($wwns.Replace("`n","")).Replace("`r","")).Replace(" ","")
        $wwns = &{for ($i = 0;$i -lt $wwns.length;$i += 16)
        {
                $wwns.substring($i,16)
        }}
        try
        {
            $newFaHost = New-PfaHost -Array $flasharray -Name $esxi.NetworkInfo.HostName -WwnList $wwns -ErrorAction stop
            return $newFaHost
        }
        catch
        {
            Write-Error $Global:Error[0]
            return 
        }
    }
}
function get-faHostFromVmHost {
    <#
    .SYNOPSIS
      Gets a FlashArray host object from a ESXi vmhost object
    .DESCRIPTION
      Takes in a vmhost and returns a matching FA host if found
    .INPUTS
      FlashArray connection and a vCenter ESXi host
    .OUTPUTS
      Returns FA host if matching one is found.
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 09/09/2018
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
        [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost]$esxi,

        [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)]
        [PurePowerShell.PureArray]$flasharray
    )
    if ($null -eq $global:defaultviserver)
    {
       throw "There is no PowerCLI connection to a vCenter, please connect first with connect-viserver."
    }
    $iscsiadapter = $esxi | Get-VMHostHBA -Type iscsi | Where-Object {$_.Model -eq "iSCSI Software Adapter"}
    $wwns = $esxi | Get-VMHostHBA -Type FibreChannel | Select-Object VMHost,Device,@{N="WWN";E={"{0:X}" -f $_.PortWorldWideName}} | Format-table -Property WWN -HideTableHeaders |out-string
        $wwns = (($wwns.Replace("`n","")).Replace("`r","")).Replace(" ","")
        $wwns = &{for ($i = 0;$i -lt $wwns.length;$i += 16)
        {
                $wwns.substring($i,16)
        }}
    $fahosts = Get-PFAHosts -array $flasharray -ErrorAction Stop
    if ($null -ne $iscsiadapter)
    {
        $iqn = $iscsiadapter.ExtensionData.IScsiName
        foreach ($fahost in $fahosts)
        {
            if ($fahost.iqn.count -ge 1)
            {
                foreach ($fahostiqn in $fahost.iqn)
                {
                    if ($iqn.ToLower() -eq $fahostiqn.ToLower())
                    {
                        return $fahost
                    }
                }
            }
        }   
    }
    if ($null -ne $wwns)
    {
        foreach ($wwn in $wwns)
        {
            foreach ($fahost in $fahosts)
            {
                if ($fahost.wwn.count -ge 1)
                {
                    foreach($fahostwwn in $fahost.wwn)
                    {
                        if ($wwn.ToLower() -eq $fahostwwn.ToLower())
                        {
                            return $fahost
                        }
                    }
                }
            }
        }
    }
    else {
        throw "No matching host could be found on the FlashArray"
    }
}
function get-faHostGroupfromVcCluster {
    <#
    .SYNOPSIS
      Retrieves a FA host group from an ESXi cluster
    .DESCRIPTION
      Takes in a vCenter Cluster and retrieves corresonding host group
    .INPUTS
      FlashArray connection and a vCenter cluster
    .OUTPUTS
      Returns success or failure.
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 09/09/2018
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
        [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$cluster,

        [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)]
        [PurePowerShell.PureArray]$flasharray
    )
    if ($null -eq $global:defaultviserver)
    {
       throw "There is no PowerCLI connection to a vCenter, please connect first with connect-viserver."
    }
    $esxiHosts = $cluster |Get-VMHost
    $faHostGroups = @()
    $faHostGroupNames = @()
    foreach ($esxiHost in $esxiHosts)
    {
        try {
            $faHost = $esxiHost | get-faHostFromVmHost -flasharray $flasharray
            if ($null -ne $faHost.hgroup)
            {
                if ($faHostGroupNames.contains($faHost.hgroup))
                {
                    continue
                }
                else {
                     $faHostGroupNames += $faHost.hgroup
                     $faHostGroup = Get-PfaHostGroup -Array $flasharray -Name $faHost.hgroup
                     $faHostGroups += $faHostGroup
                }
            }
        }
        catch{
            continue
        }
    }
    if ($null -eq $faHostGroup)
    {
        throw "No host group found for this cluster"
    }
    if ($faHostGroups.count -gt 1)
    {
        Write-Warning -Message "This cluster spans more than one host group. The recommendation is to have only one host group per cluster"
    }
    return $faHostGroups
}
function new-faHostGroupfromVcCluster {
    <#
    .SYNOPSIS
      Create a host group from an ESXi cluster
    .DESCRIPTION
      Takes in a vCenter Cluster and creates hosts (if needed) and host group
    .INPUTS
      FlashArray connection, a vCenter cluster, and iSCSI/FC option
    .OUTPUTS
      Returns success or failure.
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 08/06/2018
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
        [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$cluster,
        
        [Parameter(Position=1,mandatory=$true)]
        [string]$protocolType,

        [Parameter(Position=2,mandatory=$true,ValueFromPipeline=$True)]
        [PurePowerShell.PureArray]$flasharray
    )
    if ($null -eq $global:defaultviserver)
    {
       throw "There is no PowerCLI connection to a vCenter, please connect first with connect-viserver."
    }
    if (($protocolType -ne "FC") -and ($protocolType -ne "iSCSI"))
    {
        throw 'No valid protocol entered. Please make sure $protocolType is set to either "FC" or "iSCSI"'
    }

    $hostGroup = $cluster |get-faHostGroupfromVcCluster -flasharray $flasharray -ErrorAction SilentlyContinue
    if ($hostGroup.count -gt 1)
    {
        throw "The cluster already is configured on the FlashArray and spans more than one host group. This cmdlet does not support a multi-hostgroup configuration."
    }
    if ($hostGroup.count -eq 1)
    {
        $clustername = $hostGroup.name
    }
    $esxiHosts = $cluster |Get-VMHost
    $faHosts = @()
    foreach ($esxiHost in $esxiHosts)
    {
        $faHost = $null
        try {
            $faHost = $esxiHost |get-faHostFromVmHost -flasharray $flasharray
        }
        catch {}
        if ($null -eq $faHost)
        {
            try {
                $faHost = $esxiHost |new-faHostFromVmHost -flasharray $flasharray -protocolType $protocolType -ErrorAction Stop
                $faHosts += $faHost
            }
            catch {
                Write-Error $Global:Error[0]
                throw "Could not create host. Cannot create host group." 
            }
            
        }
        else {
            $faHosts += $faHost
        }
    }
    #FlashArray only supports Alphanumeric or the dash - character in host group names. Checking for VMware cluster name compliance and removing invalid characters.
    if ($null -eq $hostGroup)
    {
        if ($cluster.Name -match "^[a-zA-Z0-9\-]+$")
        {
            $clustername = $cluster.Name
        }
        else
        {
            $clustername = $cluster.Name -replace "[^\w\-]", ""
            $clustername = $clustername -replace "[_]", ""
            $clustername = $clustername -replace " ", ""
        }
        $hg = Get-PfaHostGroup -Array $flasharray -Name $clustername -ErrorAction SilentlyContinue
        if ($null -ne $hg)
        {
            if ($hg.hosts.count -ne 0)
            {
                #if host group name is already in use and has only unexpected hosts i will create a new one with a random number at the end
                $nameRandom = get-random -Minimum 1000 -Maximum 9999
                $hostGroup = New-PfaHostGroup -Array $flasharray -Name "$($clustername)-$($nameRandom)" -ErrorAction stop
                $clustername = "$($clustername)-$($nameRandom)"
            }
        }
        else {
            #if there is no host group, it will be created
            $hostGroup = New-PfaHostGroup -Array $flasharray -Name $clustername -ErrorAction stop
        }
    }
    $faHostNames = @()
    foreach ($faHost in $faHosts)
    {
        if ($null -eq $faHost.hgroup)
        {
            $faHostNames += $faHost.name
        }
    }
    #any hosts that are not already in the host group will be added
    Add-PfaHosts -Array $flasharray -Name $clustername -HostsToAdd $faHostNames -ErrorAction Stop |Out-Null
    $fahostGroup = Get-PfaHostGroup -Array $flasharray -Name $clustername
    return $fahostGroup
}
function set-vmHostPureFaiSCSI{
    <#
    .SYNOPSIS
      Configure FlashArray iSCSI target information on ESXi host
    .DESCRIPTION
      Takes in an ESXi hosts and configures FlashArray iSCSI target info
    .INPUTS
      FlashArray connection and an ESXi host
    .OUTPUTS
      Returns ESXi iSCSI targets.
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 09/09/2018
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
        [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost]$esxi,

        [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)]
        [PurePowerShell.PureArray]$flasharray
    )
    if ($esxi.ExtensionData.Runtime.ConnectionState -ne "connected")
    {
        Write-Warning "Host $($esxi.NetworkInfo.HostName) is not in a connected state and cannot be configured."
        return
    }
    $ESXitargets = @()
    $faiSCSItargets = Get-PfaNetworkInterfaces -Array $flasharray |Where-Object {$_.services -eq "iscsi"}
    if ($null -eq $faiSCSItargets)
    {
        throw "The target FlashArray does not currently have any iSCSI targets configured."
    }
    $iscsi = $esxi |Get-VMHostStorage
    if ($iscsi.SoftwareIScsiEnabled -ne $true)
    {
        $esxi | get-vmhoststorage |Set-VMHostStorage -SoftwareIScsiEnabled $True |out-null
    }
    foreach ($faiSCSItarget in $faiSCSItargets)
    {
        $iscsiadapter = $esxi | Get-VMHostHba -Type iScsi | Where-Object {$_.Model -eq "iSCSI Software Adapter"}
        if (!(Get-IScsiHbaTarget -IScsiHba $iscsiadapter -Type Send -ErrorAction stop | Where-Object {$_.Address -cmatch $faiSCSItarget.address}))
        {
            New-IScsiHbaTarget -IScsiHba $iscsiadapter -Address $faiSCSItarget.address -ErrorAction stop 
        }
        $esxcli = $esxi |Get-esxcli -v2 
        $iscsiargs = $esxcli.iscsi.adapter.discovery.sendtarget.param.get.CreateArgs()
        $iscsiargs.adapter = $iscsiadapter.Device
        $iscsiargs.address = $faiSCSItarget.address
        $delayedAck = $esxcli.iscsi.adapter.discovery.sendtarget.param.get.invoke($iscsiargs) |where-object {$_.name -eq "DelayedAck"}
        $loginTimeout = $esxcli.iscsi.adapter.discovery.sendtarget.param.get.invoke($iscsiargs) |where-object {$_.name -eq "LoginTimeout"}
        if ($delayedAck.Current -eq "true")
        {
            $iscsiargs = $esxcli.iscsi.adapter.discovery.sendtarget.param.set.CreateArgs()
            $iscsiargs.adapter = $iscsiadapter.Device
            $iscsiargs.address = $faiSCSItarget.address
            $iscsiargs.value = "false"
            $iscsiargs.key = "DelayedAck"
            $esxcli.iscsi.adapter.discovery.sendtarget.param.set.invoke($iscsiargs) |out-null
        }
        if ($loginTimeout.Current -ne "30")
        {
            $iscsiargs = $esxcli.iscsi.adapter.discovery.sendtarget.param.set.CreateArgs()
            $iscsiargs.adapter = $iscsiadapter.Device
            $iscsiargs.address = $faiSCSItarget.address
            $iscsiargs.value = "30"
            $iscsiargs.key = "LoginTimeout"
            $esxcli.iscsi.adapter.discovery.sendtarget.param.set.invoke($iscsiargs) |out-null
        }
        $ESXitargets += Get-IScsiHbaTarget -IScsiHba $iscsiadapter -Type Send -ErrorAction stop | Where-Object {$_.Address -cmatch $faiSCSItarget.address}
    }
    return $ESXitargets
}
function set-clusterPureFAiSCSI {
    <#
    .SYNOPSIS
      Configure an ESXi cluster with FlashArray iSCSI information
    .DESCRIPTION
      Takes in a vCenter Cluster and configures iSCSI on each host.
    .INPUTS
      FlashArray connection and a vCenter cluster.
    .OUTPUTS
      Returns iSCSI targets.
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 09/09/2018
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
        [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$cluster,

        [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)]
        [PurePowerShell.PureArray]$flasharray
    )
    $esxihosts = $cluster |Get-VMHost
    $esxiiSCSItargets = @()
    $hostCount = 0
    foreach ($esxihost in $esxihosts)
    {
        if ($hostCount -eq 0)
        {
             Write-Progress -Activity "Configuring iSCSI" -status "Host: $esxihost" -percentComplete 0
        }
        else {
            Write-Progress -Activity "Configuring iSCSI" -status "Host: $esxihost" -percentComplete (($hostCount / $esxihosts.count) *100)
        }
        $esxiiSCSItargets += $esxihost | set-vmHostPureFaiSCSI -flasharray $flasharray
        $hostCount++
    }
    return $esxiiSCSItargets
}
function get-faVolfromVMFS {
    <#
    .SYNOPSIS
      Retrieves the FlashArray volume that hosts a VMFS datastore.
    .DESCRIPTION
      Takes in a VMFS datastore and one or more FlashArrays and returns the volume if found.
    .INPUTS
      FlashArray connection(s) and a VMFS datastore.
    .OUTPUTS
      Returns FlashArray volume or null if not found.
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 09/10/2018
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]$datastore,

            [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray
    )
    if ($datastore.Type -ne 'VMFS')
    {
        throw "This is not a VMFS datastore."
    }
    $lun = $datastore.ExtensionData.Info.Vmfs.Extent.DiskName |select-object -unique
    if ($lun -like 'naa.624a9370*')
    {
        $pureVolumes = Get-PfaVolumes -Array  $flasharray
        $volserial = ($lun.ToUpper()).substring(12)
        $purevol = $purevolumes | where-object { $_.serial -eq $volserial }
        if ($null -ne $purevol.name)
        {
            return $purevol
        }
        else {
            return $null
        }
    }
    else {
        throw "This VMFS is not hosted on FlashArray storage."
    }
    
}
function new-faVolVmfs {
    <#
    .SYNOPSIS
      Create a new VMFS on a new FlashArray volume
    .DESCRIPTION
      Creates a new FlashArray-based VMFS and presents it to a cluster.
    .INPUTS
      FlashArray connection, a vCenter cluster, a volume size, and name.
    .OUTPUTS
      Returns a VMFS object.
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 09/10/2018
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$cluster,

            [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray,

            [Parameter(Position=2,mandatory=$true)]
            [string]$volName,

            [Parameter(Position=3)]
            [int]$sizeInGB,

            [Parameter(Position=4)]
            [int]$sizeInTB
    )
    if (($sizeInGB -eq 0) -and ($sizeInTB -eq 0))
    {
        throw "Please enter a size in GB or TB"
    }
    elseif (($sizeInGB -ne 0) -and ($sizeInTB -ne 0)) {
        throw "Please only enter a size in TB or GB, not both."
    }
    elseif ($sizeInGB -ne 0) {
        $volSize = $sizeInGB * 1024 *1024 *1024   
    }
    else {
        $volSize = $sizeInTB * 1024 *1024 *1024 * 1024
    }
    try {
        $hostGroup = $cluster | get-faHostGroupfromVcCluster -flasharray $flasharray -ErrorAction Stop
    }
    catch {
        throw $Global:Error[0]
    }
    $newVol = New-PfaVolume -Array $flasharray -Size $volSize -VolumeName $volName -ErrorAction Stop
    New-PfaHostGroupVolumeConnection -Array $flasharray -VolumeName $newVol.name -HostGroupName $hostGroup.name |Out-Null
    $esxi = $cluster | get-vmhost | where-object {($_.version -like '5.5.*') -or ($_.version -like '6.*')}| where-object {($_.ConnectionState -eq 'Connected')} |Select-Object -last 1
    $cluster| Get-VMHost | Get-VMHostStorage -RescanAllHba |Out-Null
    $newNAA =  "naa.624a9370" + $newVol.serial.toLower()
    $ESXiApiVersion = $esxi.ExtensionData.Summary.Config.Product.ApiVersion
    try {
        if (($ESXiApiVersion -eq "5.5") -or ($ESXiApiVersion -eq "6.0") -or ($ESXiApiVersion -eq "5.1"))
        {
            $newVMFS = $esxi |new-datastore -name $newVol.name -vmfs -Path $newNAA -FileSystemVersion 5 -ErrorAction Stop
        }
        else
        {
            $newVMFS = $esxi |new-datastore -name $newVol.name -vmfs -Path $newNAA -FileSystemVersion 6 -ErrorAction Stop
        }
        return $newVMFS
    }
    catch {
        Write-Error $Global:Error[0]
        Remove-PfaHostGroupVolumeConnection -Array $flasharray -VolumeName $newVol.name -HostGroupName $hostGroup.name
        Remove-PfaVolumeOrSnapshot -Array $flasharray -Name $newVol.name 
        Remove-PfaVolumeOrSnapshot -Array $flasharray -Name $newVol.name -Eradicate
        return $null
    }
}
function add-faVolVmfsToCluster {
    <#
    .SYNOPSIS
      Add an existing FlashArray-based VMFS to another VMware cluster.
    .DESCRIPTION
      Takes in a vCenter Cluster and a datastore and the corresponding FlashArray
    .INPUTS
      FlashArray connection, a vCenter cluster, and a datastore
    .OUTPUTS
      Returns the FlashArray host group connection.
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 09/10/2018
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$cluster,

            [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray,

            [Parameter(Position=2,mandatory=$true,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]$datastore
    )
    try {
        $pureVol = $datastore | get-faVolfromVMFS -flasharray $flasharray -ErrorAction Stop
        $hostGroup = $cluster |get-faHostGroupfromVcCluster -flasharray $flasharray -ErrorAction Stop
        $faConnection = New-PfaHostGroupVolumeConnection -Array $flasharray -VolumeName $pureVol.name -HostGroupName $hostGroup.name -ErrorAction Stop
    }
    catch {
        Write-Error $Global:Error
        return $null
    }
    try {
        $cluster| Get-VMHost | Get-VMHostStorage -RescanAllHba -RescanVmfs -ErrorAction Stop |Out-Null
    }
    catch {
        Write-Error $Global:Error[0]
        Remove-PfaHostGroupVolumeConnection -Array $flasharray -VolumeName $pureVol.name -HostGroupName $hostGroup.name |Out-Null
        return $null
    }
    return $faConnection
}
function set-faVolVmfsCapacity {
    <#
    .SYNOPSIS
      Increase the size of a FlashArray-based VMFS datastore.
    .DESCRIPTION
      Takes in a datastore, the corresponding FlashArray, and a new size. Both the volume and the VMFS will be grown.
    .INPUTS
      FlashArray connection, a size, and a datastore
    .OUTPUTS
      Returns the datastore.
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 09/11/2018
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray,

            [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]$datastore,

            [Parameter(Position=2)]
            [int]$sizeInGB,

            [Parameter(Position=3)]
            [int]$sizeInTB
    )
    if (($sizeInGB -eq 0) -and ($sizeInTB -eq 0))
    {
        throw "Please enter a size in GB or TB"
    }
    elseif (($sizeInGB -ne 0) -and ($sizeInTB -ne 0)) {
        throw "Please only enter a size in TB or GB, not both."
    }
    elseif ($sizeInGB -ne 0) {
        $volSize = $sizeInGB * 1024 *1024 *1024   
    }
    else {
        $volSize = $sizeInTB * 1024 *1024 *1024 * 1024
    }
    if ($volSize -le $pureVol.size)
    {
        throw "The new size cannot be smaller than the existing size. VMFS volumes cannot be shrunk."
        return $null
    }
    try {
        $pureVol = $datastore | get-faVolfromVMFS -flasharray $flasharray -ErrorAction Stop
        Resize-PfaVolume -Array $flasharray -VolumeName $pureVol.name -NewSize $volSize |Out-Null
    }
    catch {
        Write-Error $Global:Error
        return $null
    }
    try {
        $datastore| Get-VMHost | Get-VMHostStorage -RescanAllHba -RescanVmfs -ErrorAction Stop |Out-Null
        $esxiView = Get-View -Id ($Datastore.ExtensionData.Host |Select-Object -last 1 | Select-Object -ExpandProperty Key)
        $datastoreSystem = Get-View -Id $esxiView.ConfigManager.DatastoreSystem
        $expandOptions = $datastoreSystem.QueryVmfsDatastoreExpandOptions($datastore.ExtensionData.MoRef)
        $expandedDS = $datastoreSystem.ExpandVmfsDatastore($datastore.ExtensionData.MoRef,$expandOptions[0].spec)
    }
    catch {
        Write-Error $Global:Error[0]
        return $null
    }
    return $expandedDS
}
function get-faVolVmfsSnapshots {
    <#
    .SYNOPSIS
      Retrieve all of the FlashArray snapshots of a given VMFS volume
    .DESCRIPTION
      Takes in a datastore and the corresponding FlashArray and returns any available snapshots.
    .INPUTS
      FlashArray connection and a datastore
    .OUTPUTS
      Returns any snapshots.
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 09/17/2018
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray,

            [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]$datastore
    )
    
    try {
        $pureVol = $datastore | get-faVolfromVMFS -flasharray $flasharray -ErrorAction Stop
        $volSnapshots = Get-PfaVolumeSnapshots -Array $flasharray -VolumeName $pureVol.name 
    }
    catch {
        Write-Error $Global:Error
        return $null
    }
    return $volSnapshots
}
function new-faVolVmfsSnapshot {
    <#
    .SYNOPSIS
      Creates a new FlashArray snapshot of a given VMFS volume
    .DESCRIPTION
      Takes in a datastore and the corresponding FlashArray and creates a snapshot.
    .INPUTS
      FlashArray connection and a datastore
    .OUTPUTS
      Returns created snapshot.
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 09/17/2018
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray,

            [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]$datastore,

            [Parameter(Position=2)]
            [string]$SnapName
    )
    
    try {
        $pureVol = $datastore | get-faVolfromVMFS -flasharray $flasharray -ErrorAction Stop
        $NewSnapshot = New-PfaVolumeSnapshots -Array $flasharray -Sources $pureVol.name -Suffix $SnapName
    }
    catch {
        throw $Global:Error
        return $null
    }
    return $NewSnapshot
}
function new-faVolVmfsFromSnapshot {
    <#
    .SYNOPSIS
      Mounts a copy of a VMFS datastore to a VMware cluster from a FlashArray snapshot.
    .DESCRIPTION
      Takes in a snapshot name, the corresponding FlashArray, and a cluster. The VMFS copy will be resignatured and mounted.
    .INPUTS
      FlashArray connection, a snapshotName, and a cluster.
    .OUTPUTS
      Returns the new datastore.
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 10/24/2018
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$cluster,

            [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray,

            [Parameter(Position=2,mandatory=$true)]
            [string]$snapName
    )
    try {
        $volumeName = $snapName.split(".")[0] + "-snap-" + (Get-Random -Minimum 1000 -Maximum 9999)
        $newVol =New-PfaVolume -Array $flasharray -Source $snapName -VolumeName $volumeName -ErrorAction Stop
    }
    catch {
        return $null
    }
    $hostGroup = $flasharray |get-faHostGroupfromVcCluster -cluster $cluster
    New-PfaHostGroupVolumeConnection -Array $flasharray -VolumeName $newVol.name -HostGroupName $hostGroup.name |Out-Null
    $esxi = $cluster | Get-VMHost| where-object {($_.ConnectionState -eq 'Connected')} |Select-Object -last 1 
    $esxi | Get-VMHostStorage -RescanAllHba -RescanVMFS -ErrorAction stop |Out-Null
    $hostStorage = get-view -ID $esxi.ExtensionData.ConfigManager.StorageSystem
    $resigVolumes= $hostStorage.QueryUnresolvedVmfsVolume()
    $newNAA =  "naa.624a9370" + $newVol.serial.toLower()
    $deleteVol = $false
    foreach ($resigVolume in $resigVolumes)
    {
        if ($deleteVol -eq $true)
        {
            break
        }
        foreach ($resigExtent in $resigVolume.Extent)
        {
            if ($resigExtent.Device.DiskName -eq $newNAA)
            {
                if ($resigVolume.ResolveStatus.Resolvable -eq $false)
                {
                    if ($resigVolume.ResolveStatus.MultipleCopies -eq $true)
                    {
                        write-host "The volume cannot be resignatured as more than one unresignatured copy is present. Deleting and ending." -BackgroundColor Red
                        write-host "The following volume(s) are presented and need to be removed/resignatured first:"
                        $resigVolume.Extent.Device.DiskName |where-object {$_ -ne $newNAA}
                    }
                    $deleteVol = $true
                    break
                }
                else {
                    $volToResignature = $resigVolume
                    break
                }
            }
        }
    }
    if (($null -eq $volToResignature) -and ($deleteVol -eq $false))
    {
        write-host "No unresolved volume found on the created volume. Deleting and ending." -BackgroundColor Red
        $deleteVol = $true
    }
    if ($deleteVol -eq $true)
    {
        Remove-PfaHostGroupVolumeConnection -Array $flasharray -VolumeName $newVol.name -HostGroupName $hostGroup.name |Out-Null
        Remove-PfaVolumeOrSnapshot -Array $flasharray -Name $newVol.name |Out-Null
        Remove-PfaVolumeOrSnapshot -Array $flasharray -Name $newVol.name -Eradicate |Out-Null
        return $null
    }
    $esxcli=get-esxcli -VMHost $esxi -v2 -ErrorAction stop
    $resigOp = $esxcli.storage.vmfs.snapshot.resignature.createargs()
    $resigOp.volumelabel = $volToResignature.VmfsLabel  
    $esxcli.storage.vmfs.snapshot.resignature.invoke($resigOp) |out-null
    Start-sleep -s 5
    $esxi |  Get-VMHostStorage -RescanVMFS -ErrorAction stop |Out-Null
    $datastores = $esxi| Get-Datastore -ErrorAction stop 
    foreach ($ds in $datastores)
    {
        $naa = $ds.ExtensionData.Info.Vmfs.Extent.DiskName
        if ($naa -eq $newNAA)
        {
            $resigds = $ds | Set-Datastore -Name $newVol.name -ErrorAction stop
            return $resigds
        }
    }    
}
function update-faVvolVmVolumeGroup {
    <#
    .SYNOPSIS
      Updates the volume group on a FlashArray for a VVol-based VM.
    .DESCRIPTION
      Takes in a VM and a FlashArray connection. A volume group will be created if it does not exist, if it does, the name will be updated if inaccurate. Any volumes for the given VM will be put into that group.
    .INPUTS
      FlashArray connection, a virtual machine.
    .OUTPUTS
      Returns the FlashArray volume names of the input VM.
    .NOTES
      Version: 1.1
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 02/01/2019
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine[]]$vm,

            [Parameter(Position=1)]
            [string]$purevip,
            
            [Parameter(Position=2)]
            [System.Management.Automation.PSCredential]$faCreds,

            [Parameter(Position=3,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]$datastore,

            [Parameter(Position=4,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray
    )
    Begin{
        if ($purevip -ne "")
        {
            Write-Warning -Message "purevip will be deprecated in a future version. Please pass in only the flasharray parameter."
        }
        if ($null -ne $faCreds)
        {
            Write-Warning -Message "faCreds will be deprecated in a future version. Please use new-pfaarray and pass in only the flasharray parameter."
        }
        $ErrorActionPreference = "stop"
    }
    Process{
        if ($null -eq $flasharray)
        {
            if (($global:faRestSession -ne "") -and ($null -ne $global:faRestSession))
            {
                $faSession = $global:faRestSession
            }
            if (($purevip -eq "") -or  ($null -eq $faCreds))
            {
                throw "Please use new-pfaarray and pass in the flasharray parameter. "
            }
            $flasharray = New-PfaArray -EndPoint $purevip -Credentials $faCreds -IgnoreCertificateError
        }
        else {
            $faSession = new-pureflasharrayRestSession -flasharray $flasharray
        }
        $volumeFinalNames = @()
        if ($null -ne $datastore)
        {
            if ($datastore.Type -ne "VVOL")
            {
                throw "This is not a VVol datastore"
            }
            if ($datastore.ExtensionData.Info.VvolDS.StorageArray[0].VendorId -ne "PURE") {
                throw "This is not a Pure Storage VVol datastore"
            }
            $vms = $datastore |get-vm
        }
        elseif ($null -ne $vm) {
            $vms = $vm
        }
        else{
            throw "You must enter in either a VM object or a VVol datastore"
        }
        foreach ($vm in $vms)
        {
            $configUUID = $vm.ExtensionData.Config.VmStorageObjectId
            if ($null -eq $configUUID)
            {
                write-warning -message  "The input VM $($vm.name) is not a VVol-based virtual machine. Skipping"
                continue
            }
            add-type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
    public bool CheckValidationResult(
        ServicePoint srvPoint, X509Certificate certificate,
        WebRequest request, int certificateProblem) {
        return true;
    }
}
"@

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
            $volumeConfig =  Invoke-RestMethod -Method Get -Uri "https://$($flasharray.Endpoint)/api/$($flasharray.apiversion)/volume?tags=true&filter=value='${configUUID}'" -WebSession $faSession -ErrorAction Stop
            $configVVolName = ($volumeConfig |where-object {$_.key -eq "PURE_VVOL_ID"}).name
            if ($null -eq $configVVolName)
            {
                write-warning -message "The VM $($vm.name) was not found on this FlashArray. Skipping."
                continue
            }
            if ($vm.Name -match "^[a-zA-Z0-9\-]+$")
            {
                $vmName = $vm.Name
            }
            else
            {
                $vmName = $vm.Name -replace "[^\w\-]", ""
                $vmName = $vmName -replace "[_]", ""
                $vmName = $vmName -replace " ", ""
            }
            $vGroupRand = '{0:X}' -f (get-random -Minimum 286331153 -max 4294967295)
            $newName = "vvol-$($vmName)-$($vGroupRand)-vg"
            if ([Regex]::Matches($configVVolName, "/").Count -eq 0)
            {
                $vGroup = New-PfaVolumeGroup -Array $flasharray -Name $newName
            }
            else {
                $vGroup = $configVVolName.split('/')[0]
                $vGroup = Invoke-RestMethod -Method Put -Uri "https://$($flasharray.Endpoint)/api/$($flasharray.apiversion)/vgroup/${vGroup}?name=${newName}" -WebSession $faSession -ErrorAction Stop
            }
            $vmId = $vm.ExtensionData.Config.InstanceUuid
            $volumesVmId = Invoke-RestMethod -Method Get -Uri "https://$($flasharray.Endpoint)/api/$($flasharray.apiversion)/volume?tags=true&filter=value='${vmId}'" -WebSession $faSession -ErrorAction Stop
            $volumeNames = $volumesVmId |where-object {$_.key -eq "VMW_VmID"}
            foreach ($volumeName in $volumeNames)
            {
                if ([Regex]::Matches($volumeName.name, "/").Count -eq 1)
                {
                    if ($newName -ne $volumeName.name.split('/')[0])
                    {
                        $volName= $volumeName.name.split('/')[1]
                        Add-PfaVolumeToContainer -Array $flasharray -Container $newName -Name $volName |Out-Null
                    }
                }
                else {
                    $volName= $volumeName.name
                    Add-PfaVolumeToContainer -Array $flasharray -Container $newName -Name $volName |Out-Null
                }
            }
            $volumesVmId = Invoke-RestMethod -Method Get -Uri "https://$($flasharray.Endpoint)/api/$($flasharray.apiversion)/volume?tags=true&filter=value='${vmId}'" -WebSession $faSession -ErrorAction Stop
            $volumeFinalNames += $volumesVmId |where-object {$_.key -eq "VMW_VmID"}
        }
    }
    End{
        remove-pureflasharrayRestSession -flasharray $flasharray -faSession $faSession |Out-Null
        return $volumeFinalNames.name
    }
}
function get-vvolUuidFromHardDisk {
    <#
    .SYNOPSIS
      Gets the VVol UUID of a virtual disk
    .DESCRIPTION
      Takes in a virtual disk object
    .INPUTS
      Virtual disk object (get-harddisk).
    .OUTPUTS
      Returns the VVol UUID.
    .NOTES
      Version: 1.1
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 02/01/2019
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$True,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$vmdk
    )
    Begin {
    }
    Process {
        if ($vmdk.ExtensionData.Backing.backingObjectId -eq "")
        {
            throw "This is not a VVol-based hard disk."
        }
        if ((($vmdk |Get-Datastore).ExtensionData.Info.vvolDS.storageArray.vendorId) -ne "PURE") {
            throw "This is not a Pure Storage FlashArray VVol disk"
        }
       $uuid = $vmdk.ExtensionData.Backing.backingObjectId
    }
    End {
        return $uuid
    }

}
function get-faSnapshotsFromVvolHardDisk {
    <#
    .SYNOPSIS
      Returns all of the FlashArray snapshot names of a given hard disk
    .DESCRIPTION
      Takes in a virtual disk object
    .INPUTS
      Virtual disk object (get-harddisk).
    .OUTPUTS
      Returns all specified snapshot names.
    .NOTES
      Version: 1.1
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 02/01/2019
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(      
            [Parameter(Position=0)]
            [string]$purevip,
        
            [Parameter(Position=1,ValueFromPipeline=$True)]
            [System.Management.Automation.PSCredential]$faCreds,

            [Parameter(Position=2,ValueFromPipeline=$True)]
            [Microsoft.PowerShell.Commands.WebRequestSession]$faSession,

            [Parameter(Position=3,mandatory=$True,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$vmdk,

            [Parameter(Position=4,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray
    )
    Begin{
        if ($purevip -ne "")
        {
            Write-Warning -Message "purevip will be deprecated in a future version. Please pass in only the flasharray parameter."
        }
        if ($null -ne $faCreds)
        {
            Write-Warning -Message "faCreds will be deprecated in a future version. Please use new-pfaarray and pass in only the flasharray parameter."
        }
        if ($null -eq $flasharray)
        {
            if (($purevip -eq "") -or  ($null -eq $faCreds))
            {
                throw "Please use new-pfaarray and pass in the flasharray parameter. "
            }
        }
        if ($null -eq $faSession)
        {
            if ($null -eq $flasharray)
            {
                if ($global:faRestSession -ne "")
                {
                    $faSession = $global:faRestSession
                }
                else {
                    throw "Please pass in a FlashArray connection using new-pfaarray"
                }
            }
            else {
                $faSession = new-pureflasharrayRestSession -flasharray $flasharray 
                $purevip = $flasharray.EndPoint
            }
        }
        else {
            if ($null -ne $flasharray)
            {
                $purevip = $flasharray.EndPoint
            }
        }
   }
   Process {
        $vvolUuid = get-vvolUuidFromHardDisk -vmdk $vmdk
        if ($null -eq $flasharray)
        {
            $faVolume = get-faVolumeNameFromVvolUuid -faSession $faSession -purevip $purevip -vvolUUID $vvolUuid 
            $volumeSnaps = Invoke-RestMethod -Method Get -Uri "https://${purevip}/api/1.13/volume/${faVolume}?snap=true" -WebSession $faSession -ErrorAction Stop


        }
        else
        {
            $faVolume = get-faVolumeNameFromVvolUuid -flasharray $flasharray -vvolUUID $vvolUuid 
            $volumeSnaps = Invoke-RestMethod -Method Get -Uri "https://${purevip}/api/$($flasharray.apiversion)/volume/${faVolume}?snap=true" -WebSession $faSession -ErrorAction Stop
        }
        $snapNames = @()
        foreach ($volumeSnap in $volumeSnaps)
        {
            $snapNames += $volumeSnap.name 
        }
   }
   End {
        return $snapNames
   }
  
}
function copy-faVvolVmdkToNewVvolVmdk {
    <#
    .SYNOPSIS
      Takes an existing VVol-based virtual disk and creates a new VVol virtual disk from it.
    .DESCRIPTION
      Takes in a hard disk and creates a copy of it to a certain VM.
    .INPUTS
      FlashArray connection information, a virtual machine, and a virtual disk.
    .OUTPUTS
      Returns the new hard disk.
    .NOTES
      Version: 1.1
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 02/01/2019
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$True,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]$targetVm,

            [Parameter(Position=1,mandatory=$True,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$vmdk,

            [Parameter(Position=2)]
            [string]$purevip,
            
            [Parameter(Position=3,ValueFromPipeline=$True)]
            [System.Management.Automation.PSCredential]$faCreds,

            [Parameter(Position=4,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray
    )
    Begin {
        if ($purevip -ne "")
        {
            Write-Warning -Message "purevip will be deprecated in a future version. Please pass in only the flasharray parameter."
        }
        if ($null -ne $faCreds)
        {
            Write-Warning -Message "faCreds will be deprecated in a future version. Please use new-pfaarray and pass in only the flasharray parameter."
        }
        if ($null -eq $flasharray)
        {
            if (($purevip -eq "") -or  ($null -eq $faCreds))
            {
                throw "Please use new-pfaarray and pass in the flasharray parameter. "
            }
        }
        $ErrorActionPreference = "stop"
    }
    Process {
        if ($null -eq $flasharray)
        {
            $flasharray = New-PfaArray -EndPoint $purevip -Credentials $faCreds -IgnoreCertificateError
        }
        $vvolUuid = get-vvolUuidFromHardDisk -vmdk $vmdk 
        $faVolume = get-faVolumeNameFromVvolUuid -flasharray $flasharray -vvolUUID $vvolUuid 
        $datastore = $vmdk | Get-Datastore 
        $newHardDisk = New-HardDisk -Datastore $datastore -CapacityGB $vmdk.CapacityGB -VM $targetVm 
        $newVvolUuid = get-vvolUuidFromHardDisk -vmdk $newHardDisk 
        $newFaVolume = get-faVolumeNameFromVvolUuid -flasharray $flasharray -vvolUUID $newVvolUuid 
        New-PfaVolume -Array $flasharray -Source $faVolume -Overwrite -VolumeName $newFaVolume  |Out-Null
    }
    End {
        return $newHardDisk
    }    
}
function copy-faSnapshotToExistingVvolVmdk {
    <#
    .SYNOPSIS
      Takes an snapshot and creates a new VVol virtual disk from it.
    .DESCRIPTION
      Takes in a hard disk and creates a copy of it to a certain VM.
    .INPUTS
      FlashArray connection information, a virtual machine, and a virtual disk.
    .OUTPUTS
      Returns the new hard disk.
    .NOTES
      Version: 1.1
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 02/01/2019
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$true)]
            [string]$snapshotName,

            [Parameter(Position=1,mandatory=$True,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$vmdk,

            [Parameter(Position=2)]
            [string]$purevip,
            
            [Parameter(Position=3,ValueFromPipeline=$True)]
            [System.Management.Automation.PSCredential]$faCreds,

            [Parameter(Position=4,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray
    )
    Begin {
        if ($purevip -ne "")
        {
            Write-Warning -Message "purevip will be deprecated in a future version. Please pass in only the flasharray parameter."
        }
        if ($null -ne $faCreds)
        {
            Write-Warning -Message "faCreds will be deprecated in a future version. Please use new-pfaarray and pass in only the flasharray parameter."
        }
        if ($null -eq $flasharray)
        {
            if (($purevip -eq "") -or  ($null -eq $faCreds))
            {
                throw "Please use new-pfaarray and pass in the flasharray parameter. "
            }
        }
        $ErrorActionPreference = "stop"
    }
    Process {
        if ($null -eq $flasharray)
        {
            $flasharray = New-PfaArray -EndPoint $purevip -Credentials $faCreds -IgnoreCertificateError
        }
        $vvolUuid = get-vvolUuidFromHardDisk -vmdk $vmdk 
        $faVolume = get-faVolumeNameFromVvolUuid -flasharray $flasharray -vvolUUID $vvolUuid 
        $datastore = $vmdk | Get-Datastore 
        $arrayID = (Get-PfaArrayAttributes -Array $flasharray).id
        if ($datastore.ExtensionData.info.vvolDS.storageArray[0].uuid.substring(16) -eq $arrayID)
        {
            $snapshotSize = Get-PfaSnapshotSpaceMetrics -Array $flasharray -Name $snapshotName
            if ($vmdk.ExtensionData.capacityinBytes -eq $snapshotSize.size)
            {
                New-PfaVolume -Array $flasharray -Source $snapshotName -Overwrite -VolumeName $faVolume  |Out-Null
            }
            elseif ($vmdk.ExtensionData.capacityinBytes -lt $snapshotSize.size) {
                $vmdk = Set-HardDisk -HardDisk $vmdk -CapacityKB ($snapshotSize.size / 1024) -Confirm:$false 
                $vmdk = New-PfaVolume -Array $flasharray -Source $snapshotName -Overwrite -VolumeName $faVolume 
                
            }
            else {
                throw "The target VVol hard disk is larger than the snapshot size and VMware does not allow hard disk shrinking."
            } 
        }
        else {
            throw "The snapshot and target VVol VMDK are not on the same array."
        }
    }
    End {
        return $vmdk
    }
        
}
function copy-faSnapshotToNewVvolVmdk {
    <#
    .SYNOPSIS
      Takes an snapshot and overwrites an existing VVol virtual disk from it.
    .DESCRIPTION
      Takes an snapshot and overwrites an existing VVol virtual disk from it.
    .INPUTS
      FlashArray connection information, a source snapshot, and a virtual disk.
    .OUTPUTS
      Returns the hard disk.
    .NOTES
      Version: 1.1
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 02/01/2019
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$true)]
            [string]$snapshotName,

            [Parameter(Position=1,mandatory=$True,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]$targetVm,

            [Parameter(Position=2)]
            [string]$purevip,
            
            [Parameter(Position=3,ValueFromPipeline=$True)]
            [System.Management.Automation.PSCredential]$faCreds,

            [Parameter(Position=4,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray
    )
    Begin {
        if ($purevip -ne "")
        {
            Write-Warning -Message "purevip will be deprecated in a future version. Please pass in only the flasharray parameter."
        }
        if ($null -ne $faCreds)
        {
            Write-Warning -Message "faCreds will be deprecated in a future version. Please use new-pfaarray and pass in only the flasharray parameter."
        }
        if ($null -eq $flasharray)
        {
            if (($purevip -eq "") -or  ($null -eq $faCreds))
            {
                throw "Please use new-pfaarray and pass in the flasharray parameter. "
            }
        }
        $ErrorActionPreference = "stop"
    }
    Process{
        if ($null -eq $flasharray)
        {
            $flasharray = New-PfaArray -EndPoint $purevip -Credentials $faCreds -IgnoreCertificateError 
        }
        $arrayID = (Get-PfaArrayAttributes -Array $flasharray).id
        $datastore = $targetVm| Get-VMHost | Get-Datastore |where-object {$_.Type -eq "VVOL"} |Where-Object {$_.ExtensionData.info.vvolDS.storageArray[0].uuid.substring(16) -eq $arrayID} |Select-Object -First 1
        $snapshotSize = Get-PfaSnapshotSpaceMetrics -Array $flasharray -Name $snapshotName
        $newHardDisk = New-HardDisk -Datastore $datastore -CapacityKB ($snapshotSize.size / 1024 ) -VM $targetVm 
        $newVvolUuid = get-vvolUuidFromHardDisk -vmdk $newHardDisk 
        $newFaVolume = get-faVolumeNameFromVvolUuid -flasharray $flasharray -vvolUUID $newVvolUuid 
        New-PfaVolume -Array $flasharray -Source $snapshotName -Overwrite -VolumeName $newFaVolume  |Out-Null
    }
    End{
        return $newHardDisk 
    }          
}
function copy-faVvolVmdkToExistingVvolVmdk {
    <#
    .SYNOPSIS
      Takes an virtual disk and refreshes an existing VVol virtual disk from it.
    .DESCRIPTION
      Takes an virtual disk and refreshes an existing VVol virtual disk from it.
    .INPUTS
      FlashArray connection information, a source and target virtual disk.
    .OUTPUTS
      Returns the new hard disk.
    .NOTES
      Version: 1.1
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 02/01/2019
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$True,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$sourceVmdk,

            [Parameter(Position=1,mandatory=$True,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$targetVmdk,

            [Parameter(Position=2)]
            [string]$purevip,
            
            [Parameter(Position=3,ValueFromPipeline=$True)]
            [System.Management.Automation.PSCredential]$faCreds,

            [Parameter(Position=4,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray
    )
    Begin {
        if ($purevip -ne "")
        {
            Write-Warning -Message "purevip will be deprecated in a future version. Please pass in only the flasharray parameter."
        }
        if ($null -ne $faCreds)
        {
            Write-Warning -Message "faCreds will be deprecated in a future version. Please use new-pfaarray and pass in only the flasharray parameter."
        }
        if ($null -eq $flasharray)
        {
            if (($purevip -eq "") -or  ($null -eq $faCreds))
            {
                throw "Please use new-pfaarray and pass in the flasharray parameter. "
            }
        }
        $ErrorActionPreference = "stop"
    }
    Process {
        $targetDatastore = $targetVmdk | Get-Datastore 
        $sourceDatastore = $sourceVmdk | Get-Datastore 
        if ($sourceDatastore.ExtensionData.info.vvolDS.storageArray[0].uuid -eq $targetDatastore.ExtensionData.info.vvolDS.storageArray[0].uuid)
        {
            if ($null -eq $flasharray)
            {
                $flasharray = New-PfaArray -EndPoint $purevip -Credentials $faCreds -IgnoreCertificateError
            }
            $vvolUuid = get-vvolUuidFromHardDisk -vmdk $sourceVmdk 
            $sourceFaVolume = get-faVolumeNameFromVvolUuid -flasharray $flasharray -vvolUUID $vvolUuid 
            $vvolUuid = get-vvolUuidFromHardDisk -vmdk $targetVmdk 
            $targetFaVolume = get-faVolumeNameFromVvolUuid -flasharray $flasharray -vvolUUID $vvolUuid
            if ($targetVmdk.CapacityKB -eq $sourceVmdk.CapacityKB)
            {
                New-PfaVolume -Array $flasharray -Source $sourceFaVolume -Overwrite -VolumeName $targetFaVolume |Out-Null
            }
            elseif ($targetVmdk.CapacityKB -lt $sourceVmdk.CapacityKB) {
                $targetVmdk = Set-HardDisk -HardDisk $targetVmdk -CapacityKB $sourceVmdk.CapacityKB -Confirm:$false 
                New-PfaVolume -Array $flasharray -Source $sourceFaVolume -Overwrite -VolumeName $targetFaVolume  |Out-Null     
            }
            else {
                throw "The target VVol hard disk is larger than the snapshot size and VMware does not allow hard disk shrinking."
            }
        }
        else {
            throw "The snapshot and target VVol VMDK are not on the same array."
        }
    }
    End{
        return $targetVmdk
    }    
}
function new-faSnapshotOfVvolVmdk {
    <#
    .SYNOPSIS
      Takes a VVol virtual disk and creates a FlashArray snapshot.
    .DESCRIPTION
      Takes a VVol virtual disk and creates a snapshot of it.
    .INPUTS
      FlashArray connection information and a virtual disk.
    .OUTPUTS
      Returns the snapshot name.
    .NOTES
      Version: 1.1
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 02/01/2019
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$True,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$vmdk,

            [Parameter(Position=1)]
            [string]$purevip,
            
            [Parameter(Position=2,ValueFromPipeline=$True)]
            [System.Management.Automation.PSCredential]$faCreds,

            [Parameter(Position=3,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray
    )
    Begin {
        if ($purevip -ne "")
        {
            Write-Warning -Message "purevip will be deprecated in a future version. Please pass in only the flasharray parameter."
        }
        if ($null -ne $faCreds)
        {
            Write-Warning -Message "faCreds will be deprecated in a future version. Please use new-pfaarray and pass in only the flasharray parameter."
        }
        if ($null -eq $flasharray)
        {
            if (($purevip -eq "") -or  ($null -eq $faCreds))
            {
                throw "Please use new-pfaarray and pass in the flasharray parameter. "
            }
        }
        $ErrorActionPreference = "stop"
    }
    Process {
        if ($null -eq $flasharray)
        {
            $flasharray = New-PfaArray -EndPoint $purevip -Credentials $faCreds -IgnoreCertificateError
        }
        $vvolUuid = get-vvolUuidFromHardDisk -vmdk $vmdk -ErrorAction Stop
        $faVolume = get-faVolumeNameFromVvolUuid -flasharray $flasharray -vvolUUID $vvolUuid -ErrorAction Stop
        $snapshot = New-PfaVolumeSnapshots -Array $flasharray -Sources $faVolume -ErrorAction Stop
    }
    End {
        return $snapshot.name
    } 
}
function new-faVolRdm {
    <#
    .SYNOPSIS
      Creates a new Raw Device Mapping for a VM
    .DESCRIPTION
      Creates a new volume on a FlashArray and presents it to a VM as a RDM. Optionally can also be created from a snapshot
    .INPUTS
      FlashArray connection, volume name, capacity, virtual machine, SCSI adapter.
    .OUTPUTS
      FlashArray volume name
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 02/27/2019
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray,
            
            [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]$vm,

            [Parameter(Position=2)]
            [string]$volName,

            [Parameter(Position=3)]
            [int]$sizeInGB,

            [Parameter(Position=4)]
            [int]$sizeInTB,

            [Parameter(Position=5,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]$datastore,

            [Parameter(Position=6,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.ScsiController]$scsiController,

            [Parameter(Position=7)]
            [string]$snapshotName
    )
    Begin{
        if ($snapshotName -eq "")
        {
            if (($sizeInGB -eq 0) -and ($sizeInTB -eq 0))
            {
                throw "Please enter a size in GB or TB"
            }
            elseif (($sizeInGB -ne 0) -and ($sizeInTB -ne 0)) {
                throw "Please only enter a size in TB or GB, not both."
            }
            elseif ($sizeInGB -ne 0) {
                $volSize = $sizeInGB * 1024 *1024 *1024   
            }
            else {
                $volSize = $sizeInTB * 1024 *1024 *1024 * 1024
            }
        }
        elseif (($snapshotName -ne "") -and (($sizeInGB -ne 0) -or ($sizeInTB -ne 0))) 
        {
            throw "Please enter a snapshot or a size, not both."
        }
    }
    Process{
        $ErrorActionPreference = "stop"
        if ($volName -eq "")
        {
            $rand = get-random -Maximum 9999 -Minimum 1000
            $volName = "$($vm.Name)-rdm$($rand)"
            if ($volName -notmatch "^[a-zA-Z0-9\-]+$")
            {
                $volName = $volName -replace "[^\w\-]", ""
                $volName = $volName -replace "[_]", ""
                $volName = $volName -replace " ", ""
            }
        }
        if ($null -eq $datastore)
        {
            $vmDatastore = Get-Datastore -vm $vm
            if ($vmDatastore.count -gt 1)
            {
                $datastore = get-datastore (($vm.ExtensionData.Layoutex.file |where-object {$_.name -like "*.vmx*"}).name.split("]")[0].substring(1))
            }
            else {
                $datastore = $vmDatastore[0]
            }
        }
        if ($datastore.type -eq "VVOL")
        {
            throw "The datastore cannot be of type VVols as RDM pointer files are not supported on that type of datastore. Please specify a VMFS or NFS datastore"
        }
        $cluster = $vm | get-cluster
        if ($null -eq $cluster)
        {
            throw "This VM is not on a host in a cluster. Non-clustered hosts are not supported by this script."     
        }
        $hostGroup = $cluster | get-faHostGroupfromVcCluster -flasharray $flasharray -ErrorAction Stop
        if ($snapshotName -ne "")
        {
            $newVol = New-PfaVolume -Array $flasharray -VolumeName $volName -Source $snapshotName -ErrorAction Stop
        }
        else {
            $newVol = New-PfaVolume -Array $flasharray -Size $volSize -VolumeName $volName -ErrorAction Stop
        }
        New-PfaHostGroupVolumeConnection -Array $flasharray -VolumeName $newVol.name -HostGroupName $hostGroup.name |Out-Null
        $esxiHosts = $cluster| Get-VMHost 
        foreach ($esxiHost in $esxiHosts)
        {
            $esxi = $esxiHost.ExtensionData
            $storageSystem = Get-View -Id $esxi.ConfigManager.StorageSystem
            $hbas = ($esxihost |Get-VMHostHba |where-object {$_.Type -eq "FibreChannel" -or $_.Type -eq "iSCSI"}).device
            foreach ($hba in $hbas) {
                $storageSystem.rescanHba($hba)
            }
        }
        $newNAA =  "naa.624a9370" + $newVol.serial.toLower()
        if($null -eq $scsiController)
        {
            $controller = $vm |Get-ScsiController 
            if ($controller.count -gt 1)
            {
                $pvSCSIs = $vm |Get-ScsiController |Where-Object {$_.Type -eq "ParaVirtual"}
                if ($pvSCSIs.count -gt 1)
                {
                    $pvDisksHigh = 1000
                    foreach ($pvSCSI in $pvSCSIs)
                    {
                       $pvDisks = ($vm | Get-HardDisk | Where-Object {$_.ExtensionData.ControllerKey -eq $pvSCSI.key}).count
                       if ($pvDisksHigh -ge $pvDisks)
                       {
                            $pvDisksHigh = $pvDisks
                            $controller = $pvSCSI
                       }
                    }
                }
                elseif ($pvSCSIs.count -eq 1)
                {
                    $controller = $pvSCSIs
                }
                else 
                {
                    $lsiSCSIs = $vm |Get-ScsiController 
                    if ($lsiSCSIs.count -gt 1)
                    {
                        $lsiDisksHigh = 1000
                        foreach ($lsiSCSI in $lsiSCSIs)
                        {
                            $lsiDisks = ($vm | Get-HardDisk | Where-Object {$_.ExtensionData.ControllerKey -eq $lsiSCSI.key}).count
                            if ($lsiDisksHigh -ge $lsiDisks)
                            {
                                $lsiDisksHigh = $lsiDisks
                                $controller = $lsiSCSI
                            }
                        }
                    }
                    else
                    {
                        $controller = $lsiSCSIs
                    }
                }
            }   
        } 
        else {
            $controller = $scsiController
        }
        try {
            $newRDM = $vm | new-harddisk -DeviceName "/vmfs/devices/disks/$($newNAA)" -DiskType RawPhysical -Controller $controller -Datastore $datastore -ErrorAction stop
        }
        catch {
            Remove-PfaHostGroupVolumeConnection -Array $flasharray -VolumeName $newvol.name -HostGroupName $hostGroup.name|Out-Null
            Remove-PfaVolumeOrSnapshot -Array $flasharray -Name $newvol.name |Out-Null
            Remove-PfaVolumeOrSnapshot -Array $flasharray -Eradicate:$true -Name $newvol.name |Out-Null
            throw $PSItem
        }       
    }
    End{
        return $newRDM
    }
}
function get-faVolfromRDM {
    <#
    .SYNOPSIS
      Retrieves the FlashArray volume that hosts a RDM disk.
    .DESCRIPTION
      Takes in a RDM virtual disk and a FlashArray and returns the volume if found.
    .INPUTS
      FlashArray connection and a virtual disk.
    .OUTPUTS
      Returns FlashArray volume or error if not found.
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 02/27/2019
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$True,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$rdm,

            [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray
    )
    Begin {
        #nothing to do here
    }
    Process {
        if ($rdm.DiskType -ne 'RawPhysical')
        {
            throw "This is not a physical mode RDM disk. Detected configuration is $($rdm.DiskType)"
        }
        $lun = ("naa." + $rdm.ExtensionData.Backing.LunUuid.substring(10).substring(0,32))
        if ($lun -like 'naa.624a9370*')
        {
            $volSerial = ($lun.ToUpper()).substring(12)
            $purevol =  Get-PfaVolumes -Array  $flasharray -Filter "serial='$volSerial'"
        }
        else {
            throw "Specified RDM is not hosted on FlashArray storage."
        }
    }
    End {
        if ($null -ne $purevol)
        {
            return $purevol
        }
        else {
            throw "Specified RDM was not found on this FlashArray."
        }
    }  
}
function new-faVolRdmSnapshot {
    <#
    .SYNOPSIS
      Creates a new FlashArray snapshot of a given RDM
    .DESCRIPTION
      Takes in a RDM disk and the corresponding FlashArray and creates a snapshot.
    .INPUTS
      FlashArray connection and a RDM disk
    .OUTPUTS
      Returns created snapshot.
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 02/27/2019
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$true,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray,

            [Parameter(Position=1,mandatory=$True,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$rdm,

            [Parameter(Position=2)]
            [string]$snapshot
    )
    Begin {
        #nothing to do here
    }
    Process {
        $pureVol = $rdm | get-faVolfromRDM -flasharray $flasharray -ErrorAction Stop
        if ($snapshot -eq "")
        {
            $newSnapshot = New-PfaVolumeSnapshots -Array $flasharray -Sources $pureVol.name  
        }
        else {
            $newSnapshot = New-PfaVolumeSnapshots -Array $flasharray -Sources $pureVol.name -Suffix $snapshot
        }
    }
    End {
        return $newSnapshot
    }  
}
function get-faVolRDMSnapshots {
    <#
    .SYNOPSIS
      Retrieves snapshots of a FlashArray-based RDM
    .DESCRIPTION
      Pass in a RDM disk and this will returns all of the FlashArray snapshots
    .INPUTS
      FlashArray connection and a RDM-based disk.
    .OUTPUTS
      Returns FlashArray snapshot(s).
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 02/27/2019
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$True,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$rdm,

            [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray
    )
    Begin {
        #nothing to do here
    }
    Process {
        $pureVol = $rdm | get-faVolfromRDM -flasharray $flasharray 
        $snapshots = Get-PfaVolumeSnapshots -Array $flasharray -VolumeName $pureVol.name 
    }
    End {
        return $snapshots
    }  
}
function copy-faSnapshotToRDM {
    <#
    .SYNOPSIS
      Input a FlashArray RDM and a snapshot to refresh the RDM
    .DESCRIPTION
      Pass in a RDM disk and a snapshot and it will copy the snapshot to the RDM FlashArray volume
    .INPUTS
      FlashArray connection, a snapshot, and a RDM-based disk.
    .OUTPUTS
      Returns the RDM disk
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 02/27/2019
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$True,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$rdm,

            [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray,

            [Parameter(Position=2)]
            [string]$snapshot,

            [Parameter(Position=3)]
            [switch]$offlineConfirm
    )
    Begin {
        if ($snapshot -eq "")
        {
            throw "You must enter a snapshot source"
        }
    }
    Process {
        $sourceVol = $rdm | get-faVolfromRDM -flasharray $flasharray 
        $vm = $rdm.Parent
        $controller = $rdm |Get-ScsiController
        $datastore = $rdm |Get-Datastore
        Remove-HardDisk $rdm -DeletePermanently -Confirm:$false
        $refreshedVol = New-PfaVolume -Array $flasharray -VolumeName $sourceVol.name -Source $snapshot -Overwrite -ErrorAction Stop
        $snapshotObj = Get-PfaVolumeSnapshot -SnapshotName $snapshot -Array $flasharray
        if ($snapshotObj.size -ne $sourceVol.size)
        {
            if ($offlineConfirm -ne $true)
            {
                throw "The snapshot and target are different sizes. FlashArray volumes can be resized online, but VMware does not permit it with RDMs. Please confirm you will allow the RDM to go offline temporarily for the resize operation with the -offlineConfirm parameter."
            }
            $esxiHosts = $rdm.Parent|get-cluster| Get-VMHost 
            foreach ($esxiHost in $esxiHosts)
            {
                $esxi = $esxiHost.ExtensionData
                $storageSystem = Get-View -Id $esxi.ConfigManager.StorageSystem
                $hbas = ($esxihost |Get-VMHostHba |where-object {$_.Type -eq "FibreChannel" -or $_.Type -eq "iSCSI"}).device
                foreach ($hba in $hbas) {
                    $storageSystem.rescanHba($hba)
                }
                $storageSystem.RefreshStorageSystem()
            }
            $newNAA =  "naa.624a9370" + $refreshedVol.serial.toLower()
            $updatedRDM = $vm | new-harddisk -DeviceName "/vmfs/devices/disks/$($newNAA)" -DiskType RawPhysical -Controller $controller -Datastore $datastore -ErrorAction stop
        }
    }
    End {
        return $updatedRDM
    }  
}
function set-faVolRDMCapacity {
    <#
    .SYNOPSIS
      Resizes the RDM volume
    .DESCRIPTION
      Takes in a new size and resizes the underlying volume and rescans the VMware environment
    .INPUTS
      Takes in a RDM virtual disk, a FlashArray, and a new size.
    .OUTPUTS
      Returns RDM disk.
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 03/1/2019
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$True,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$rdm,

            [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray,

            [Parameter(Position=2)]
            [int]$sizeInGB,

            [Parameter(Position=3)]
            [int]$sizeInTB,

            [Parameter(Position=4)]
            [switch]$truncate,

            [Parameter(Position=5)]
            [switch]$offlineConfirm
    )
    Begin {
        if ($offlineConfirm -ne $true)
        {
            throw "FlashArray volumes can be resized online, but VMware does not permit it with RDMs. Please confirm you will allow the RDM to go offline temporarily for the resize operation with the -offlineConfirm parameter."
        }
        if (($sizeInGB -eq 0) -and ($sizeInTB -eq 0))
        {
            throw "Please enter a size in GB or TB"
        }
        elseif (($sizeInGB -ne 0) -and ($sizeInTB -ne 0)) {
            throw "Please only enter a size in TB or GB, not both."
        }
        elseif ($sizeInGB -ne 0) {
            $volSize = $sizeInGB * 1024 *1024 *1024   
        }
        else {
            $volSize = $sizeInTB * 1024 *1024 *1024 * 1024
        }
    }
    Process {
        $pureVol = $rdm | get-faVolfromRDM -flasharray $flasharray 
        $vm = $rdm.Parent
        $controller = $rdm |Get-ScsiController
        $datastore = $rdm |Get-Datastore
        if (($truncate -ne $true) -and ($pureVol.size -gt $volSize))
        {
            throw "This operation will shrink the target RDM--please ensure this is expected and if so, please rerun the operation with the -truncate parameter."
        }
        Remove-HardDisk $rdm -DeletePermanently -Confirm:$false
        if ($truncate -eq $true)
        {
            $expandedVol = Resize-PfaVolume -Array $flasharray -VolumeName $pureVol.name -NewSize $volSize -Truncate
        }
        else {
            $expandedVol = Resize-PfaVolume -Array $flasharray -VolumeName $pureVol.name -NewSize $volSize 
        }
        $esxiHosts = $rdm.Parent|get-cluster| Get-VMHost 
        foreach ($esxiHost in $esxiHosts)
        {
            $esxi = $esxiHost.ExtensionData
            $storageSystem = Get-View -Id $esxi.ConfigManager.StorageSystem
            $hbas = ($esxihost |Get-VMHostHba |where-object {$_.Type -eq "FibreChannel" -or $_.Type -eq "iSCSI"}).device
            foreach ($hba in $hbas) {
                $storageSystem.rescanHba($hba)
            }
            $storageSystem.RefreshStorageSystem()
        }
        $expandedVol = Get-PfaVolume -Name $expandedVol.name -Array $flasharray
        $newNAA =  "naa.624a9370" + $expandedVol.serial.toLower()
        $updatedRDM = $vm | new-harddisk -DeviceName "/vmfs/devices/disks/$($newNAA)" -DiskType RawPhysical -Controller $controller -Datastore $datastore -ErrorAction stop
    }
    End {
        return $updatedRDM
    }  
}
function remove-faVolRDM {
    <#
    .SYNOPSIS
      Removes an RDM volume
    .DESCRIPTION
      Deletes the virtual disk pointer to the RDM and deletes the volume on the FlashArray. Volume will be deleted permanently in 24 hours
    .INPUTS
      Takes in a RDM virtual disk and a FlashArray connection.
    .OUTPUTS
      Returns destroyed FA volume.
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 02/28/2019
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$True,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$rdm,

            [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray
    )
    Begin {
        #nothing to do
    }
    Process {
        $pureVol = $rdm | get-faVolfromRDM -flasharray $flasharray 
        $esxiHosts = $rdm.Parent|get-cluster| Get-VMHost 
        Remove-HardDisk $rdm -DeletePermanently -Confirm:$false
        $hostConnections = Get-PfaVolumeHostConnections -Array $flasharray -VolumeName $pureVol.name
        if ($hostConnections.count -gt 0)
        {
            foreach ($hostConnection in $hostConnections)
            {
                Remove-PfaHostVolumeConnection -Array $flasharray -VolumeName $pureVol.name -HostName $hostConnection.host |Out-Null
            } 
        }
        $hostGroupConnections = Get-PfaVolumeHostGroupConnections -Array $flasharray -VolumeName $pureVol.name
        if ($hostGroupConnections.count -gt 0)
        {
            $hostGroupConnections = $hostGroupConnections.hgroup |Select-Object -unique
            foreach ($hostGroupConnection in $hostGroupConnections)
            {
                Remove-PfaHostGroupVolumeConnection -Array $flasharray -VolumeName $pureVol.name -HostGroupName $hostGroupConnection |Out-Null
            } 
        }
        foreach ($esxiHost in $esxiHosts)
        {
            $esxi = $esxiHost.ExtensionData
            $storageSystem = Get-View -Id $esxi.ConfigManager.StorageSystem
            $hbas = ($esxihost |Get-VMHostHba |where-object {$_.Type -eq "FibreChannel" -or $_.Type -eq "iSCSI"}).device
            foreach ($hba in $hbas) {
                $storageSystem.rescanHba($hba)
            }
        }
       $destroyedVol =  Remove-PfaVolumeOrSnapshot -Array $flasharray -Name $pureVol.name 
    }
    End {
        return $destroyedVol
    }  
}
function convert-faVolRDMtoVvol {
    <#
    .SYNOPSIS
      Converts a RDM to a VVol
    .DESCRIPTION
      Removes the RDM from the virtual machine and copies it to a new VVol and destroys the old RDM.
    .INPUTS
      Takes in a RDM virtual disk, a FlashArray connection, and optionally a VVol datastore.
    .OUTPUTS
      Returns the new VVol virtual disk.
    .NOTES
      Version: 1.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 03/01/2019
      Purpose/Change: Initial script development
   
    *******Disclaimer:******************************************************
    This scripts are offered "as is" with no warranty. While this
    scripts is tested and working in my environment, it is recommended that you test
    this script in a test lab before using in a production environment. Everyone can
    use the scripts/commands provided here without any written permission but I
    will not be liable for any damage or loss to the system.
    ************************************************************************
    #>


    [CmdletBinding()]
    Param(
            [Parameter(Position=0,mandatory=$True,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk]$rdm,

            [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray,

            [Parameter(Position=3,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]$datastore,

            [Parameter(Position=3)]
            [switch]$offlineConfirm
    )
    Begin {
        #nothing to do
    }
    Process {
        $vm = $rdm.Parent
        if (($vm.PowerState -ne "PoweredOff") -and ($offlineConfirm -ne $true))
        {
            throw "The RDM to VVol migration is an offline process--please either shut down the VM or confirm this downtime with the -offlineConfirm parameter"
        }    
        $sourceVol = $rdm|get-faVolfromRDM -flasharray $flasharray -ErrorAction Stop
        $arraySerial = (Get-PfaArrayAttributes -array $fa).id
        if ($null -eq $datastore)
        {
            $datastores = $vm |get-vmhost |Get-Datastore |Where-Object {$_.Type -eq "VVOL"}
            foreach ($checkDatastore in $datastores)
            {
                if ($arraySerial -eq $checkDatastore.ExtensionData.Info.VvolDS.StorageArray[0].uuid.Substring(16))
                {
                    #finding first VVol datastore on host running VM on same FlashArray
                    $datastore = $checkDatastore
                    break
                }
            }
            if ($null -eq $datastore)
            {
                throw "No VVol datastore found on ESXi host for input array. Please ensure one is mounted."
            }
        }
        else {
            if ($arraySerial -ne $datastore.ExtensionData.Info.VvolDS.StorageArray[0].uuid.Substring(16))
            {
                throw "The input datastore is not on the same array as the input FlashArray connection."
            }
        }
        $controller = $rdm |Get-ScsiController
        $volSize = $rdm.CapacityGB
        remove-faVolRDM -rdm $rdm -flasharray $flasharray -ErrorAction Stop |Out-Null
        $vvolVmdk = $vm | new-harddisk -CapacityGB $volSize -Controller $controller -Datastore $datastore -ErrorAction stop
        $vvolUuid = $vvolVmdk |get-vvolUuidFromHardDisk
        $targetVol = get-faVolumeNameFromVvolUuid -flasharray $flasharray -vvolUUID $vvolUuid
        Restore-PfaDestroyedVolume -Array $flasharray -Name $sourceVol.name |Out-Null
        New-PfaVolume -Array $flasharray -VolumeName $targetVol -Source $sourceVol.name -Overwrite -ErrorAction Stop |Out-Null
        Remove-PfaVolumeOrSnapshot -Array $flasharray -Name $sourceVol.name |Out-Null
    }
    End {
        return $vvolVmdk
    }  
}