PureStorage.FlashArray.VMware.VMFS.psm1

function get-pfaVolfromVMFS {
    <#
    .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: 2.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 05/26/2019
      Purpose/Change: Updated for new connection mgmt
   
    *******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,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray[]]$flasharray
    )
    if ($datastore.Type -ne 'VMFS')
    {
        throw "This is not a VMFS datastore."
    }
    if ($null -eq $flasharray)
    {
        $fa = get-pfaConnectionOfDatastore -datastore $datastore
    }
    else {
      $fa = get-pfaConnectionOfDatastore -datastore $datastore -flasharrays $flasharray
    }
    $pureVolumes = Get-PfaVolumes -Array  $fa
    $lun = $datastore.ExtensionData.Info.Vmfs.Extent.DiskName |select-object -unique
    $volserial = ($lun.ToUpper()).substring(12)
    $purevol = $purevolumes | where-object { $_.serial -eq $volserial }
    if ($null -ne $purevol.name)
    {
        return $purevol
    }
    else {
        throw "The volume was not found."
    }
}
function new-pfaVolVmfs {
    <#
    .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: 2.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 05/26/2019
      Purpose/Change: Updated for new connection mgmt
   
    *******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)]
            [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$cluster,

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

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

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

            [Parameter(Position=4)]
            [int]$sizeInTB
    )
    Begin {
        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
        }
        $newDatastores = @()
        $allFAs = @()
        $newNAAs = @()
        $volNames = @()
        $hostGroupNames = @()
        $oneVolume = 0
    }
    Process 
    {
        if ($null -eq $flasharray)
        {
          $flasharray = checkDefaultFlashArray
        }
        foreach ($fa in $flasharray)
        {
          try {
            $hostGroup = $cluster | get-pfaHostGroupfromVcCluster -flasharray $fa
          }  
          catch {
              for ($h =0; $h -lt $volNames.Count; $h++)
              {
                Remove-PfaHostGroupVolumeConnection -Array $allFAs[$h] -VolumeName $volNames[$h] -HostGroupName $hostGroupNames[$h] |Out-Null
                Remove-PfaVolumeOrSnapshot -Array $allFAs[$h] -Name $volNames[$h] |Out-Null
                Remove-PfaVolumeOrSnapshot -Array $allFAs[$h] -Name $volNames[$h] -Eradicate |Out-Null
              }
              $cluster | get-pfaHostGroupfromVcCluster -flasharray $fa -ErrorAction Stop
          }
            if ($oneVolume -gt 0)
            {
              if ($oneVolume -eq 1)
              {
                $nameSuffix = ("-" + (get-random -Maximum 9999 -Minimum 1000))
                Rename-PfaVolumeOrSnapshot -Array $lastFA -Name $volName -NewName ($volName + $nameSuffix) |Out-Null
                $volNames[0] = ($volName + "-" + $nameSuffix)
                $newName = ($volName + "-" + (get-random -Maximum 9999 -Minimum 1000))
              }
              else {
                $newName = ($volName + "-" + (get-random -Maximum 9999 -Minimum 1000))
              }
            }
            else {
              $newName = $volName
            }
            $newVol = New-PfaVolume -Array $fa -Size $volSize -VolumeName $newName -ErrorAction Stop
            $Global:CurrentFlashArray = $fa
            $lastFA = $fa
            New-PfaHostGroupVolumeConnection -Array $fa -VolumeName $newVol.name -HostGroupName $hostGroup.name |Out-Null
            $newNAAs +=  "naa.624a9370" + $newVol.serial.toLower()
            $allFAs += $fa
            $volNames += $newVol.name
            $hostGroupNames += $hostGroup.name 
            $oneVolume++
        }
    }
    End 
    {
        $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
        $ESXiApiVersion = $esxi.ExtensionData.Summary.Config.Product.ApiVersion
        $varCount = 0
        foreach ($newNAA in $newNAAs)
        {  
          Write-Debug -Message $newNAA
            try 
            {
                if (($ESXiApiVersion -eq "5.5") -or ($ESXiApiVersion -eq "6.0") -or ($ESXiApiVersion -eq "5.1"))
                {
                    $newVMFS = $esxi |new-datastore -name $volNames[$varCount] -vmfs -Path $newNAAs[$varCount] -FileSystemVersion 5 -ErrorAction Stop
                }
                else
                {
                    $newVMFS = $esxi |new-datastore -name $volNames[$varCount] -vmfs -Path $newNAAs[$varCount] -FileSystemVersion 6 -ErrorAction Stop
                }
                $newDatastores += $newVMFS
            }
            catch {
                Write-Error $Global:Error[0]
                Remove-PfaHostGroupVolumeConnection -Array $allFAs[$varCount] -VolumeName $volNames[$varCount] -HostGroupName $hostGroupNames[$varCount] |Out-Null
                Remove-PfaVolumeOrSnapshot -Array $allFAs[$varCount] -Name $volNames[$varCount] |Out-Null
                Remove-PfaVolumeOrSnapshot -Array $allFAs[$varCount] -Name $volNames[$varCount] -Eradicate |Out-Null
            }
            $varCount++
        }
        
      return $newDatastores
    }
}
function add-pfaVolVmfsToCluster {
    <#
    .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: 2.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 05/26/2019
      Purpose/Change: Updated for new connection mgmt
   
    *******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)]
            [PurePowerShell.PureArray[]]$flasharray,

            [Parameter(Position=2,mandatory=$true)]
            [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]$datastore
    )
    Begin {
        $faConnections = @()
        if ($null -eq $flasharray)
        {
          $fa = get-pfaConnectionOfDatastore -datastore $datastore -ErrorAction Stop
        }
        else 
        {
          $fa = get-pfaConnectionOfDatastore -datastore $datastore -flasharrays $flasharray -ErrorAction Stop
        }
    }
    Process 
    {
        foreach ($cs in $cluster)
        {
            $pureVol = $datastore | get-pfaVolfromVMFS -flasharray $fa -ErrorAction Stop
            $hostGroup = get-pfaHostGroupfromVcCluster -flasharray $fa -ErrorAction Stop -cluster $cs
            if ($hostGroup.count -gt 1)
            {
              throw "This cluster spans more than one host group, please ensure this is a 1:1 relationship."
            }
            else 
            {
              try {
                $faConnection = New-PfaHostGroupVolumeConnection -Array $fa -VolumeName $pureVol.name -HostGroupName $hostGroup.name -ErrorAction Stop
              }
              catch {
                Write-Error $Global:Error[0]
                continue 
              }
              $cs| Get-VMHost | Get-VMHostStorage -RescanAllHba -RescanVmfs -ErrorAction Stop |Out-Null
              $faConnections += $faConnection
              $Global:CurrentFlashArray = $fa
            } 
        }
    }
    End 
    {
        return $faConnections
    }
}
function set-pfaVolVmfsCapacity {
    <#
    .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: 2.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 05/26/2019
      Purpose/Change: Updated for new connection mgmt
   
    *******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)]
            [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 ($null -eq $flasharray)
    {
        $fa = get-pfaConnectionOfDatastore -datastore $datastore -ErrorAction Stop
    }
    else 
    {
        $fa = get-pfaConnectionOfDatastore -datastore $datastore -flasharrays $flasharray -ErrorAction Stop
    }
    $pureVol = $datastore | get-pfaVolfromVMFS -flasharray $fa -ErrorAction Stop
    if ($volSize -le $pureVol.size)
    {
        throw "The new size cannot be smaller than the existing size. VMFS volumes cannot be shrunk."
    }
    Resize-PfaVolume -Array $fa -VolumeName $pureVol.name -NewSize $volSize -ErrorAction Stop |Out-Null
    $Global:CurrentFlashArray = $fa
    $datastore| Get-VMHost | Get-VMHostStorage -RescanAllHba -RescanVmfs -ErrorAction Stop  -WarningAction SilentlyContinue |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)
    $ds = get-datastore -Id $expandedDS
    return $ds
}
function get-pfaVolVmfsSnapshot {
    <#
    .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: 2.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 05/26/2019
      Purpose/Change: Updated for new connection mgmt
   
    *******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)]
            [PurePowerShell.PureArray[]]$flasharray,

            [Parameter(Position=1,mandatory=$true,ValueFromPipeline=$True)]
            [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]$datastore
    )
    if ($null -eq $flasharray)
    {
        $fa = get-pfaConnectionOfDatastore -datastore $datastore -ErrorAction Stop
    }
    else 
    {
        $fa = get-pfaConnectionOfDatastore -datastore $datastore -flasharrays $flasharray -ErrorAction Stop
    }
    $pureVol = $datastore | get-pfaVolfromVMFS -flasharray $fa -ErrorAction Stop
    $volSnapshots = Get-PfaVolumeSnapshots -Array $fa -VolumeName $pureVol.name 
    $Global:CurrentFlashArray = $fa
    return $volSnapshots
}
function new-pfaVolVmfsSnapshot {
    <#
    .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: 2.0
      Author: Cody Hosterman https://codyhosterman.com
      Creation Date: 05/26/2019
      Purpose/Change: Updated for new connection mgmt
   
    *******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)]
            [PurePowerShell.PureArray[]]$flasharray,

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

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

            [Parameter(Position=3)]
            [string]$suffix
    )
    Begin 
    {
        if (("" -ne $SnapName) -and ("" -ne $suffix))
        {
            throw "Please only enter in the suffix, the snapName parameter is being deprecated."
        }
        elseif ("" -ne $Snapname) {
          Write-Warning -Message "The snapName parameter is being deprecated--please use the suffix parameter instead."  
          $suffix = $SnapName        
        }
        $newSnapshots = @()
    }
    Process 
    {
        foreach ($ds in $datastore)
        {
            if ($null -eq $flasharray)
            {
                $fa = get-pfaConnectionOfDatastore -datastore $ds -ErrorAction Stop
            }
            else 
            {
                $fa = get-pfaConnectionOfDatastore -datastore $ds -flasharrays $flasharray -ErrorAction Stop
            }
            $pureVol = $ds | get-pfaVolfromVMFS -flasharray $fa -ErrorAction Stop
            $Global:CurrentFlashArray = $fa
            if ($suffix -ne "")
            {
              $newSnapshots += New-PfaVolumeSnapshots -Array $fa -Sources $pureVol.name -Suffix $suffix
            }
            else {
              $newSnapshots += New-PfaVolumeSnapshots -Array $fa -Sources $pureVol.name 
            }
            
        }
    }
    End {
      return $newSnapshots
    }
}
function new-pfaVolVmfsFromSnapshot {
    <#
    .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: Updated for new connection mgmt
   
    *******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,ValueFromPipeline=$True)]
            [PurePowerShell.PureArray]$flasharray,

            [Parameter(Position=2,mandatory=$true)]
            [string]$snapName
    )
    $volumeName = $snapName.split(".")[0] + "-snap-" + (Get-Random -Minimum 1000 -Maximum 9999)
    $newVol =New-PfaVolume -Array $flasharray -Source $snapName -VolumeName $volumeName -ErrorAction Stop
    $hostGroup = $flasharray |get-pfaHostGroupfromVcCluster -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 checkDefaultFlashArray{
    if ($null -eq $Global:DefaultFlashArray)
    {
        throw "You must pass in a FlashArray connection or create a default FlashArray connection with new-pfaconnection"
    }
    else 
    {
        return $Global:DefaultFlashArray
    }
}
function getAllFlashArrays {
  if ($null -ne $Global:AllFlashArrays)
  {
      return $Global:AllFlashArrays
  }
  else
  {
      throw "Please either pass in one or more FlashArray connections or create connections via the new-pfaConnection cmdlet."
  }
}
New-Alias -Name get-faVolfromVMFS -Value get-pfaVolfromVMFS
New-Alias -Name new-faVolVmfs -Value new-pfaVolVmfs
New-Alias -Name add-faVolVmfsToCluster -Value add-pfaVolVmfsToCluster
New-Alias -Name set-faVolVmfsCapacity -Value set-pfaVolVmfsCapacity
New-Alias -Name get-faVolVmfsSnapshots -Value get-pfaVolVmfsSnapshot
New-Alias -Name new-faVolVmfsSnapshot -Value new-pfaVolVmfsSnapshot
New-Alias -Name new-faVolVmfsFromSnapshot -Value new-pfaVolVmfsFromSnapshot