Utils.Exported.ps1


<#
 .Synopsis
    Get the list of drive letters available for mounting a snapshot.
 .Description
    Query the specified ComputerAddress to get a list of available drive letters.
    This information can be used in the Mount-PsbSnapshotSet cmdlet to mount a snapshot set to those drive letters using the Path parameter.
    If an available drive letter is in use as a mapped network drive, the drive letter will have to be unmapped from the share before the mounted disk is accessible.
 .Parameter ComputerAddress
    The name of the VM.
 .Parameter ComputerSession
    The PSSession for the VM.
 .Example
    $session = new-pssession
    Get-PSBAvailableDrive -ComputerAddress $vmName -ComputerSession $session
#>

function Invoke-GetAvailableDriveOp {
    Param (
        [Parameter(Mandatory = $true)]
        [string]
        $ComputerAddress,

        [Parameter(Mandatory = $true)]
        [System.Management.Automation.Runspaces.PSSession]
        $ComputerSession,

        [parameter(Mandatory = $true)]
        [string]
        $FunctionName
    )

    $FreeDriveLetter = SafeInvokeRemote -Session $ComputerSession -ScriptBlock {
        $Drives = (Get-PSDrive).Name -match '^[a-z]$'

        $AvailableDrives = @([int][char]'D'..[int][char]'Z' | foreach { if ($Drives -notcontains [char]$_) { [char]$_ + ':' } })
        return $AvailableDrives
    }

    Write-Log -Level INFO -FunctionName $FunctionName -Msg "Free drive letters: $FreeDriveLetter"
    return $FreeDriveLetter
}

<#
 .Synopsis
    Connects an imported Lun as Hard Disk to VM hosted on ESXi
 .Description
    Connects Lun which is imported using Import-PfaSqlSnap as Hard Disk to VM hosted on ESXi configured as RDM
 .Parameter VolumeSerials
    List of Volume serial number of the Lun on Flash Array which is imported.
 .Parameter VCenterAddress
    The friendly name of the controlling VCenter previously configured with Add-PfaBackupCred
 .Parameter VCenterCredential
    vCenter credentials
 .Parameter VMName
    The name of the VM, if the SQL server host is a VM.
 .Parameter FlashArrayAddress
    The FlashArray address
 .Parameter VMPersistentId
    The PersistentId (also referred to as the InstanceUuid or MoRef) of the VM.
 .Example
    Connect-PfaVMHardDisk -VolumeSerials @(AC02BDF1FF541479000122CD) -VMName $VMName -VCenterAddress $VCenterAddress
#>

function Connect-PfaVMHardDisk {
    Param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string[]] $VolumeSerials,

        [Parameter(Mandatory = $true, ParameterSetName= 'VMName')]
        [ValidateNotNullOrEmpty()]
        [string] $VMName,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string] $VCenterAddress,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [pscredential] $VCenterCredential,

        [Parameter(Mandatory = $true)]
        $RestClient,

        [Parameter(Mandatory = $true, ParameterSetName='VMPersistentId')]
        [Parameter(Mandatory = $false, ParameterSetName='VMName')]
        [AllowEmptyString()]
        [string]
        $VMPersistentId,

        [Parameter(Mandatory = $false)]
        [string]
        $DatastoreName
    )
    $FunctionName = $MyInvocation.MyCommand
    Write-Log -Level INFO -FunctionName $FunctionName -Msg "Entering with parameter: $(Get-Parameter $MyInvocation)"

    $EsxiDetails = Get-ESXiDetails -VCenterAddress $VCenterAddress -VCenterCredential $VCenterCredential -VMName $VMName -VMPersistentId $VMPersistentId
    $ESXiName = $EsxiDetails.ESXiName
    # Get the current name and Id from the VM we found.
    # Handles the case where the user supplied just the name or id,
    # or if they gave both, but the name doesn't coorespond to the Id.
    
    if (!$ESXiName) {
        ThrowErrorCode -ErrorCode $ErrorCode_IdentifyEsxiError -params @($EsxiDetails.VMNameString,$VCenterAddress)
    }
    
    # this function can take a while to rescan storage, we need it so the recently connected volume shows up on the vcenter
    Get-VMHostStorage -VMHost $ESXiName -RescanAllHba
    
    foreach($VolumeSerial in $volumeSerials){
        $naaPage83 = Get-NaaPage83 -VolumeSerial $VolumeSerial
        $ScsiLun = Get-ScsiLun -VmHost $ESXiName | where CanonicalName -eq $naaPage83 #-eq is case insensitive by default
        if ($null -eq $ScsiLun -or [String]::IsNullOrWhiteSpace($ScsiLun)) {
            ThrowErrorCode -ErrorCode $ErrorCode_ScsiLunNotFound -params @($ESXiName,$naaPage83)
        }
        Write-Log -Level INFO -FunctionName $FunctionName -Msg "ESXi $ESXiName ScssiLun is $($ScsiLun)"

        $RDMHD = Get-HardDisk -VM $EsxiDetails.VM | where { ($_.PSObject.Properties['ScsiCanonicalName']) -and ($_.ScsiCanonicalName -eq $naaPage83) }
        if ($RDMHD) {
            Write-Log -Level WARN -FunctionName $FunctionName -Msg "RDM connection already exists on VM '$($EsxiDetails.VMNameString)' for volume serial $VolumeSerial"
        }
        else {
            Write-Log -Level INFO -FunctionName $FunctionName -Msg "Creating new RDM HD on VM '$($EsxiDetails.VMNameString)' for volume serial $VolumeSerial"
            if ($DatastoreName) {
                $Datastore = Get-Datastore $DatastoreName -Server $EsxiDetails.Server -ErrorAction SilentlyContinue
                # Replication DataStore appear twice
                if ($DataStore -is [array]) {
                    if ($DataStore.Length -ge 1) {
                        $DataStore = $DataStore[0]
                    }
                    else {
                        $DataStore = $null
                    }
                }
            }
            else {
                $Datastore = Get-DataStoreFromVolumeCopy -VM $EsxiDetails.VM -VolumeSerial $VolumeSerial -RestClient $RestClient
            }
            if ($null -eq $Datastore) {
                # We failed to get a valid DataStore, throw an error
                ThrowErrorCode -ErrorCode $ErrorCode_FailedToDetermineDatastore
            }
            else {
                # Verify DatStore type
                if ($Datastore.Type -ne "VMFS") {
                    ThrowErrorCode -ErrorCode $ErrorCode_WrongDatastoreType -params @($Datastore.name,$Datastore.Type)
                }
                $RDMHD = New-HardDisk -VM $EsxiDetails.VM -DiskType RawPhysical -DeviceName $ScsiLun.ConsoleDeviceName -Datastore $Datastore -ErrorAction Stop
                Write-Log -Level INFO -FunctionName $FunctionName -Msg "Creating new RDM HD on VM '$($EsxiDetails.VMNameString)' for volume serial $VolumeSerial - Done!"
            }
        }
    }
}

<#
 .Synopsis
    Removes a Hard Disk from VM hosted on ESXi
 .Description
    Removes a Hard Disk which was previously imported using Import-PfaSqlSnap from VM hosted on ESXi configured as RDM
 .Parameter VolumeSerial
    Volume serial number of the Lun on Flash Array which was previously imported.
 .Parameter VCenterAddress
    The friendly name of the controlling VCenter previously configured with Add-PfaBackupCred
 .Parameter VCenterCredential
    vCenterCredentials
 .Parameter VMName
    The name of the VM, if the SQL server host is a VM.
 .Parameter VMPersistentId
    The PersistentId (also referred to as the InstanceUuid or MoRef) of the VM.
 .Example
    Remove-PfaVMHardDisk -VolumeSerial AC02BDF1FF541479000122CD -VMName $VMName -VCenterAddress $VCenterAddress
#>

function Remove-PfaVMHardDisk {
    Param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string] $VolumeSerial,

        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [string] $VMName,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string] $VCenterAddress,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [PSCredential] $VCenterCredential,

        [Parameter(Position=0,mandatory=$false)]
        [AllowEmptyString()]
        [string]
        $VMPersistentId
    )
    $FunctionName = $MyInvocation.MyCommand
    Write-Log -Level INFO -FunctionName $FunctionName -Msg "Entering with parameter: $(Get-Parameter $MyInvocation)"

    $naaPage83 = Get-NaaPage83 -VolumeSerial $VolumeSerial

    $EsxiDetails = Get-ESXiDetails -VCenterAddress $VCenterAddress -VCenterCredential $VCenterCredential -VMName $VMName -VMPersistentId $VMPersistentId
    $ESXiName = $EsxiDetails.ESXiName

    $ScsiLun = Get-ScsiLun -VmHost $ESXiName | where CanonicalName -eq $naaPage83 #-eq is case insensitive by default
    if ($null -eq $ScsiLun -or [String]::IsNullOrWhiteSpace($ScsiLun)) {
        ThrowErrorCode -ErrorCode $ErrorCode_ScsiLunNotFound -params @($ESXiName,$naaPage83)
    }
    Write-Log -Level INFO -FunctionName $FunctionName -Msg "ESXi $ESXiName ScssiLun is $($ScsiLun)"

    $RDMHD = Get-HardDisk -VM $EsxiDetails.VM | where { ($_.PSObject.Properties['ScsiCanonicalName']) -and ($_.ScsiCanonicalName -eq $naaPage83) }
    if (-not $RDMHD) {
        Write-Log -Level INFO -FunctionName $FunctionName -Msg "RDM connection not found VM '$($EsxiDetails.VMNameString)'. for volume serial $VolumeSerial"
    }
    else {
        Write-Log -Level INFO -FunctionName $FunctionName -Msg "Removing RDM HD on VM '$($EsxiDetails.VMNameString)' for volume serial $VolumeSerial"
        $RDMHD = Remove-HardDisk -HardDisk $RDMHD -DeletePermanently -Confirm:$false
        Write-Log -Level INFO -FunctionName $FunctionName -Msg "Removing RDM HD on VM '$($EsxiDetails.VMNameString)' for volume serial $VolumeSerial - Done!"
    }
}

<#
 .Synopsis
    Get VM Persistent ID (or IDs if more than one VM has the same name) from vCenter.
    The VM friendly name in vCenter, the vCenter hostname, FQDN, or IP Address, and the vCenter credential are required parameters.
 .Description
    Get VM Persistent ID (or IDs if more than one VM has the same name) from vCenter.
    The VM Persistent ID is needed when creating a new volume set (New-PsbVolumeSet) and when invoking a snapshot (Invoke-PsbSnapshotJob) so that operations can succeed even if the VM is renamed in vCenter.
 .Parameter VCenterAddress
    The friendly name of the controlling VCenter previously configured with Add-PfaBackupCred
 .Parameter VCenterCredential
    The credential for the controlling VCenter previously configured with Add-PfaBackupCred
 .Parameter VMName
    VM name in vCenter
 .Example
    Get-PSBVMPersistentId -VMName $vmName -VCenterAddress $vCenterEndpoint -VCenterCredential $vCenterCredential
#>

function Invoke-GetVMPersistentIdOp {
    Param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String] $VCenterAddress,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [PSCredential] $VCenterCredential,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string] $VMName,

        [parameter(Mandatory = $true)]
        [string]
        $FunctionName
    )
    
    $IDs = @()
    
    if (-not (Get-Module -ListAvailable -Name VMware.PowerCLI)) {
        ThrowErrorCode -ErrorCode $ErrorCode_PowerCLIMissing
    }
    Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -DefaultVIServerMode Multiple -Scope Session -Confirm:$false | Out-Null
    Connect-VIServer -Server $VCenterAddress -Credential $VCenterCredential -ErrorAction Stop | Out-Null
    $VMs = Get-VM -Server $VCenterAddress -Name $VMName
    
    $IDs = foreach ($VM in $VMs) {
        $VM.PersistentId
    }
    
    Write-Log -Level INFO -FunctionName $FunctionName -Msg "VM(s) Persistent ID: $($IDs -Join ',')"
    return $IDs
}

<#
 .Synopsis
    Enumerate information about a disk by specifying a drive letter or mount point.
 .Description
    Enumerate information about a disk by specifying a drive letter or mount point.
    Disks that are not supported for Pure Storage snapshots will return a value of Unsupported.
    VolumeType (physical, rdm, vvol) of a disk must be specified in the backup configuration.
 .Parameter ComputerAddress
    The name of the SQL server host machine.
 .Parameter Path
    Disk path
 .Parameter RestClient
   Client for FlashArray
 .Parameter VCenterAddress
    When specified limit searching vCenters to only $VCenterAddress
 .Parameter VCenterCredential
    vCenter credentials
 .Parameter VMName
    When specified limit searching VMs to only $VMName
 .Example
    Get-PfaVerboseDiskInfo -ComputerAddress mycomputer -Path E:
    Get disk information for drive E:
#>

function Get-PfaVerboseDiskInfo {
    param (
        [Parameter(Mandatory = $true, ParameterSetName= 'Virtual')]
        [Parameter(Mandatory = $true, ParameterSetName= 'Physical')]
        [System.Management.Automation.Runspaces.PSSession]
        $Session,

        [Parameter(Mandatory = $true, ParameterSetName= 'Virtual')]
        [Parameter(Mandatory = $true, ParameterSetName= 'Physical')]
        [string]
        $Path,

        [Parameter(Mandatory = $true, ParameterSetName= 'Virtual')]
        [Parameter(Mandatory = $true, ParameterSetName= 'Physical')]
        $RestClient,

        [Parameter(Mandatory = $true, ParameterSetName= 'Virtual')]
        [string]
        $VCenterAddress,

        [Parameter(Mandatory = $true, ParameterSetName= 'Virtual')]
        [PSCredential]
        $VCenterCredential,

        [Parameter(Mandatory = $true, ParameterSetName= 'Virtual')]
        [string]
        $VMName
    )

    $FunctionName = $MyInvocation.MyCommand
    Write-Log -Level INFO -FunctionName $FunctionName -Msg "Entering with parameter: $(Get-Parameter $MyInvocation)"

    $result = @{}

    $FlashArrayAddress = $RestClient.ArrayName
    $ArrayConnections = @{}
    if ($RestClient) {
        # An array name is provided
        $ArrayConnections[$FlashArrayAddress] = $RestClient
    }    

      
    $PathList = $Path.Split(",")
    foreach ($CurrentPath in $PathList) {
        $DiskInfo = SafeInvokeRemote -Session $session -ArgumentList $CurrentPath -ScriptBlock {
            param (
                $Path
            )
            $DiskInfo = $null
            if ($Path.Length -eq 1) {
                # Drive Letter without :
                $partitionFromPath = get-partition -DriveLetter $Path -ErrorAction SilentlyContinue
            }
            elseif ($Path.Length -eq 2) {
                if($Path[1] -ne ':'){
                    ThrowErrorCode -ErrorCode $ErrorCode_InvalidPath -params @($Path)
                }
                
                # Drive Letter
                $DriveLetter = $Path.Substring(0, $Path.Length - 1)
                $partitionFromPath = get-partition -DriveLetter $DriveLetter -ErrorAction SilentlyContinue
            }
            elseif ($Path.Length -gt 2) {
                # Mount Point
                $ResolvedPath = (Resolve-Path -Path $Path).Path
                if ($ResolvedPath[$ResolvedPath.Length -1] -ne '\') {
                $ResolvedPath = $ResolvedPath + '\'
                }
                $WmiVol = Get-WmiObject win32_volume | Where-Object {$_.Name -eq $ResolvedPath}
                $CimVol = get-volume | Where-Object {$_.path -eq $WmiVol.DeviceID}
                $partitionFromPath = get-partition -Volume $CimVol -ErrorAction SilentlyContinue
            }
            else {
                ThrowErrorCode -ErrorCode $ErrorCode_UnsupportedPathFormat -params @($Path)
            }

            if(-not $partitionFromPath){
                # drive letter/path did not exist
                ThrowErrorCode -ErrorCode $ErrorCode_InvalidPath -params @($Path)
            }

            $DiskInfo = $partitionFromPath | get-disk

            $partitionsInDisk = $DiskInfo | Get-Partition | where-object {$_.AccessPaths}
            if($partitionsInDisk.Count -gt 1){
                ThrowErrorCode -ErrorCode $ErrorCode_MultiPartitions
            }
            
            return $DiskInfo
        }

        $result[$CurrentPath] = FillInDiskInfo -DiskInfo $DiskInfo -restClient $RestClient -VCenterAddress $VCenterAddress -VMName $VMName
    }
    return $result
}


function GetVerboseDiskInfoById {
    param (
        [System.Management.Automation.Runspaces.PSSession]
        $Session,
        $RestClient,
        [string[]]
        $DiskIds,
        [string]
        $VCenterAddress,
        [PSCredential]
        $VCenterCredential,
        [string]
        $VMName
    )

    $FunctionName = $MyInvocation.MyCommand
    Write-Log -Level INFO -FunctionName $FunctionName -Msg "Entering with parameter: $(Get-Parameter $MyInvocation)"

    $result = @{}

    $FlashArrayAddress = $RestClient.ArrayName
    $ArrayConnections = @{}
    if ($RestClient) {
        # An array name is provided
        $ArrayConnections[$FlashArrayAddress] = $RestClient
    }    

    $DisksInfo = SafeInvokeRemote -Session $session -ArgumentList @(,$DiskIds) -ScriptBlock {
        param($DiskIds)

        # get disks with serial in diskIds and that have 0 or 1 partitions
        $DisksInfo =  get-disk | Where-object {$DiskIds -contains $_.SerialNumber}
        
        return $DisksInfo
    }

    foreach ($DiskInfo in $DisksInfo) {
        $DiskUniqueID = $DiskInfo.UniqueId
        
        $result[$DiskUniqueID] = FillInDiskInfo -DiskInfo $DiskInfo -restClient $RestClient -VCenterAddress $VCenterAddress -VMName $VMName
        
        $Path = SafeInvokeRemote -Session $session -ArgumentList $DiskInfo -ScriptBlock {
            param($DiskInfo)
            $paths = ($DiskInfo | Get-Partition).AccessPaths
            return $paths | Where-Object {-not ($_ -like "\\?\*")}
        }

        if($Path){
            $result[$DiskUniqueID]["Path"] = $Path
        }
        else{
            $result[$DiskUniqueID]["Path"] = ""
        }
    }

    return $result
}


function CleanupMountByDiskId{
    Param(

        [System.Management.Automation.Runspaces.PSSession]
        $ComputerSession,
        $RestClient,
        [string[]]
        $DiskIds,
        [string[]]
        $newVolumesSerials,
        [string]
        $VCenterAddress,
        [PSCredential]
        $VCenterCredential,
        [string]
        $VMName,
        [string]
        $FunctionName
        
    )

    # unexpose disks on $diskIds
    $verboseDisk = GetVerboseDiskInfoById -Session $ComputerSession -RestClient $RestClient -DiskIds $diskIds -VCenterAddress $VCenterAddress -VCenterCredential $VCenterCredential -VMName $VMName
    foreach ($disk in $verboseDisk.Keys){
        try{
            if( -not ($verboseDisk[$disk]['ArrayVolumeInfo']["VolumeSerial"] -in $newVolumesSerials)){
                #it's not one of the mounted volumes
                continue
            }

            if($verboseDisk[$disk]["Path"]){
                # unexpose vvol from VM
                Write-Log -Level INFO  -FunctionName $FunctionName -Msg "Unexposing $($verboseDisk[$disk]["Path"])"

                $success = $success -and (Unexpose-Disks -Session $ComputerSession -Paths $verboseDisk[$disk]["Path"] -VolumeSerialNumbers $diskIds)
            }

            if("vVol" -eq $disk["VolumeType"]){

                # destroy vvol
                $disk.VMWareInfo.VMDisk | Remove-HardDisk -DeletePermanently -Confirm:$false
            }
        }
        catch{
            Write-Log -Level WARN -FunctionName $FunctionName -Msg "Failed to cleanup $disk"
        }
    }
}

function CleanupMountByVolSerial {
    Param(
        $RestClient,
        [string[]]
        $newVolumesSerials,
        [string]
        $VolumeType,
        [string]
        $VCenterAddress,
        [PSCredential]
        $VCenterCredential,
        $MountVM,
        [string]
        $FunctionName
    )
    # destroy volumes on $newvolumeSerials
    foreach ($serial in $newVolumesSerials){
        try{
            $volume = Get-Pfa2Volume -Array $RestClient -Filter "Serial='$($serial)'" -ErrorAction SilentlyContinue
            
            if($volume){
                # remove RDM volumes from VM
                if ("RDM" -ieq $VolumeType) {
                    Remove-PfaVMHardDisk -VolumeSerial $serial -VMName $($MountVM.Name) -VMPersistentId $($MountVM.PersistentId) -VCenterAddress $VCenterAddress -VCenterCredential $VCenterCredential -ErrorAction SilentlyContinue
                }
                
                if ("VVOL" -ieq $VolumeType) {
                    # vvols should be cleaned by the unexpose cmdlet
                    # if vvol still exists it failed the unexpose
                    # we should not delete the vvol manually as it can put the datastore in a bad status
                    # we will log a warning and proceed
                    Write-Log -Level WARN -FunctionName $FunctionName -Msg "vVol [$serial] could not be removed. Please delete disk manually on the vCenter"
                    continue
                }

                # remove all host/hostgroup connections
                Disconnect-VolHosts -volumeName $Volume.Name -RestClient $RestClient
                
                # destroy vol
                Remove-Pfa2Volume -Array $RestClient -Name $Volume.Name 
                
            }
        }
        catch{
            Write-Log -Level WARN -FunctionName $FunctionName -Msg "Failed to cleanup $serial"
        }

    }
}

function FillInDiskInfo{
    param(
        $DiskInfo,
        $restClient,
        $VCenterAddress,
        $VMName
    )

    $result = @{
        "VolumeType" = "Unsupported";
        "ArrayVolumeInfo" = @() ;
        "ComputerDiskInfo" = $null;
        "VMWareInfo" = @()
    }

    $result["ComputerDiskInfo"] = $DiskInfo
    $DiskName = $DiskInfo.FriendlyName
    $DiskUniqueID = $DiskInfo.UniqueId

    if ($DiskName -like "PURE FlashArray*") {
        $result["VolumeType"] = "Physical"
        # Might be Physical or RDM Disk
        # Search all arrays for Volume with serial nnumber that matches the Disk SerialNumber
        
        $volume = Get-Pfa2Volume -Array $RestClient -Filter "Serial='$($DiskInfo.SerialNumber)'"
        if ($null -ne $volume) {
            $result['ArrayVolumeInfo'] += @{VolumeName=$volume.Name; ArrayName=$RestClient.ArrayName; "VolumeSerial"=$volume.serial; "VolumeId"=$volume.Id}
        }
        else{
            # if we didn't find the volume on the array, throw an error
            ThrowErrorCode -ErrorCode $ErrorCode_VolumesNotFoundOnFA -params @($DiskInfo.SerialNumber) 
        }
        # Check if RDM. Find HardDisk from vCenter and see if the DiskType is RawPhysical
        $naaPage83 = "naa.$DiskUniqueID"                
        if ($VCenterAddress) {
            $VMHardDisk = Get-HardDisk -VM $VMName | Where-Object {($_.PSObject.Properties['ScsiCanonicalName']) -and ($_.ScsiCanonicalName.ToUpper() -eq $naaPage83.ToUpper())}
            if ($null -ne $VMHardDisk) {
                $result["VolumeType"] = "RDM"
                $result["VMWareInfo"] += @{ "VCenterAddress"= $VCenterAddress; "VMName"=$VMName; "VMDisk"=$VMHardDisk }
            }
        }
        
    }
    elseif ($DiskName -like "VMWare*") {
        # Might be vVol or vmfs disk

        if(-not $VCenterAddress){
            ThrowErrorCode -ErrorCode $ErrorCode_InvalidVolType -params @($path,'Physical') 
        }
        
        $VMHardDisks = Get-HardDisk -VM $VMName
        foreach ($vmHardDisk in $vmHardDisks)
        {
            if ($null -eq $vmHardDisk.ExtensionData.Backing.uuid) {
                continue
            }
            $vmHardDiskUuid = $vmHardDisk.ExtensionData.Backing.uuid | ForEach-Object {$_.replace(' ','').replace('-','')}
            if ($vmHardDiskUuid -ieq $DiskUniqueID) {
                # Get related Array info
                try
                {
                    $vvolUUID =  Get-PfaVvolUuidFromVmdk -vmdk $vmHardDisk
                    $result["VolumeType"] = "vVol"
                    $result["VMWareInfo"] += @{ "VCenterAddress"= $VCenterAddress; "VMName"=$VMName; "VMDisk"=$VMHardDisk }
                    # Get related Array information
                                                    
                    try {                                    
                        $volname = Get-PfaVolumeNameFromVvolUuid -vvolUUID $vvolUUID -restClient $restClient
                        if ($null -ne $volname){
                            $EV = $null

                            # gets the volume info from the array
                            $vol = Get-Pfa2Volume -Name $volname -Array $restClient -ErrorAction SilentlyContinue -ErrorVariable EV

                            # if volume not found on array throw error
                            if ($EV) {
                                ThrowErrorCode -ErrorCode $ErrorCode_VolumesNotFoundOnFA -params @($volname) -innerException $EV.Exception
                            }

                            # if volume found on array add info to the result
                            $result['ArrayVolumeInfo'] += @{VolumeName=$vol.Name; ArrayName=$RestClient.ArrayName; "VolumeSerial"=$vol.Serial; "VolumeId"=$vol.Id}
                        }
                    } catch {
                        $Msg = "Failed to check array $($RestClient.ArrayName)). $($_.Exception.Message)"
                        Write-Log -Level WARN -FunctionName $FunctionName -Msg $Msg
                    }                                                            
                }
                catch
                {
                    # Either not a vvol or not a Pure Storage disk
                    continue # Skip this disk
                }
            }
        }
        
        if ("Unknown" -eq $result) {
            # Could not find a matching disk then it must be a non-supported disk
            $result = "Unsupported"
        }
    }
    else {
        # Unsupported disk type
        $result = "Unsupported"
    }

    return $result
}
# SIG # Begin signature block
# MIIn+AYJKoZIhvcNAQcCoIIn6TCCJ+UCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU84BIm8Jo0/z/erJvoPOMRINf
# VRGggiEoMIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv21DiCEAYWjANBgkqhkiG9w0B
# AQwFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk
# IElEIFJvb3QgQ0EwHhcNMjIwODAxMDAwMDAwWhcNMzExMTA5MjM1OTU5WjBiMQsw
# CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
# ZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQw
# ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz
# 7MKnJS7JIT3yithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS
# 5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7
# bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfI
# SKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jH
# trHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14
# Ztk6MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2
# h4mXaXpI8OCiEhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt
# 6zPZxd9LBADMfRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPR
# iQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ER
# ElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4K
# Jpn15GkvmB0t9dmpsh3lGwIDAQABo4IBOjCCATYwDwYDVR0TAQH/BAUwAwEB/zAd
# BgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wHwYDVR0jBBgwFoAUReuir/SS
# y4IxLVGLp6chnfNtyA8wDgYDVR0PAQH/BAQDAgGGMHkGCCsGAQUFBwEBBG0wazAk
# BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAC
# hjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURS
# b290Q0EuY3J0MEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0
# LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwEQYDVR0gBAowCDAGBgRV
# HSAAMA0GCSqGSIb3DQEBDAUAA4IBAQBwoL9DXFXnOF+go3QbPbYW1/e/Vwe9mqyh
# hyzshV6pGrsi+IcaaVQi7aSId229GhT0E0p6Ly23OO/0/4C5+KH38nLeJLxSA8hO
# 0Cre+i1Wz/n096wwepqLsl7Uz9FDRJtDIeuWcqFItJnLnU+nBgMTdydE1Od/6Fmo
# 8L8vC6bp8jQ87PcDx4eo0kxAGTVGamlUsLihVo7spNU96LHc/RzY9HdaXFSMb++h
# UD38dglohJ9vytsgjTVgHAIDyyCwrFigDkBjxZgiwbJZ9VVrzyerbHbObyMt9H5x
# aiNrIv8SuFQtJ37YOtnwtoeW/VvRXKwYw02fc7cBqZ9Xql4o4rmUMIIGrjCCBJag
# AwIBAgIQBzY3tyRUfNhHrP0oZipeWzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQG
# EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
# cnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIw
# MzIzMDAwMDAwWhcNMzcwMzIyMjM1OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UE
# ChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQg
# UlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEF
# AAOCAg8AMIICCgKCAgEAxoY1BkmzwT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCw
# zIP5WvYRoUQVQl+kiPNo+n3znIkLf50fng8zH1ATCyZzlm34V6gCff1DtITaEfFz
# sbPuK4CEiiIY3+vaPcQXf6sZKz5C3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ
# 7Gnf2ZCHRgB720RBidx8ald68Dd5n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7
# QKxfst5Kfc71ORJn7w6lY2zkpsUdzTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/teP
# c5OsLDnipUjW8LAxE6lXKZYnLvWHpo9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCY
# OjgRs/b2nuY7W+yB3iIU2YIqx5K/oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9K
# oRxrOMUp88qqlnNCaJ+2RrOdOqPVA+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6
# dSgkQe1CvwWcZklSUPRR8zZJTYsg0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM
# 1+mYSlg+0wOI/rOP015LdhJRk8mMDDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbC
# dLI/Hgl27KtdRnXiYKNYCQEoAA6EVO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbEC
# AwEAAaOCAV0wggFZMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1N
# hS9zKXaaL3WMaiCPnshvMB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9P
# MA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcB
# AQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggr
# BgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
# c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAI
# BgZngQwBBAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7Zv
# mKlEIgF+ZtbYIULhsBguEE0TzzBTzr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI
# 2AvlXFvXbYf6hCAlNDFnzbYSlm/EUExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/ty
# dBTX/6tPiix6q4XNQ1/tYLaqT5Fmniye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVP
# ulr3qRCyXen/KFSJ8NWKcXZl2szwcqMj+sAngkSumScbqyQeJsG33irr9p6xeZmB
# o1aGqwpFyd/EjaDnmPv7pp1yr8THwcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc
# 6UsCUqc3fpNTrDsdCEkPlM05et3/JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3c
# HXg65J6t5TRxktcma+Q4c6umAU+9Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0d
# KNPH+ejxmF/7K9h+8kaddSweJywm228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZP
# J/tgZxahZrrdVcA6KYawmKAr7ZVBtzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLe
# Mt8EifAAzV3C+dAjfwAL5HYCJtnwZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDy
# Divl1vupL0QVSucTDh3bNzgaoSv27dZ8/DCCBrAwggSYoAMCAQICEAitQLJg0pxM
# n17Nqb2TrtkwDQYJKoZIhvcNAQEMBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoT
# DERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UE
# AxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIxMDQyOTAwMDAwMFoXDTM2
# MDQyODIzNTk1OVowaTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
# bmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBS
# U0E0MDk2IFNIQTM4NCAyMDIxIENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
# AgoCggIBANW0L0LQKK14t13VOVkbsYhC9TOM6z2Bl3DFu8SFJjCfpI5o2Fz16zQk
# B+FLT9N4Q/QX1x7a+dLVZxpSTw6hV/yImcGRzIEDPk1wJGSzjeIIfTR9TIBXEmtD
# mpnyxTsf8u/LR1oTpkyzASAl8xDTi7L7CPCK4J0JwGWn+piASTWHPVEZ6JAheEUu
# oZ8s4RjCGszF7pNJcEIyj/vG6hzzZWiRok1MghFIUmjeEL0UV13oGBNlxX+yT4Us
# SKRWhDXW+S6cqgAV0Tf+GgaUwnzI6hsy5srC9KejAw50pa85tqtgEuPo1rn3MeHc
# reQYoNjBI0dHs6EPbqOrbZgGgxu3amct0r1EGpIQgY+wOwnXx5syWsL/amBUi0nB
# k+3htFzgb+sm+YzVsvk4EObqzpH1vtP7b5NhNFy8k0UogzYqZihfsHPOiyYlBrKD
# 1Fz2FRlM7WLgXjPy6OjsCqewAyuRsjZ5vvetCB51pmXMu+NIUPN3kRr+21CiRshh
# WJj1fAIWPIMorTmG7NS3DVPQ+EfmdTCN7DCTdhSmW0tddGFNPxKRdt6/WMtyEClB
# 8NXFbSZ2aBFBE1ia3CYrAfSJTVnbeM+BSj5AR1/JgVBzhRAjIVlgimRUwcwhGug4
# GXxmHM14OEUwmU//Y09Mu6oNCFNBfFg9R7P6tuyMMgkCzGw8DFYRAgMBAAGjggFZ
# MIIBVTASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBRoN+Drtjv4XxGG+/5h
# ewiIZfROQjAfBgNVHSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8B
# Af8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwdwYIKwYBBQUHAQEEazBpMCQG
# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKG
# NWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290
# RzQuY3J0MEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3JsMBwGA1UdIAQVMBMwBwYFZ4EMAQMw
# CAYGZ4EMAQQBMA0GCSqGSIb3DQEBDAUAA4ICAQA6I0Q9jQh27o+8OpnTVuACGqX4
# SDTzLLbmdGb3lHKxAMqvbDAnExKekESfS/2eo3wm1Te8Ol1IbZXVP0n0J7sWgUVQ
# /Zy9toXgdn43ccsi91qqkM/1k2rj6yDR1VB5iJqKisG2vaFIGH7c2IAaERkYzWGZ
# gVb2yeN258TkG19D+D6U/3Y5PZ7Umc9K3SjrXyahlVhI1Rr+1yc//ZDRdobdHLBg
# XPMNqO7giaG9OeE4Ttpuuzad++UhU1rDyulq8aI+20O4M8hPOBSSmfXdzlRt2V0C
# FB9AM3wD4pWywiF1c1LLRtjENByipUuNzW92NyyFPxrOJukYvpAHsEN/lYgggnDw
# zMrv/Sk1XB+JOFX3N4qLCaHLC+kxGv8uGVw5ceG+nKcKBtYmZ7eS5k5f3nqsSc8u
# pHSSrds8pJyGH+PBVhsrI/+PteqIe3Br5qC6/To/RabE6BaRUotBwEiES5ZNq0RA
# 443wFSjO7fEYVgcqLxDEDAhkPDOPriiMPMuPiAsNvzv0zh57ju+168u38HcT5uco
# P6wSrqUvImxB+YJcFWbMbA7KxYbD9iYzDAdLoNMHAmpqQDBISzSoUSC7rRuFCOJZ
# DW3KBVAr6kocnqX9oKcfBnTn8tZSkP2vhUgh+Vc7tJwD7YZF9LRhbr9o4iZghurI
# r6n+lB3nYxs6hlZ4TjCCBsIwggSqoAMCAQICEAVEr/OUnQg5pr/bP1/lYRYwDQYJ
# KoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
# bmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2
# IFRpbWVTdGFtcGluZyBDQTAeFw0yMzA3MTQwMDAwMDBaFw0zNDEwMTMyMzU5NTla
# MEgxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjEgMB4GA1UE
# AxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjMwggIiMA0GCSqGSIb3DQEBAQUAA4IC
# DwAwggIKAoICAQCjU0WHHYOOW6w+VLMj4M+f1+XS512hDgncL0ijl3o7Kpxn3GIV
# WMGpkxGnzaqyat0QKYoeYmNp01icNXG/OpfrlFCPHCDqx5o7L5Zm42nnaf5bw9Yr
# IBzBl5S0pVCB8s/LB6YwaMqDQtr8fwkklKSCGtpqutg7yl3eGRiF+0XqDWFsnf5x
# XsQGmjzwxS55DxtmUuPI1j5f2kPThPXQx/ZILV5FdZZ1/t0QoRuDwbjmUpW1R9d4
# KTlr4HhZl+NEK0rVlc7vCBfqgmRN/yPjyobutKQhZHDr1eWg2mOzLukF7qr2JPUd
# vJscsrdf3/Dudn0xmWVHVZ1KJC+sK5e+n+T9e3M+Mu5SNPvUu+vUoCw0m+PebmQZ
# BzcBkQ8ctVHNqkxmg4hoYru8QRt4GW3k2Q/gWEH72LEs4VGvtK0VBhTqYggT02ke
# fGRNnQ/fztFejKqrUBXJs8q818Q7aESjpTtC/XN97t0K/3k0EH6mXApYTAA+hWl1
# x4Nk1nXNjxJ2VqUk+tfEayG66B80mC866msBsPf7Kobse1I4qZgJoXGybHGvPrhv
# ltXhEBP+YUcKjP7wtsfVx95sJPC/QoLKoHE9nJKTBLRpcCcNT7e1NtHJXwikcKPs
# CvERLmTgyyIryvEoEyFJUX4GZtM7vvrrkTjYUQfKlLfiUKHzOtOKg8tAewIDAQAB
# o4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/
# BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
# MB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSltu8T
# 5+/N0GSh1VapZTGj3tXjSTBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5k
# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0
# YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0
# cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0
# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGlt
# ZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCBGtbeoKm1mBe8cI1P
# ijxonNgl/8ss5M3qXSKS7IwiAqm4z4Co2efjxe0mgopxLxjdTrbebNfhYJwr7e09
# SI64a7p8Xb3CYTdoSXej65CqEtcnhfOOHpLawkA4n13IoC4leCWdKgV6hCmYtld5
# j9smViuw86e9NwzYmHZPVrlSwradOKmB521BXIxp0bkrxMZ7z5z6eOKTGnaiaXXT
# UOREEr4gDZ6pRND45Ul3CFohxbTPmJUaVLq5vMFpGbrPFvKDNzRusEEm3d5al08z
# jdSNd311RaGlWCZqA0Xe2VC1UIyvVr1MxeFGxSjTredDAHDezJieGYkD6tSRN+9N
# UvPJYCHEVkft2hFLjDLDiOZY4rbbPvlfsELWj+MXkdGqwFXjhr+sJyxB0JozSqg2
# 1Llyln6XeThIX8rC3D0y33XWNmdaifj2p8flTzU8AL2+nCpseQHc2kTmOt44Owde
# OVj0fHMxVaCAEcsUDH6uvP6k63llqmjWIso765qCNVcoFstp8jKastLYOrixRoZr
# uhf9xHdsFWyuq69zOuhJRrfVf8y2OMDY7Bz1tqG4QyzfTkx9HmhwwHcK1ALgXGC7
# KP845VJa1qwXIiNO9OzTF/tQa/8Hdx9xl0RBybhG02wyfFgvZ0dl5Rtztpn5aywG
# Ru9BHvDwX+Db2a2QgESvgBBBijCCB2cwggVPoAMCAQICEATd+82EVAN2YngfhA+f
# z/UwDQYJKoZIhvcNAQELBQAwaTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
# ZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2ln
# bmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENBMTAeFw0yMzEwMDQwMDAwMDBaFw0y
# NjExMTUyMzU5NTlaMG8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MREwDwYDVQQHEwhCZWxsZXZ1ZTEbMBkGA1UEChMSUHVyZSBTdG9yYWdlLCBJbmMu
# MRswGQYDVQQDExJQdXJlIFN0b3JhZ2UsIEluYy4wggIiMA0GCSqGSIb3DQEBAQUA
# A4ICDwAwggIKAoICAQCdhXqOLFS3HR5KD2RtAOzGdwKU0mMGGHfU7qUo1YFvDCN8
# vF/X8LDhouGtsZPdIfd298orsXHfXYElTgBo91gba7SqKBWi9xdXTqMR5vpt41K/
# a554AgiQp02nfYwuspZoAGnt//mDJ6ErP1jUFiWuwHsYsxk0gFEayp5xIKzmj3q4
# 9g+AenKpktbDn6HPpXZPdvg+g+GR9lPpiJo7Z40SIqzaacJsVcl5MhPfbFdLeP1s
# n0MBW3BiYLyz4CEUq8IA2vJ2557N0uB0UzWERE31brL0mBn5gB1g8Zij9VsI9J5+
# Q+THKYIgwknlnXFiSwQhQbJ3Cn7IVotei1M/D011XjUR66kNHm02VVDsbxX92xLf
# qIX7BZ0e6shMsOFVakkdM00nXhfRscDkRqEQ+IwgC3vcyJgp/QRX0SfWaaD5G0fi
# ECMBZtmq5hijTJ18MAW2KaFePW0PIn9IRnoXS3tx9coXVJMTFwnLYdIukelF4jIW
# 779IP5lQH7IBNHS01BgysjWVaQhPYxWZYtsxyRUX3gVRjFChhOtBNCAy2S+YYjUS
# TOM7CdUNTtCARX/HgcRYxxU7UTOYXPYyabdQu3mFF8yD5YNkarlgc4TQ+H1PWnIU
# l7pq3P0ZSaE5Est24ApVi6wlZC/Q3jQRKPziRg8x7Zv1TZX8TfxPDmE0Nsd+BwID
# AQABo4ICAzCCAf8wHwYDVR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYD
# VR0OBBYEFCvH/lBQxrVtiuuihv+e6+2VgDPXMD4GA1UdIAQ3MDUwMwYGZ4EMAQQB
# MCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAOBgNV
# HQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwgbUGA1UdHwSBrTCBqjBT
# oFGgT4ZNaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0
# Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwU6BRoE+GTWh0dHA6
# Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5n
# UlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMIGUBggrBgEFBQcBAQSBhzCBhDAkBggr
# BgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFwGCCsGAQUFBzAChlBo
# dHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2Rl
# U2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNydDAJBgNVHRMEAjAAMA0GCSqG
# SIb3DQEBCwUAA4ICAQCrjkZxw1B2w/pYu4siB36x5J9Xate13IDt57bxZvc7OGgz
# limeUq1/HObzW4Vej9tESBpT6a5FBuVSXQXvYQntQczEFBBksRXyad3tx5/xElHA
# LaE6BCZUtPKu3/CSrgsvVi7OgWNybOFWF2Xk9K1djImG55J7jOY+8ZKegUSlHPjB
# 8HF9G4VdS85L2SuFaRzOMTEIW+h0Ihkp6Js1rbe0YLZu/ad6VWFKoX++FDg3cbM8
# FLf482p+XCmuX/qZuFmySYDQQ4jvNiecEiyZ4m6HUryx9Fagc0NBADiOJc1R2p2I
# QbBasyndhn8KWlGSudJ+uCfuzD6ukGVe4kOpYlqkzVeOscetaY0/5v+896yP4FA8
# NS68I2eMuKbis2ouOIrAVkNPdymBjaEW1U6q979upeEG22UjjrRkq5qSdO+nk2tK
# NL1ZIc92bqIs132yuwVZ6A7Dvez03VSitT2UVBMz0BKNy1EnZ4hjqBrApU+Bbcwc
# 7nPV9hKKbEFKCcCNLpkAP8SCVX6r7qMyqYhAl+XKSfCkMpxRD2LykRup5mz54cQP
# RPoy86iVhFhWUez1O3t371sgYulMuxaff5mXK3xlzYZUHpJGkOYntQ2VlqUpl/VO
# KcNTXWnuPOyuUZY0b9tWU0Ofs8Imp7+lULJ7XUbrJoY1bUa22ce912PVBsWOojGC
# BjowggY2AgEBMH0waTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
# bmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBS
# U0E0MDk2IFNIQTM4NCAyMDIxIENBMQIQBN37zYRUA3ZieB+ED5/P9TAJBgUrDgMC
# GgUAoHAwEAYKKwYBBAGCNwIBDDECMAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcC
# AQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYE
# FJ6MilDlc6e6seYigtp6wO/V9EMbMA0GCSqGSIb3DQEBAQUABIICAHdLRr24qt4l
# zw9PzzahHQhZTYuPqZKYvYnHz+8Zi463pW4fkNCQTFPa65mDOl1Euq8MgYHf2vH4
# 8/8L5uUn3d5C4tbBKu55afopKxdrRkpjN16/lvsEBu+BGILy8NBnCjh+msmUP0Wi
# KqVRwFG0rKG9HrcV4sdkKLBtpOY+V2qlREjIL5KdtIcjrCPK+NuBhIsaLovV1V66
# e7bA8hQnFUYw9Is64F22BYh98fY6JMANKd4954Qe7wy+nj10gIk7fWKPS1TEVgGk
# +J60DCVkKNfTfKHWY4yOife++yyPlxAvhnWrtvo7qgNaznHyyS9Sr9zEN44H105I
# 4AveZE5Y0Vaad/ftQJk0/Xw/Bgb7rNPgziVRivpZGqUIDm7M1NOtyVtma+308y/t
# Nkmg/sUa/vQ4PdyYwvwPPioTSzbCICddjChAwRTOHM2fjVV1yvLpDEDbVHW6xuQP
# gAzukmJe44DliMsZQwBc39q7yFj45G6LWyRNg12N0v/Y4g1/xKlC1AghaH6RduiW
# ynrIYoehCMRvPylk7YrwUAb7hA7B7c1SaUeP28w9/4aPTEdnYA5Pn3aIwpppQsy2
# anSlSfAdzZx8G9jr8VPEhaflhbuHFKAspTE2mQ85T6s5kWFokQTWIXGFkMhiMdgP
# DI++5YrQFo/uAcoo0s+rYZApawWqqHZmoYIDIDCCAxwGCSqGSIb3DQEJBjGCAw0w
# ggMJAgEBMHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu
# MTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRp
# bWVTdGFtcGluZyBDQQIQBUSv85SdCDmmv9s/X+VhFjANBglghkgBZQMEAgEFAKBp
# MBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTIzMTEx
# MDAwMjY0OFowLwYJKoZIhvcNAQkEMSIEIHFF0bA5A1lh5CIML8h4ziqsTlAVKinT
# mmxiAALmmu62MA0GCSqGSIb3DQEBAQUABIICAGS4ddJHBs6cciNBhrsqiJPPkn/s
# NYJ3LaQXJjOlN2mkRM1T7ihkqyveHLPDM/h8FWL+qWWijKpD5F95oT4KrDQWOnPn
# W84GPm8TkIObQmLkAtrTi5nyKl2LlovTQT/AkrPGQACqhWEXKzfcGL80KvDgiEGz
# kOe0EGV/LWJxTPbNXQ5TgaytsTJuuJFleffuajin1/Fbdw4mhztCQXygWmBRp77D
# Ji/VHF6uUy2adhuPAOc3UxIDQPIz8HkIRgQ5ntneLLEySioyVXtCyFI9S+tnl2hE
# 8xU+lYm+BQjHfZYo0Kie4j0KlD1BSV3/+Fy3lGvXXeOx/21zm9lTDVmRnWwxT1zz
# WogSVtLYEBo7/rBVB1wH6yh6P/XwBrrw8tILoqliRcAnJjGolZjUPuwwvHTh1+nZ
# sz6VJ8TaWwyxAvGJIuau9KcJ1CiH3crEsp5B6p3Nc7LEadiRlQ+5HsCjRn4J5tfa
# 3odxavsfeFL95dN6iqiaKTgQwmUyr36qPV/AERFEL55dDT4y3aKqybBJEL+XVmmx
# i+k3yp37MVDSAAgP/09wgV3KNLUUUgYXJ4t8IQKJj1WANIf2KWPgK/FTD/1KcPwO
# vIjZyetiFW0Z56AvKvVNDG/roNkbfI/0JwWDJrumeaB2whDUjaULcowAMPGb9P1l
# WIqetBz8TWFnkFxU
# SIG # End signature block