PureStorage.CBS.AVS.psm1

. $PSScriptRoot/PureStorage.PhoneHomeLogger.ps1
. $PSScriptRoot/PureStorage.Logger.ps1
. $PSScriptRoot/PureStorage.CommonUtil.ps1
. $PSScriptRoot/PureStorage.RunCommandLauncher.ps1
. $PSScriptRoot/PureStorage.CBS.AVS.VMFS.ps1
. $PSScriptRoot/PureStorage.CBS.AVS.Configuration.ps1
. $PSScriptRoot/PureStorage.CBS.AVS.Monitor.ps1

$DEFAULT_UTILIZATION_THRESHOLD = 80
$DEFAULT_RUNCOMMAND_TIMEOUT_IN_MINUTE = 10

function Build-PCBSCluster {
  <#
    .SYNOPSIS
     Build or update settings for a cluster of ESXi servers
    .DESCRIPTION
     Build or update settings for a cluster of ESXi servers. Creates a hostgroup in Pure Cloud Block Store if it does not exists and
     updates iSCSI settings. Can be used when creating a new cluster or when adding hosts to a cluster.
    .PARAMETER ClusterName
     Cluster name
    .PARAMETER PureCloudBlockStoreConnection
     Optional. Pure Cloud Block Store Connection. The connection can be created using Connect-Pf2Array cmdlet
    .PARAMETER AVSCloudName
     AVS cloud name
    .PARAMETER AVSResourceGroup
     AVS Resource group name
    .PARAMETER SkipMPIOCheck
     Optional. Skip MPIO check. If the parameter is specified, the MPIO check will be skipped. Please use only if directed by Pure Storage support.
    .PARAMETER TimeoutInMinutes
     Optional. Timeout in minutes for RunCommand operations.
    .EXAMPLE
    Build-PCBSCluster -ClusterName "mycluster" -PureCloudBlockStoreConnection $CBSConnection `
      -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup
    #>

  [CmdletBinding()]
  Param (
    [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
    [String]$ClusterName,

    [Parameter(Mandatory = $false)]
    $PureCloudBlockStoreConnection,

    [Parameter(Mandatory = $true)]
    [String]$AVSCloudName,

    [Parameter(Mandatory = $true)]
    [String]$AVSResourceGroup,

    [Parameter(Mandatory = $false)]
    [switch] $SkipMPIOCheck,

        [Parameter(Mandatory=$false)]
        [ValidateRange(5, 60)]
        [int] $TimeoutInMinutes
    )

  Write-Progress -Activity "Building cluster" -Status "0% Complete:" -PercentComplete 1

  $fa = Connect-PureCloudBlockStore -PureCloudBlockStoreConnection $PureCloudBlockStoreConnection
  $logger = (New-Object -TypeName 'PhoneHomeLogger' -ArgumentList $fa)
  $WorkflowID = New-WorkflowID
  New-PhoneHomeWorkflowLogEntry -RestClient $fa -Event "Begin" -ID $WorkflowID -Name $MyInvocation.MyCommand

  try {
    $vCenterServer = Connect-AVSvCenter -AVSResourceGroupName $AVSResourceGroup -AVSPrivateCloudName $AVSCloudName -Logger $logger
    $cluster = Get-Cluster -Server $vCenterServer -Name $ClusterName
    if (-not $cluster) {
      throw "Could not find cluster '$ClusterName'..."
    }

    $TimeoutInMinutes = Get-Timeout -Cluster $cluster -InputParams $PSBoundParameters

    Test-AVSProvisioningState -AvsResourceGroupName $AVSResourceGroup -AvsPrivateCloudName $AVSCloudName -AvsClusterName $ClusterName -Logger $logger

    if (-not $SkipMPIOCheck) {
      Test-MPIOProvisionStatus -AvsResourceGroupName $AVSResourceGroup -AvsPrivateCloudName $AVSCloudName -Cluster $cluster -Logger $logger
    }
    else {
      $logger.LogWarning("Skipping check for MPIO status ...")
    }


        Write-Progress -Activity "Configuring iSCSI" -Status "25% Complete:" -PercentComplete 25
        $updated_hosts = New-PfaHostGroupfromVcCluster -FlashArray $fa -Cluster $cluster `
        -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup -TimeoutInMinutes $TimeoutInMinutes -Logger $logger
        Write-Progress -Activity "Removing unused hosts" -Status "50% Complete:" -PercentComplete 50
        Remove-PfaUnusedHosts -FlashArray $fa -Cluster $cluster -Logger $logger | Out-Null

        Write-Progress -Activity "Refreshing iSCSI targets" -Status "75% Complete:" -PercentComplete 75
        if ($updated_hosts) {
            $ethList = (Get-Pfa2NetworkInterface -Array $FlashArray | Where-Object {$_.services -eq "iscsi"} | Where-Object {$_.enabled -eq $true} | Where-Object {$null -ne $_.Eth.address}).Eth
            $ISCSIAddressList = @()
            foreach ($eth in $ethList) {
            $ISCSIAddressList += $eth.address
            }

            $params = @{
                ClusterName = $ClusterName
                # Need to join the list as string as RunCommand does not support array type
                ISCSIAddress = $ISCSIAddressList -join ","
            }

            Invoke-RunScript -RunCommandName  "Remove-VMHostStaticiSCSITargets" -RunCommandModule "Microsoft.AVS.VMFS" -Parameters $params `
                -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup -TimeoutInMinutes $TimeoutInMinutes -Logger $logger
        }
        $logger.LogInfo("Cluster '$ClusterName' is successfully built")
        Write-Progress -Activity "Operation is done" -Status "100% Complete:" -PercentComplete 100
    }
    catch {
        New-PhoneHomeWorkflowLogEntry -RestClient $fa -Event "Error" -ID $WorkflowID -Name $MyInvocation.MyCommand -ErrorMessage $_
        throw
    }
    finally {
        $logger.Flush()
    }
    New-PhoneHomeWorkflowLogEntry -RestClient $fa -Event "Complete" -ID $WorkflowID -Name $MyInvocation.MyCommand
    Disconnect-VIServer -Server $vCenterServer -Confirm:$false -ErrorAction SilentlyContinue
}

function New-PCBSVmfsDatastore {
    <#
    .SYNOPSIS
      Creates a new VMFS datastore and mounts to a VMware cluster
    .DESCRIPTION
      Creates a new VMFS datastore and mounts to a VMware cluster
    .PARAMETER ClusterName
      Cluster name
    .PARAMETER DatastoreName
      Datastore name
    .PARAMETER Size
      Datastore capacity size in bytes
    .PARAMETER PodName
     Optional. Pod name. If the parameter is specified, the backing volume for the datastore will be created in the specified Pod.
    .PARAMETER NoDefaultProtection
     Optional. Bypass default protection group. If the parameter is specified, the datastore will not be protected by default protection group
    .PARAMETER PureCloudBlockStoreConnection
     Optional. Pure Cloud Block Store Connection. The connection can be created using Connect-Pf2Array cmdlet
    .PARAMETER AVSCloudName
     AVS cloud name
    .PARAMETER AVSResourceGroup
     AVS Resource group name
    .PARAMETER TimeoutInMinutes
      Optional. Timeout in minutes for RunCommand operations.

    .EXAMPLE
      New-PCBSVmfsDatastore -ClusterName myClusterName -PureCloudBlockStoreConnection $CBSConnection -DatastoreName MyVMFSStore -Size 4GB `
        -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup

      Create a datastore "MyVMFS"
    #>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)]
        [String]$ClusterName,

        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [String]$DatastoreName,

        [ValidateRange(1GB,64TB)] # 1 GB to 64 TB
        [Parameter(Mandatory=$true)]
        [UInt64]$Size,

        [Parameter(Mandatory=$false)]
        [string]$PodName,

        [Parameter(Mandatory=$false)]
        [switch] $NoDefaultProtection,

        [Parameter(Mandatory=$false)]
        $PureCloudBlockStoreConnection,

        [Parameter(Mandatory=$true)]
        [String]$AVSCloudName,

        [Parameter(Mandatory=$true)]
        [String]$AVSResourceGroup,

        [Parameter(Mandatory=$false)]
        [ValidateRange(5, 60)]
        [int] $TimeoutInMinutes
    )
    Write-Progress -Activity "Creating datastore" -Status "0% Complete:" -PercentComplete 1

    $fa = Connect-PureCloudBlockStore -PureCloudBlockStoreConnection $PureCloudBlockStoreConnection
    $logger = (New-Object -TypeName 'PhoneHomeLogger' -ArgumentList $fa)
    $WorkflowID = New-WorkflowID
    New-PhoneHomeWorkflowLogEntry -RestClient $fa -Event "Begin" -ID $WorkflowID -Name $MyInvocation.MyCommand

    try {
        $vCenterServer = Connect-AVSvCenter -AVSResourceGroupName $AVSResourceGroup -AVSPrivateCloudName $AVSCloudName -Logger $logger
        $Cluster = Get-Cluster -Server $vCenterServer -Name $ClusterName -ErrorAction Ignore
        if (-not $Cluster) {
            throw "Cluster $ClusterName does not exist."
        }

        Test-AVSProvisioningState -AvsResourceGroupName $AVSResourceGroup -AvsPrivateCloudName $AVSCloudName -AvsClusterName $ClusterName -Logger $logger
        $TimeoutInMinutes = Get-Timeout -Cluster $Cluster -InputParams $PSBoundParameters
        New-PfaVmfs -Cluster $Cluster -Flasharray $fa -Name $DatastoreName -vCenterServer $vCenterServer -Size $Size -NoDefaultProtection:$NoDefaultProtection.IsPresent `
            -PodName $PodName -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup -TimeoutInMinutes $TimeoutInMinutes -Logger $logger

        $logger.LogInfo("Datastore '$DatastoreName' is successfully created")
        Write-Progress -Activity "Operation is done" -Status "100% Complete:" -Completed
    } catch {
        New-PhoneHomeWorkflowLogEntry -RestClient $fa -Event "Error" -ID $WorkflowID -Name $MyInvocation.MyCommand -ErrorMessage $_
        throw
    }
    finally {
        $logger.Flush()
    }
    New-PhoneHomeWorkflowLogEntry -RestClient $fa -Event "Complete" -ID $WorkflowID -Name $MyInvocation.MyCommand
    Disconnect-VIServer -Server $vCenterServer -Confirm:$false -ErrorAction SilentlyContinue
}

function Restore-PCBSVmfsDatastore {
  <#
   .SYNOPSIS
     Mounts a copy of a VMFS datastore to a VMware cluster from a Pure Cloud Block Store snapshot, volume, or pod.
   .DESCRIPTION
     Takes in a snapshot/volume/pod name, the corresponding Pure Cloud Block Store, and a cluster. The VMFS copy will be resignatured and mounted.
   .PARAMETER ClusterName
     Cluster name
   .PARAMETER VolumeSnapshotName
     Volume snapshot name. A volume will be created from the volume snapshot. A datastore will be created from the volume
   .PARAMETER VolumeName
     Volume name. A datastore will be created from the volume. No volume copy will be created, the volume specified will be directly used for the datastore
   .PARAMETER ProtectionGroupSnapshotName
     Protection group snapshot name. All of volume snapshots of the protection group snapshot will be used for restoring. The snapshot will be skipped if not supported.
   .PARAMETER PodName
     Pod name. All of volumes of the pod will be used for restoring. The volume will be skipped if not supported
   .PARAMETER PureCloudBlockStoreConnection
     Optional. Pure Cloud Block Store Connection. The connection can be created using Connect-Pf2Array cmdlet
   .PARAMETER DatastoreName
     Optional. Datastore name. If the parameter is not specified, a generated name will be used.
   .PARAMETER AVSCloudName
     AVS cloud name
   .PARAMETER AVSResourceGroup
     AVS Resource group name
    .PARAMETER TimeoutInMinutes
     Optional. Timeout in minutes for RunCommand operations.

   .EXAMPLE
     Restore-PCBSVmfsDatastore -ClusterName myClusterName -VolumeSnapshotName mySnapshotName -PureCloudBlockStoreConnection $CBSConnection `
       -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup

      Takes in a snapshot name, the corresponding Pure Cloud Block Store, and a cluster. The VMFS copy will be resignatured and mounted.
    .EXAMPLE
      Restore-PCBSVmfsDatastore -ClusterName myClusterName -VolumeName myVolumeName -PureCloudBlockStoreConnection $CBSConnection `
        -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup

      Takes in a volume name, the corresponding Pure Cloud Block Store, and a cluster. The VMFS copy will be resignatured and mounted.
    #>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)]
        [String]$ClusterName,

        [Parameter(Mandatory = $true, ParameterSetName = 'VolumeSnapshot')]
        [String]$VolumeSnapshotName,

        [Parameter(Mandatory = $true, ParameterSetName = 'Volume')]
        [String]$VolumeName,

        [Parameter(Mandatory = $true, ParameterSetName = 'ProtectionGroupSnapshot')]
        [String]$ProtectionGroupSnapshotName,

        [Parameter(Mandatory = $true, ParameterSetName = 'Pod')]
        [String]$PodName,

        [Parameter(Mandatory = $false, ParameterSetName = 'Volume')]
        [Parameter(Mandatory = $false, ParameterSetName = 'VolumeSnapshot')]
        [String]$DatastoreName,

        [Parameter(Mandatory=$false)]
        $PureCloudBlockStoreConnection,

        [Parameter(Mandatory=$true)]
        [String]$AVSCloudName,

    [Parameter(Mandatory = $true)]
    [String]$AVSResourceGroup,

    [Parameter(Mandatory = $false)]
    [ValidateRange(5, 60)]
    [int] $TimeoutInMinutes
  )
  Write-Progress -Activity "Restoring datastore" -Status "0% Complete:" -PercentComplete 1

    $fa = Connect-PureCloudBlockStore -PureCloudBlockStoreConnection $PureCloudBlockStoreConnection
    $logger = (New-Object -TypeName 'PhoneHomeLogger' -ArgumentList $fa)
    $WorkflowID = New-WorkflowID
    New-PhoneHomeWorkflowLogEntry -RestClient $fa -Event "Begin" -ID $WorkflowID -Name $MyInvocation.MyCommand

    try {
        $vCenterServer = Connect-AVSvCenter -AVSResourceGroupName $AVSResourceGroup -AVSPrivateCloudName $AVSCloudName -Logger $logger
        $Cluster = Get-Cluster -Server $vCenterServer -Name $ClusterName -ErrorAction Ignore
        if (-not $Cluster) {
            throw "Cluster '$ClusterName' does not exist."
        }

        Test-AVSProvisioningState -AvsResourceGroupName $AVSResourceGroup -AvsPrivateCloudName $AVSCloudName -AvsClusterName $ClusterName -Logger $logger

        if (-not [string]::IsNullOrEmpty($DatastoreName)) {
        $Datastore = Get-Datastore -Server $vCenterServer -Name $DatastoreName -ErrorAction Ignore
            if ($Datastore) {
                throw "Datastore '$Datastore' already exists."
            }
        }

    $TimeoutInMinutes = Get-Timeout -Cluster $cluster -InputParams $PSBoundParameters

    switch ($PSCmdlet.ParameterSetName) {
      'Volume' {
        $logger.LogInfo("Creating datastore from volume...")
        $NewDatastore = Restore-PfaVmfsFromVolume -FlashArray $fa -Cluster $Cluster -VolumeName $VolumeName -DatastoreName $DatastoreName `
          -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup -TimeoutInMinutes $TimeoutInMinutes -Logger $logger

        break
      }
      'VolumeSnapshot' {
        $logger.LogInfo("Creating datastore from volume snapshot...")
        $NewDatastore = Restore-PfaVmfsFromVolumeSnapshot -FlashArray $fa -Cluster $Cluster -VolumeSnapshotName $VolumeSnapshotName -DatastoreName $DatastoreName `
          -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup -TimeoutInMinutes $TimeoutInMinutes -Logger $logger

        break
      }
      'ProtectionGroupSnapshot' {
        $logger.LogInfo("Creating datastore from protection group snapshot...")
        $NewDatastore = Restore-PfaVmfsFromProtectionGroupSnapshot -FlashArray $fa -Cluster $Cluster -ProtectionGroupSnapshotName $ProtectionGroupSnapshotName `
          -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup -TimeoutInMinutes $TimeoutInMinutes -Logger $logger
        break
      }
      'Pod' {
        $logger.LogInfo("Creating datastore from pod...")
        $NewDatastore = Restore-PfaVmfsFromPod -FlashArray $fa -Cluster $Cluster -PodName $PodName `
          -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup -TimeoutInMinutes $TimeoutInMinutes -Logger $logger
        break
      }
    }
    Write-Progress -Activity "Operation is done" -Status "100% Complete:" -Completed

        $logger.LogInfo("Datastore '$($NewDatastore.Name)' is successfully restored")
    } catch {
        New-PhoneHomeWorkflowLogEntry -RestClient $fa -Event "Error" -ID $WorkflowID -Name $MyInvocation.MyCommand -ErrorMessage $_
        throw
    }
    finally {
        $logger.Flush()
    }
    New-PhoneHomeWorkflowLogEntry -RestClient $fa -Event "Complete" -ID $WorkflowID -Name $MyInvocation.MyCommand
    Disconnect-VIServer -Server $vCenterServer -Confirm:$false -ErrorAction SilentlyContinue
    return $NewDatastore
}


function Remove-PCBSVmfsDatastore {
  <#
    .SYNOPSIS
     Detaches and unmount a VMFS datastore from a cluster. Remove the datastore from the host group
    .DESCRIPTION
     Detaches and unmount a VMFS datastore from a cluster. Remove the datastore from the host group. The connection configured for the volume and cluster host/host group will be removed from Pure Cloud Block Store. The volume will be destroyed if there is no other connection left.
    .PARAMETER ClusterName
     Cluster name
    .PARAMETER DatastoreName
     Datastore name
    .PARAMETER PureCloudBlockStoreConnection
     Optional. Pure Cloud Block Store Connection. The connection can be created using Connect-Pf2Array cmdlet
    .PARAMETER AVSCloudName
     AVS cloud name
    .PARAMETER AVSResourceGroup
     AVS Resource group name
    .PARAMETER TimeoutInMinutes
    Optional. Timeout in minutes for RunCommand operations.
    .EXAMPLE
     Remove-PCBSVmfsDatastore -ClusterName "mycluster" -DatastoreName "myDatastore" PureCloudBlockStoreConnection $CBSConnection `
        -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup

     Takes in a datastore name, datastore would be removed.
    #>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)]
        [String]$ClusterName,

        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [String]$DatastoreName,

        [Parameter(Mandatory=$false)]
        $PureCloudBlockStoreConnection,

        [Parameter(Mandatory=$true)]
        [String]$AVSCloudName,

        [Parameter(Mandatory=$true)]
        [String]$AVSResourceGroup,

        [Parameter(Mandatory=$false)]
        [ValidateRange(5, 60)]
        [int] $TimeoutInMinutes
    )
    Write-Progress -Activity "Removing datastore" -Status "0% Complete:" -PercentComplete 1
    $FlashArray = Connect-PureCloudBlockStore -PureCloudBlockStoreConnection $PureCloudBlockStoreConnection
    $logger = (New-Object -TypeName 'PhoneHomeLogger' -ArgumentList $FlashArray)
    $WorkflowID = New-WorkflowID
    New-PhoneHomeWorkflowLogEntry -RestClient $FlashArray -Event "Begin" -ID $WorkflowID -Name $MyInvocation.MyCommand

    try {
        $vCenterServer = Connect-AVSvCenter -AVSResourceGroupName $AVSResourceGroup -AVSPrivateCloudName $AVSCloudName -Logger $logger
        $Cluster = Get-Cluster -Server $vCenterServer -Name $ClusterName -ErrorAction Ignore
        if (-not $Cluster) {
            throw "Cluster '$ClusterName' does not exist."
        }

        $TimeoutInMinutes = Get-Timeout -Cluster $cluster -InputParams $PSBoundParameters

        Test-AVSProvisioningState -AvsResourceGroupName $AVSResourceGroup -AvsPrivateCloudName $AVSCloudName -AvsClusterName $ClusterName -Logger $logger

        $Datastore = Get-Datastore -Server $vCenterServer -Name $DatastoreName -ErrorAction Ignore
        if (-not $Datastore) {
            throw "Datastore '$DatastoreName' does not exist."
        }
        Remove-PfaVmfsDatastore -Cluster $Cluster -Datastore $DataStore -FlashArray $FlashArray `
            -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup -TimeoutInMinutes $TimeoutInMinutes -Logger $logger

        $logger.LogInfo("Datastore '$DatastoreName' is successfully removed")
        Write-Progress -Activity "Operation is done" -Status "100% Complete:" -Completed
    } catch {
        New-PhoneHomeWorkflowLogEntry -RestClient $FlashArray -Event "Error" -ID $WorkflowID -Name $MyInvocation.MyCommand -ErrorMessage $_
        throw
    }
    finally {
        $logger.Flush()
    }
    New-PhoneHomeWorkflowLogEntry -RestClient $FlashArray -Event "Complete" -ID $WorkflowID -Name $MyInvocation.MyCommand
    Disconnect-VIServer -Server $vCenterServer -Confirm:$false -ErrorAction SilentlyContinue
}


function Set-PCBSVmfsCapacity {
  <#
    .SYNOPSIS
     Increase the size of a Pure Cloud Block Store-based VMFS datastore.
    .DESCRIPTION
     Takes in a datastore, the corresponding Pure Cloud Block Store, and a new size. Both the volume and the VMFS will be grown.
    .PARAMETER ClusterName
     Cluster name
    .PARAMETER DatastoreName
     Datastore name
    .PARAMETER Size
     New datastore capacity size in bytes
    .PARAMETER PureCloudBlockStoreConnection
     Optional. Pure Cloud Block Store Connection. The connection can be created using Connect-Pf2Array cmdlet
    .PARAMETER AVSCloudName
     AVS cloud name
    .PARAMETER AVSResourceGroup
     AVS Resource group name
    .PARAMETER TimeoutInMinutes
      Optional. Timeout in minutes for RunCommand operations.
    .EXAMPLE
     Set-PCBSVmfsCapacity -ClusterName "mycluster" -DatastoreName myDatastore -Size 3GB -PureCloudBlockStoreConnection $CBSConnection `
        -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup

     Expand size of the datastore myDatastore to 3GB.
    #>

    [CmdletBinding()]
    Param (
          [Parameter(Mandatory=$true)]
          [String]$ClusterName,

          [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
          [string]$DatastoreName,

          [ValidateRange(1GB,64TB)] # 1 GB to 64 TB
          [Parameter(Mandatory=$true)]
          [UInt64]$Size,

          [Parameter(Mandatory=$false)]
          $PureCloudBlockStoreConnection,

          [Parameter(Mandatory=$true)]
          [String]$AVSCloudName,

          [Parameter(Mandatory=$true)]
          [String]$AVSResourceGroup,

          [Parameter(Mandatory=$false)]
          [ValidateRange(5, 60)]
          [int]$TimeoutInMinutes
    )
    Write-Progress -Activity "Resizing datastore" -Status "0% Complete:" -PercentComplete 1

    $FlashArray = Connect-PureCloudBlockStore -PureCloudBlockStoreConnection $PureCloudBlockStoreConnection
    $logger = (New-Object -TypeName 'PhoneHomeLogger' -ArgumentList $FlashArray)
    $WorkflowID = New-WorkflowID
    New-PhoneHomeWorkflowLogEntry -RestClient $FlashArray -Event "Begin" -ID $WorkflowID -Name $MyInvocation.MyCommand

    try {
        $vCenterServer = Connect-AVSvCenter -AVSResourceGroupName $AVSResourceGroup -AVSPrivateCloudName $AVSCloudName -Logger $logger
        $Cluster = Get-Cluster -Server $vCenterServer -Name $ClusterName -ErrorAction Ignore
        if (-not $Cluster) {
            throw "Cluster '$ClusterName' does not exist."
        }

        $TimeoutInMinutes = Get-Timeout -Cluster $cluster -InputParams $PSBoundParameters

        Test-AVSProvisioningState -AvsResourceGroupName $AVSResourceGroup -AvsPrivateCloudName $AVSCloudName -AvsClusterName $ClusterName -Logger $logger

        $Datastore = Get-Datastore -Server $vCenterServer -Name $DatastoreName -ErrorAction SilentlyContinue
        if (-not $Datastore) {
            throw "Could not find datastore '$DatastoreName'! Please make sure to select an existing datastore."
        }
        Set-PfaVmfsCapacity -Cluster $Cluster -FlashArray $FlashArray -Datastore $Datastore -SizeInByte $Size `
            -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup -TimeoutInMinutes $TimeoutInMinutes -Logger $logger

        $logger.LogInfo("Datastore '$DatastoreName' is successfully resized")
        Write-Progress -Activity "Operation is done" -Status "100% Complete:" -Completed
    } catch {
        New-PhoneHomeWorkflowLogEntry -RestClient $FlashArray -Event "Error" -ID $WorkflowID -Name $MyInvocation.MyCommand -ErrorMessage $_
        throw
    }
    finally {
        $logger.Flush()
    }
    New-PhoneHomeWorkflowLogEntry -RestClient $FlashArray -Event "Complete" -ID $WorkflowID -Name $MyInvocation.MyCommand
    Disconnect-VIServer -Server $vCenterServer -Confirm:$false -ErrorAction SilentlyContinue
}

function Sync-PCBSClusterVMHostStorage {
  <#
   .SYNOPSIS
    Refresh the storage of all hosts in a cluster
   .DESCRIPTION
    Refresh the storage of all hosts in a cluster. This operation is useful when the storage of the hosts is out of sync with the storage of the cluster. For example, a new host is added to the cluster, but the existing datastore does not appear on the new host.
   .PARAMETER ClusterName
    Cluster name
   .PARAMETER PureCloudBlockStoreConnection
    Optional. Pure Cloud Block Store Connection. The connection can be created using Connect-Pfa2Array cmdlet
   .PARAMETER AVSCloudName
    AVS cloud name
   .PARAMETER AVSResourceGroup
    AVS Resource group name
   .PARAMETER TimeoutInMinutes
     Optional. Timeout in minutes for RunCommand operations.
   .EXAMPLE
    Sync-PCBSClusterVMHostStorage -ClusterName "mycluster" -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup
   #>

   [CmdletBinding()]
   Param (
     [Parameter(Mandatory = $true)]
     [String]$ClusterName,

     [Parameter(Mandatory = $false)]
     $PureCloudBlockStoreConnection,

     [Parameter(Mandatory = $true)]
     [String]$AVSCloudName,

     [Parameter(Mandatory = $true)]
     [String]$AVSResourceGroup,

     [Parameter(Mandatory = $false)]
     [ValidateRange(5, 60)]
     [int]$TimeoutInMinutes = 10
   )
    Write-Progress -Activity "Syncing cluster VM host storage" -Status "0% Complete:" -PercentComplete 1

    $fa = Connect-PureCloudBlockStore -PureCloudBlockStoreConnection $PureCloudBlockStoreConnection
    $logger = (New-Object -TypeName 'PhoneHomeLogger' -ArgumentList $fa)

    try {
        $params = @{
          ClusterName = $ClusterName;
        }

        Invoke-RunScript -RunCommandName "Sync-ClusterVMHostStorage" -RunCommandModule "Microsoft.AVS.VMFS" -Parameters $params `
            -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup -TimeoutInMinutes $TimeoutInMinutes -Logger $logger

        Write-Progress -Activity "Operation is done" -Status "100% Complete:" -Completed
    }
    catch {
        throw
    }
    finally {
        $logger.Flush()
    }
}

<#
.SYNOPSIS
Fully remove a Pure Storage CBS AVS monitor deployment from Azure infrastructure

.DESCRIPTION
Fully remove a Pure Storage CBS AVS monitor deployment from Azure infrastructure. The resource group and its resources will be destroyed. The vNet subnet used by the monitor will also be remove from the vNet.

.PARAMETER MonitorResourceGroup
ResourceGroup to host monitor infrastructure components

 .PARAMETER RemoveSubnet
Optional. Indicates whether to remove the subnet used by the monitor from the vNet. If not provided, the subnet will not be removed.

.EXAMPLE
Remove-PCBSAVSMonitor -MonitorResourceGroup "myAVSMonitorResourceGroup"

.NOTES
Must be logged in to Azure (using Connect-AzAccount) before running this cmdlet
#>

function Remove-PCBSAVSMonitor {
  [CmdletBinding()]
    Param (
      [Parameter(Mandatory=$true)]
      [String]$MonitorResourceGroup,

      [Parameter(Mandatory=$false)]
      [Switch]$RemoveSubnet,

      [Parameter(Mandatory=$false)]
      $PureCloudBlockStoreConnection
    )

    $fa = Connect-PureCloudBlockStore -PureCloudBlockStoreConnection $PureCloudBlockStoreConnection
    $logger = (New-Object -TypeName 'PhoneHomeLogger' -ArgumentList $fa)

    $params = @{
      MonitorResourceGroup = $MonitorResourceGroup
      MonitorType = "host"
      RemoveSubnet = $RemoveSubnet
      Logger = $logger
    }

    try {
      Remove-Monitor @params
    } finally {
      $logger.Flush()
    }
}

<#
.SYNOPSIS
Deploy or update a Pure Storage CBS AVS monitor to Azure infrastructure

.DESCRIPTION
Deploy or update a Pure Storage CBS AVS monitor to Azure infrastructure. The monitor will scan periodically for AVS Cluster/Host changes and will make sure to change iSCSI configuration accordingly.

.PARAMETER MonitorResourceGroup
Resource group to host monitor infrastructure components. The resource group will be created if not exists

.PARAMETER MonitorResourceGroupRegion
Resource group region to host monitor infrastructure components.

.PARAMETER AVSCloudName
AVS cloud name

.PARAMETER AVSResourceGroup
AVS Resource group name

.PARAMETER VNetName
An existing VNet name. The VNey specified should have access to AVS as well as Pure Storage Cloud Block Store (CBS) array.

.PARAMETER VNetResourceGroup
VNet REsourceGroup

.PARAMETER VNetSubnetAddress
The VNet subnet address range in CIDR notation (e.g. 192.168.1.0/24). It must be contained by the address space of the virtual network.

.PARAMETER VNetSubnetName
If VNetSubnetAddress is specified, then VnetSubnetName can be optionaly used to specify a new subnet name otherwise it is existing VNet subnet name.

.PARAMETER MonitorIntervalInMinute
Optional. The default monitor interval is 10 minutes.

.PARAMETER RunCommandTimeoutInMinute
Optional. The default timeout for RunCommand operations is 10 minutes.

.EXAMPLE
Deploy-PCBSAVSMonitor -AVSCloudName "my-avs" -AVSResourceGroup "avs-resourcegroup" `
              -MonitorResourceGroup "NewResourceGroup" -MonitorResourceGroupRegion "westus2" -VNetName "my-vnet" -VNetResourceGroup "vnet-resourcegroup" -VNetSubnetAddress "192.168.3.0/24"

.NOTES
Must be logged in to Azure (using Connect-AzAccount) before running this cmdlet
#>

function Deploy-PCBSAVSMonitor {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)]
        [String]$MonitorResourceGroup,

        [Parameter(Mandatory=$true)]
        [String]$MonitorResourceGroupRegion,

        [Parameter(Mandatory=$true)]
        [String]$AVSCloudName,

        [Parameter(Mandatory=$true)]
        [String]$AVSResourceGroup,

    [Parameter(Mandatory = $true)]
    [String]$VNetName,

        [Parameter(Mandatory=$true)]
        [String]$VNetResourceGroup,

        [Parameter(ParameterSetName='NewSubnet', Mandatory=$true)]
        [String]$VNetSubnetAddress,

        [Parameter(ParameterSetName='ExistingSubnet', Mandatory=$true)]
        [Parameter(ParameterSetName='NewSubnet', Mandatory=$false)]
        [String]$VNetSubnetName,

    [Parameter(Mandatory = $false)]
    [ValidateScript({ $_ -ge 10 }, ErrorMessage = "The minimum interval for the monitor is 10 minutes.")]
    [ValidateScript({ $_ -le 60 }, ErrorMessage = "The maximum interval for the monitor is 60 minutes.")]
    [int]$MonitorIntervalInMinute = 10,

    [Parameter(Mandatory = $false)]
    [ValidateRange(10, 60)]
    [int] $RunCommandTimeoutInMinute = $DEFAULT_RUNCOMMAND_TIMEOUT_IN_MINUTE,

    [Parameter(Mandatory=$false)]
    $PureCloudBlockStoreConnection
  )

  $fa = Connect-PureCloudBlockStore -PureCloudBlockStoreConnection $PureCloudBlockStoreConnection
  $logger = (New-Object -TypeName 'PhoneHomeLogger' -ArgumentList $fa)

  $params = @{
    MonitorResourceGroup       = $MonitorResourceGroup
    MonitorResourceGroupRegion = $MonitorResourceGroupRegion
    AVSCloudName               = $AVSCloudName
    AVSResourceGroup           = $AVSResourceGroup
    VNetName                   = $VNetName
    VNetResourceGroup          = $VNetResourceGroup
    MonitorIntervalInMinute    = $MonitorIntervalInMinute
    MonitorType                = "host"
    DefaultRunCommandTimeoutInMinute = $RunCommandTimeoutInMinute
    Logger                     = $logger
  }
  $paramSetName = $PSCmdlet.ParameterSetName
  if ($paramSetName -eq "NewSubnet") {
    $params.Add("VNetSubnetAddress", $VNetSubnetAddress)
    if ($VNetSubnetName) {
      $params.Add("VNetSubnetName", $VNetSubnetName)
    }
  }
  else {
    $params.Add("VNetSubnetName", $VNetSubnetName)
  }
  try {
    Deploy-MonitoringResource @params
  } finally {
    $logger.Flush()
  }
}

<#
.SYNOPSIS
Deploy or update a Pure Storage CBS Capacity monitor

.DESCRIPTION
Deploy or update a Pure Storage CBS Capacity monitor to Azure infrastructure.
The monitor will scan periodically for CBS space utilization and will automatically request expanding the storage if the utilization reaches pre-defined threshold

.PARAMETER MonitorResourceGroup
Resource group to host monitor infrastructure components. The resource group will be created if not exists

.PARAMETER MonitorResourceGroupRegion
Resource group region to host monitor infrastructure components.

.PARAMETER VNetName
An existing VNet name. The VNey specified should have access to AVS as well as Pure Storage Cloud Block Store (CBS) array.

.PARAMETER VNetResourceGroup
VNet REsourceGroup

.PARAMETER VNetSubnetAddress
The VNet subnet address range in CIDR notation (e.g. 192.168.1.0/24). It must be contained by the address space of the virtual network.

.PARAMETER VNetSubnetName
If VNetSubnetAddress is specified, then VnetSubnetName can be optionaly used to specify a new subnet name otherwise it is existing VNet subnet name.

.PARAMETER DefaultUtilizationThreshold
Optional. The default utilization threshold is 80%.

.PARAMETER MonitorIntervalInMinute
Optional. The default monitor interval is 10 minutes.

.EXAMPLE
Deploy-PCBSCapacityMonitor -MonitorResourceGroup "NewResourceGroup" -MonitorResourceGroupRegion "westus2" `
             -VNetName "my-vnet" -VNetResourceGroup "vnet-resourcegroup" -VNetSubnetAddress "192.168.3.0/24"

.NOTES
Must be logged in to Azure (using Connect-AzAccount) before running this cmdlet
#>

function Deploy-PCBSCapacityMonitor {
  [CmdletBinding()]
  Param(
      [Parameter(Mandatory=$true)]
      [String]$MonitorResourceGroup,

      [Parameter(Mandatory=$true)]
      [String]$MonitorResourceGroupRegion,

    [Parameter(Mandatory = $true)]
    [String]$VNetName,

      [Parameter(Mandatory=$true)]
      [String]$VNetResourceGroup,

      [Parameter(ParameterSetName='NewSubnet', Mandatory=$true)]
      [String]$VNetSubnetAddress,

      [Parameter(ParameterSetName='ExistingSubnet', Mandatory=$true)]
      [Parameter(ParameterSetName='NewSubnet', Mandatory=$false)]
      [String]$VNetSubnetName,

      [Parameter(Mandatory=$false)]
      [ValidateRange(1, 100)]
      [int] $DefaultUtilizationThreshold = $DEFAULT_UTILIZATIOn_THRESHOLD,

      [Parameter(Mandatory=$false)]
      [ValidateScript({ $_ -ge 10 }, ErrorMessage = "The minimum interval for the monitor is 10 minutes.")]
      [ValidateScript({ $_ -le 60 }, ErrorMessage = "The maximum interval for the monitor is 60 minutes.")]
      [int]$MonitorIntervalInMinute = 30,

      [Parameter(Mandatory=$false)]
      $PureCloudBlockStoreConnection
  )

  $fa = Connect-PureCloudBlockStore -PureCloudBlockStoreConnection $PureCloudBlockStoreConnection
  $logger = (New-Object -TypeName 'PhoneHomeLogger' -ArgumentList $fa)

  $params = @{
    MonitorResourceGroup        = $MonitorResourceGroup
    MonitorResourceGroupRegion  = $MonitorResourceGroupRegion
    VNetName                    = $VNetName
    VNetResourceGroup           = $VNetResourceGroup
    MonitorIntervalInMinute     = $MonitorIntervalInMinute
    DefaultUtilizationThreshold = $DefaultUtilizationThreshold
    MonitorType                 = "capacity"
    Logger                      = $logger
  }
  $paramSetName = $PSCmdlet.ParameterSetName
  if ($paramSetName -eq "NewSubnet") {
    $params.Add("VNetSubnetAddress", $VNetSubnetAddress)
    if ($VNetSubnetName) {
      $params.Add("VNetSubnetName", $VNetSubnetName)
    }
  }
  else {
    $params.Add("VNetSubnetName", $VNetSubnetName)
  }
  try {
    Deploy-MonitoringResource @params
  } finally {
    $logger.Flush()
  }
}

<#
.SYNOPSIS
List the existing Pure Cloud Block Store in the Pure Storage CBS AVS monitor

.DESCRIPTION
List the existing Pure Cloud Block Store in the Pure Storage CBS AVS monitor

.PARAMETER MonitorResourceGroup
ResourceGroup to host monitor infrastructure components

.EXAMPLE
Get-PCBSAVSMonitorArray -MonitorResourceGroup myMonitor

.NOTES
Must be logged in to Azure (using Connect-AzAccount) before running this cmdlet
#>


function Get-PCBSAVSMonitorArray {
    [CmdletBinding()]
    Param (
      [Parameter(Mandatory=$true)]
      [String]$MonitorResourceGroup
    )
    $ResourceGroup = Get-AzResourceGroup -Name $MonitorResourceGroup

    if (-not $ResourceGroup) {
        throw "Resource group $MonitorResourceGroupdoes not exist"
    }

    if (-not $ResourceGroup.Tags["PureStorage.CBS.AVS"]) {
        throw "Resouce group $MonitorResourceGroup specified does not host Pure Storage CBS AVS monitor"
    }

    $UserPrincipalName = (Get-AzContext).Account.Id
    $KeyVault = Get-AzKeyVault -ResourceGroupName $MonitorResourceGroup
    Set-AzKeyVaultAccessPolicy -VaultName $KeyVault.VaultName  -UserPrincipalName $UserPrincipalName -PermissionsToSecrets set,delete,get,purge,list

    $Arrays = @()
    $Secrets = Get-AzKeyVaultSecret -VaultName $KeyVault.VaultName | where-object {$_.Name -like "*-$($KeyVault.VaultName)-username"}
    foreach ($Secret in $Secrets) {
        $ArrayName = ($Secret.name -Split "-$($KeyVault.VaultName)-username")[0]
        # If the string matches the format like "172-168-1-0", the array ip addressed was processed because secret name does not allow "."
        if ($ArrayName -match "^\d+-\d+-\d+-\d+$") {
            $ArrayName = $ArrayName.Replace("-", ".")
        }
        $Arrays += $ArrayName
    }

    return $Arrays
}

<#
.SYNOPSIS
Add a Pure Cloud Block Store to an existing Pure Storage CBS AVS monitor

.DESCRIPTION
Add a Pure Cloud Block Store to an existing Pure Storage CBS AVS monitor. If the Pure Block store already exists, it will be overwritten

.PARAMETER MonitorResourceGroup
ResourceGroup to host monitor infrastructure components

.PARAMETER PureCloudBlockStoreEndpoint
Pure Cloud Block Store endpoint address

.PARAMETER PureCloudBlockStoreCredential
Pure Cloud Block Store credential

.PARAMETER Force
Optional. If specified, the cmdlet will not throw error if the Pure Cloud Block Store cannot be verified

.EXAMPLE
Add-PCBSAVSMonitorArray -MonitorResourceGroup myMonitorGroup -PureCloudBlockStoreEndpoint myArray -PureCloudBlockStoreCredential (Get-Credential)

.NOTES
Must be logged in to Azure (using Connect-AzAccount) before running this cmdlet
#>

function Add-PCBSAVSMonitorArray {
    [CmdletBinding()]
    Param (
      [Parameter(Mandatory=$true)]
      [String]$MonitorResourceGroup,

      [Parameter(Mandatory=$true)]
      [String]$PureCloudBlockStoreEndpoint,

      [Parameter(Mandatory=$true)]
      [pscredential]$PureCloudBlockStoreCredential,

      [Parameter(Mandatory=$false)]
      [Switch]$Force,

      [Parameter(Mandatory=$false)]
      $PureCloudBlockStoreConnection
    )

    $fa = Connect-PureCloudBlockStore -PureCloudBlockStoreConnection $PureCloudBlockStoreConnection
    $logger = (New-Object -TypeName 'PhoneHomeLogger' -ArgumentList $fa)

    $params = @{
      MonitorResourceGroup = $MonitorResourceGroup
      PureCloudBlockStoreEndpoint = $PureCloudBlockStoreEndpoint
      PureCloudBlockStoreCredential = $PureCloudBlockStoreCredential
      MonitorType = "host"
      Logger = $logger
    }

    if ($Force) {
        $params.Add("Force", $Force)
    }

    try {
      Add-MonitorArray @params
    } finally {
      $logger.Flush()
    }
}

<#
.SYNOPSIS
Remove an existing Pure Cloud Block Store from a Pure Storage CBS AVS monitor

.DESCRIPTION
Remove an existing Pure Cloud Block Store from a Pure Storage CBS AVS monitor. If the Pure Block store already exists, it will be overwritten

.PARAMETER MonitorResourceGroup
ResourceGroup to host monitor infrastructure components

.PARAMETER PureCloudBlockStoreEndpoint
Pure Cloud Block Store endpoint address

.EXAMPLE
Remove-PCBSAVSMonitorArray -MonitorResourceGroup myMonitorGroup -PureCloudBlockStoreEndpoint myArray

.NOTES
Must be logged in to Azure (using Connect-AzAccount) before running this cmdlet
#>

function Remove-PCBSAVSMonitorArray {
    [CmdletBinding()]
    Param (
      [Parameter(Mandatory=$true)]
      [String]$MonitorResourceGroup,

      [Parameter(Mandatory=$true)]
      [String]$PureCloudBlockStoreEndpoint,

      [Parameter(Mandatory=$false)]
      $PureCloudBlockStoreConnection
    )

    $fa = Connect-PureCloudBlockStore -PureCloudBlockStoreConnection $PureCloudBlockStoreConnection
    $logger = (New-Object -TypeName 'PhoneHomeLogger' -ArgumentList $fa)

    $params = @{
      MonitorResourceGroup = $MonitorResourceGroup
      MonitorType = "host"
      PureCloudBlockStoreEndpoint = $PureCloudBlockStoreEndpoint
      Logger = $logger
    }
    try {
      Remove-MonitorArray @params
    } finally {
      $logger.Flush()
    }
}

<#
.SYNOPSIS
Fully remove a Pure Storage CBS Capacity monitor deployment from Azure infrastructure

.DESCRIPTION
Fully remove a Pure Storage CBS capacity monitor deployment from Azure infrastructure. The resource group and its resources will be destroyed. The vNet subnet used by the monitor will also be remove from the vNet.

.PARAMETER MonitorResourceGroup
ResourceGroup to host monitor infrastructure components

 .PARAMETER RemoveSubnet
Optional. Indicates whether to remove the subnet used by the monitor from the vNet. If not provided, the subnet will not be removed.

.EXAMPLE
Remove-PCBSCapacityMonitor -MonitorResourceGroup "myCapacityMonitorResourceGroup"

.NOTES
Must be logged in to Azure (using Connect-AzAccount) before running this cmdlet
#>

function Remove-PCBSCapacityMonitor {
  [CmdletBinding()]
    Param (
      [Parameter(Mandatory=$true)]
      [String]$MonitorResourceGroup,

      [Parameter(Mandatory=$false)]
      [Switch]$RemoveSubnet,

      [Parameter(Mandatory=$false)]
      $PureCloudBlockStoreConnection
    )

    $fa = Connect-PureCloudBlockStore -PureCloudBlockStoreConnection $PureCloudBlockStoreConnection
    $logger = (New-Object -TypeName 'PhoneHomeLogger' -ArgumentList $fa)

    $params = @{
      MonitorResourceGroup = $MonitorResourceGroup
      MonitorType = "capacity"
      RemoveSubnet = $RemoveSubnet
      Logger = $logger
    }

    try {
      Remove-Monitor @params
    } finally {
      $logger.Flush()
    }
}

<#
.SYNOPSIS
Add a Pure Cloud Block Store to an existing Pure Storage CBS capacity monitor

.DESCRIPTION
Add a Pure Cloud Block Store to an existing Pure Storage CBS capacity monitor. If the Pure Block store already exists, it will be overwritten

.PARAMETER MonitorResourceGroup
ResourceGroup to host monitor infrastructure components

.PARAMETER PureCloudBlockStoreEndpoint
Pure Cloud Block Store endpoint address

.PARAMETER PureCloudBlockStoreCredential
Pure Cloud Block Store credential

.PARAMETER UtilizationThreshold
Optional. The utilization threshold for the Pure Cloud Block Store. If not provided, the default threshold will be used.

.PARAMETER Force
Optional. If specified, the cmdlet will not throw error if the Pure Cloud Block Store cannot be verified

.EXAMPLE
Add-PCBSCapacityMonitorArray -MonitorResourceGroup myMonitorGroup -PureCloudBlockStoreEndpoint myArray -PureCloudBlockStoreCredential (Get-Credential) `
                             -UtilizationThreshold 80

.NOTES
Must be logged in to Azure (using Connect-AzAccount) before running this cmdlet
#>

function Add-PCBSCapacityMonitorArray {
  [CmdletBinding()]
  Param (
    [Parameter(Mandatory = $true)]
    [String]$MonitorResourceGroup,

    [Parameter(Mandatory = $true)]
    [String]$PureCloudBlockStoreEndpoint,

    [Parameter(Mandatory = $true)]
    [pscredential]$PureCloudBlockStoreCredential,

    [Parameter(Mandatory = $false)]
    [int] $UtilizationThreshold,

    [Parameter(Mandatory = $false)]
    [Switch]$Force,

    [Parameter(Mandatory=$false)]
    $PureCloudBlockStoreConnection
  )

  $fa = Connect-PureCloudBlockStore -PureCloudBlockStoreConnection $PureCloudBlockStoreConnection
  $logger = (New-Object -TypeName 'PhoneHomeLogger' -ArgumentList $fa)

  $params = @{
    MonitorResourceGroup          = $MonitorResourceGroup
    PureCloudBlockStoreEndpoint   = $PureCloudBlockStoreEndpoint
    PureCloudBlockStoreCredential = $PureCloudBlockStoreCredential
    MonitorType                   = "capacity"
    Logger                        = $logger
  }

  if ($UtilizationThreshold) {
    $params.Add("UtilizationThreshold", $UtilizationThreshold)
  }

  if ($Force) {
    $params.Add("Force", $Force)
  }

  try {
    Add-MonitorArray @params
  } finally {
    $logger.Flush()
  }
}

<#
.SYNOPSIS
Get metadata of a Pure Storage CBS capacity monitor

.DESCRIPTION
Get metadata of a Pure Storage CBS capacity monitor

.PARAMETER MonitorResourceGroup
ResourceGroup that hosts monitor infrastructure components
#>

function Get-PCBSCapacityMonitor {
  param (
    [Parameter(Mandatory = $true)]
    [String]$MonitorResourceGroup
  )

  Get-Monitor -MonitorResourceGroup $MonitorResourceGroup -MonitorType "capacity"

  return $result
}

<#
.SYNOPSIS
Get metadata of a Pure Storage CBS AVS monitor

.DESCRIPTION
Get metadata of a Pure Storage CBS AVS monitor

.PARAMETER MonitorResourceGroup
ResourceGroup that hosts monitor infrastructure components
#>

function Get-PCBSAVSMonitor {
  param (
    [Parameter(Mandatory = $true)]
    [String]$MonitorResourceGroup
  )

  Get-Monitor -MonitorResourceGroup $MonitorResourceGroup -MonitorType "host"

  return $result
}

<#
.SYNOPSIS
Remove an existing Pure Cloud Block Store from a Pure Storage CBS capacity monitor

.DESCRIPTION
Remove an existing Pure Cloud Block Store from a Pure Storage CBS capacity monitor.

.PARAMETER MonitorResourceGroup
ResourceGroup that hosts the monitor infrastructure components

.PARAMETER PureCloudBlockStoreEndpoint
Pure Cloud Block Store endpoint address
#>


function Remove-PCBSCapacityMonitorArray {
  param (
    [Parameter(Mandatory = $true)]
    [String]$MonitorResourceGroup,

    [Parameter(Mandatory = $true)]
    [String]$PureCloudBlockStoreEndpoint,

    [Parameter(Mandatory=$false)]
    $PureCloudBlockStoreConnection
  )

  $fa = Connect-PureCloudBlockStore -PureCloudBlockStoreConnection $PureCloudBlockStoreConnection
  $logger = (New-Object -TypeName 'PhoneHomeLogger' -ArgumentList $fa)

  $params = @{
    MonitorResourceGroup        = $MonitorResourceGroup
    MonitorType                 = "capacity"
    PureCloudBlockStoreEndpoint = $PureCloudBlockStoreEndpoint
    Logger                      = $logger
  }

  try {
    Remove-MonitorArray @params
  } finally {
    $logger.Flush()
  }
}

<#
.SYNOPSIS
Test the availability of a RunCommand package

.DESCRIPTION
Test the availability of a RunCommand package

.PARAMETER RunCommandPackageName
The name of the RunCommand package

.PARAMETER RunCommandPackageVersion
The version of the RunCommand package

.PARAMETER AVSCloudName
The name of the AVS cloud

.PARAMETER AVSResourceGroup
The name of the AVS resource group
#>

function Test-PCBSRunCommandPackageAvailability {
  param (
    [Parameter(Mandatory = $true)]
    [String] $RunCommandPackageName,

    [Parameter(Mandatory = $true)]
    [String] $RunCommandPackageVersion,

    [Parameter(Mandatory = $true)]
    [String] $AVSCloudName,

    [Parameter(Mandatory = $true)]
    [String] $AVSResourceGroup,

    [Parameter(Mandatory=$false)]
    $PureCloudBlockStoreConnection
  )

  $fa = Connect-PureCloudBlockStore -PureCloudBlockStoreConnection $PureCloudBlockStoreConnection
  $logger = (New-Object -TypeName 'PhoneHomeLogger' -ArgumentList $fa)

  $SubscriptionId = Get-DefaultAzureSubscriptionId
  $logger.LogInfo("Using Azure default subscription: $SubscriptionId...")

  try {
    Test-RunCommandPackageAvailability -SubscriptionId $SubscriptionId -RunCommandModule $RunCommandPackageName -RunCommandPackageVersion $RunCommandPackageVersion -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup -Logger $logger
  } finally {
    $logger.Flush()
  }
}

<#
.SYNOPSIS
Clear iSCSI targets on a VMHost(s)

.DESCRIPTION
Clear iSCSI targets on a VMHost. The function will clean iSCSI targets on the VMHost provided (or all VMHosts in the cluster if no VMHost is specified).
If no ScsiIpAddress, the function will clear all disconnected iSCSI targets on the VMHost(s).

.PARAMETER AVSCloudName
The name of the AVS cloud

.PARAMETER AVSResourceGroup
The name of the AVS resource group

.PARAMETER ClusterName
The name of the cluster

.PARAMETER VMHostName
The name of the VMHost

.PARAMETER ScsiIpAddress
The iSCSI address of the target
#>


function Clear-PCBSiSCSITargets {
  param (
    [Parameter(Mandatory = $true)]
    [String] $AVSCloudName,

    [Parameter(Mandatory = $true)]
    [String] $AVSResourceGroup,

    [Parameter (Mandatory = $true)]
    [String]$ClusterName,

    [Parameter (Mandatory = $false)]
    [String]$VMHostName,

    [Parameter (Mandatory = $false)]
    [String]$ScsiIpAddress,

    [Parameter(Mandatory=$false)]
    $PureCloudBlockStoreConnection
  )

  Write-Progress -Activity "Clearing iSCSI targets" -Status "0% Complete:" -PercentComplete 1

  $fa = Connect-PureCloudBlockStore -PureCloudBlockStoreConnection $PureCloudBlockStoreConnection
  $logger = (New-Object -TypeName 'PhoneHomeLogger' -ArgumentList $fa)

  try {
    $vCenterServer = Connect-AVSvCenter -AVSResourceGroupName $AVSResourceGroup -AVSPrivateCloudName $AVSCloudName -Logger $logger
    $Cluster = Get-Cluster -Name $ClusterName
    if (-not $Cluster) {
      throw "Cluster $ClusterName does not exist"
    }

    $params = @{
      ClusterName = $ClusterName
    }
    if ($VMHostName) {
      $params["VMHostName"] = $VMHostName
    }

    if ($ScsiIpAddress) {
      $params["ISCSIAddress"] = $ScsiIpAddress
      Invoke-RunScript -RunCommandName  "Remove-VMHostStaticiSCSITargets" -RunCommandModule "Microsoft.AVS.VMFS" -Parameters $params `
        -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup -TimeoutInMinutes $TimeoutInMinutes -Logger $logger
    }
    else {
      Invoke-RunScript -RunCommandName  "Clear-DisconnectedIscsiTargets" -RunCommandModule "Microsoft.AVS.VMFS" -Parameters $params `
        -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup -TimeoutInMinutes $TimeoutInMinutes -Logger $logger
    }
  }
  catch {
    Write-Progress -Activity "Error" -Status "100% Complete:" -PercentComplete 100
    throw
  }
  finally {
    $logger.Flush()
  }
  Disconnect-VIServer -Server $vCenterServer -Confirm:$false -ErrorAction SilentlyContinue
  Write-Progress -Activity "Operation is done" -Status "100% Complete:" -PercentComplete 100
}

Export-ModuleMember -Function Build-PCBSCluster
Export-ModuleMember -Function New-PCBSVmfsDatastore
Export-ModuleMember -Function Restore-PCBSVmfsDatastore
Export-ModuleMember -Function Remove-PCBSVmfsDatastore
Export-ModuleMember -Function Set-PCBSVmfsCapacity
Export-ModuleMember -Function Deploy-PCBSAVSMonitor
Export-ModuleMember -Function Remove-PCBSAVSMonitor
Export-ModuleMember -Function Add-PCBSAVSMonitorArray
Export-ModuleMember -Function Remove-PCBSAVSMonitorArray
Export-ModuleMember -Function Get-PCBSAVSMonitorArray
Export-ModuleMember -Function Get-PCBSAVSMonitor
Export-ModuleMember -Function Deploy-PCBSCapacityMonitor
Export-ModuleMember -Function Remove-PCBSCapacityMonitor
Export-ModuleMember -Function Add-PCBSCapacityMonitorArray
Export-ModuleMember -Function Get-PCBSCapacityMonitor
Export-ModuleMember -Function Remove-PCBSCapacityMonitorArray
Export-ModuleMember -Function Test-PCBSRunCommandPackageAvailability
Export-ModuleMember -Function Clear-PCBSiSCSITargets
Export-ModuleMember -Function Sync-PCBSClusterVMHostStorage

# SIG # Begin signature block
# MIIpRAYJKoZIhvcNAQcCoIIpNTCCKTECAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAo5MEXBjLtVWlS
# /3PBU0s2Dm61b3kLaUXSKNNcy4uIzaCCDfIwggbmMIIEzqADAgECAhB3vQ4DobcI
# +FSrBnIQ2QRHMA0GCSqGSIb3DQEBCwUAMFMxCzAJBgNVBAYTAkJFMRkwFwYDVQQK
# ExBHbG9iYWxTaWduIG52LXNhMSkwJwYDVQQDEyBHbG9iYWxTaWduIENvZGUgU2ln
# bmluZyBSb290IFI0NTAeFw0yMDA3MjgwMDAwMDBaFw0zMDA3MjgwMDAwMDBaMFkx
# CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMS8wLQYDVQQD
# EyZHbG9iYWxTaWduIEdDQyBSNDUgQ29kZVNpZ25pbmcgQ0EgMjAyMDCCAiIwDQYJ
# KoZIhvcNAQEBBQADggIPADCCAgoCggIBANZCTfnjT8Yj9GwdgaYw90g9z9DljeUg
# IpYHRDVdBs8PHXBg5iZU+lMjYAKoXwIC947Jbj2peAW9jvVPGSSZfM8RFpsfe2vS
# o3toZXer2LEsP9NyBjJcW6xQZywlTVYGNvzBYkx9fYYWlZpdVLpQ0LB/okQZ6dZu
# bD4Twp8R1F80W1FoMWMK+FvQ3rpZXzGviWg4QD4I6FNnTmO2IY7v3Y2FQVWeHLw3
# 3JWgxHGnHxulSW4KIFl+iaNYFZcAJWnf3sJqUGVOU/troZ8YHooOX1ReveBbz/IM
# BNLeCKEQJvey83ouwo6WwT/Opdr0WSiMN2WhMZYLjqR2dxVJhGaCJedDCndSsZlR
# Qv+hst2c0twY2cGGqUAdQZdihryo/6LHYxcG/WZ6NpQBIIl4H5D0e6lSTmpPVAYq
# gK+ex1BC+mUK4wH0sW6sDqjjgRmoOMieAyiGpHSnR5V+cloqexVqHMRp5rC+QBmZ
# y9J9VU4inBDgoVvDsy56i8Te8UsfjCh5MEV/bBO2PSz/LUqKKuwoDy3K1JyYikpt
# WjYsL9+6y+JBSgh3GIitNWGUEvOkcuvuNp6nUSeRPPeiGsz8h+WX4VGHaekizIPA
# tw9FbAfhQ0/UjErOz2OxtaQQevkNDCiwazT+IWgnb+z4+iaEW3VCzYkmeVmda6tj
# cWKQJQ0IIPH/AgMBAAGjggGuMIIBqjAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAww
# CgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU2rONwCSQ
# o2t30wygWd0hZ2R2C3gwHwYDVR0jBBgwFoAUHwC/RoAK/Hg5t6W0Q9lWULvOljsw
# gZMGCCsGAQUFBwEBBIGGMIGDMDkGCCsGAQUFBzABhi1odHRwOi8vb2NzcC5nbG9i
# YWxzaWduLmNvbS9jb2Rlc2lnbmluZ3Jvb3RyNDUwRgYIKwYBBQUHMAKGOmh0dHA6
# Ly9zZWN1cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L2NvZGVzaWduaW5ncm9vdHI0
# NS5jcnQwQQYDVR0fBDowODA2oDSgMoYwaHR0cDovL2NybC5nbG9iYWxzaWduLmNv
# bS9jb2Rlc2lnbmluZ3Jvb3RyNDUuY3JsMFYGA1UdIARPME0wQQYJKwYBBAGgMgEy
# MDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9z
# aXRvcnkvMAgGBmeBDAEEATANBgkqhkiG9w0BAQsFAAOCAgEACIhyJsav+qxfBsCq
# jJDa0LLAopf/bhMyFlT9PvQwEZ+PmPmbUt3yohbu2XiVppp8YbgEtfjry/RhETP2
# ZSW3EUKL2Glux/+VtIFDqX6uv4LWTcwRo4NxahBeGQWn52x/VvSoXMNOCa1Za7j5
# fqUuuPzeDsKg+7AE1BMbxyepuaotMTvPRkyd60zsvC6c8YejfzhpX0FAZ/ZTfepB
# 7449+6nUEThG3zzr9s0ivRPN8OHm5TOgvjzkeNUbzCDyMHOwIhz2hNabXAAC4ShS
# S/8SS0Dq7rAaBgaehObn8NuERvtz2StCtslXNMcWwKbrIbmqDvf+28rrvBfLuGfr
# 4z5P26mUhmRVyQkKwNkEcUoRS1pkw7x4eK1MRyZlB5nVzTZgoTNTs/Z7KtWJQDxx
# pav4mVn945uSS90FvQsMeAYrz1PYvRKaWyeGhT+RvuB4gHNU36cdZytqtq5NiYAk
# CFJwUPMB/0SuL5rg4UkI4eFb1zjRngqKnZQnm8qjudviNmrjb7lYYuA2eDYB+sGn
# iXomU6Ncu9Ky64rLYwgv/h7zViniNZvY/+mlvW1LWSyJLC9Su7UpkNpDR7xy3bzZ
# v4DB3LCrtEsdWDY3ZOub4YUXmimi/eYI0pL/oPh84emn0TCOXyZQK8ei4pd3iu/Y
# TT4m65lAYPM8Zwy2CHIpNVOBNNwwggcEMIIE7KADAgECAgxcuW61kTkv+4t8zgQw
# DQYJKoZIhvcNAQELBQAwWTELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNp
# Z24gbnYtc2ExLzAtBgNVBAMTJkdsb2JhbFNpZ24gR0NDIFI0NSBDb2RlU2lnbmlu
# ZyBDQSAyMDIwMB4XDTI0MDMxMTE0MDQxMloXDTI3MDMxMjE0MDQxMlowcjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENs
# YXJhMRswGQYDVQQKExJQdXJlIFN0b3JhZ2UsIEluYy4xGzAZBgNVBAMTElB1cmUg
# U3RvcmFnZSwgSW5jLjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMCQ
# rioSn48IvHpTg5dofsUYj/pNTDidwjYUrcxVu78NoyhSweG8FhcxDi/SI40+8Fcc
# l3D5ZoqpjkFnGhzSwmpxU3J4AP7+fdTZht9eWD1I5qKY07esYwdPDV4yg+csPfdG
# PqI2XjRfT5UC3YkXQeUrX8KQZldD4KqvgxzpYcuBwsgHbTb/eArpi68YgFR2jgZG
# yZigfy8RuJMrL1thcBOe/VWjUyK21wVT8cuunBYFaStLHhsRBRMDcZBDuTSGC4ev
# E6oaCqlQbdMl9YFJ64mDQsKlCxrr7rmLVtcVzKGwmjp4b2xRwE+RmTh6JtrUL9Wx
# /3a3UzgAnDNimfwp85zoL48kyLtHqQ3FI8tVKGm+aBOgBZfmURoy7fbp4zKhGgqF
# bpOmILO16i4f999YsEEJQgIF3CtyH1R60/ZZWlDmoeeEgjAGrnd14muU5Hk3Cksr
# 43uPUAg+fV78Y0fDV85ibm42ZwwPuz6MI4HhYNUlGzRwIQ31vjaGuAMWHNqFKkcO
# 0JuIeHQ/gFKPnYIxnGC9H9R4Kw/uMezqtnYJwGU2epB/ABl/w7U4NgU2ZOxWB5BF
# y4frZ3f+hNgbjFUjMaXnVFotOJxXntzjdSl4znw8DaKiC5ooChteZMITG9p078p/
# TUsOJQbUtFADSY1hsfCfB7t+gJSNt5peS9GOZIMVAgMBAAGjggGxMIIBrTAOBgNV
# HQ8BAf8EBAMCB4AwgZsGCCsGAQUFBwEBBIGOMIGLMEoGCCsGAQUFBzAChj5odHRw
# Oi8vc2VjdXJlLmdsb2JhbHNpZ24uY29tL2NhY2VydC9nc2djY3I0NWNvZGVzaWdu
# Y2EyMDIwLmNydDA9BggrBgEFBQcwAYYxaHR0cDovL29jc3AuZ2xvYmFsc2lnbi5j
# b20vZ3NnY2NyNDVjb2Rlc2lnbmNhMjAyMDBWBgNVHSAETzBNMEEGCSsGAQQBoDIB
# MjA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWduLmNvbS9yZXBv
# c2l0b3J5LzAIBgZngQwBBAEwCQYDVR0TBAIwADBFBgNVHR8EPjA8MDqgOKA2hjRo
# dHRwOi8vY3JsLmdsb2JhbHNpZ24uY29tL2dzZ2NjcjQ1Y29kZXNpZ25jYTIwMjAu
# Y3JsMBMGA1UdJQQMMAoGCCsGAQUFBwMDMB8GA1UdIwQYMBaAFNqzjcAkkKNrd9MM
# oFndIWdkdgt4MB0GA1UdDgQWBBSzJ9KiDCa3UBiAajy+Iioj5kQjzDANBgkqhkiG
# 9w0BAQsFAAOCAgEAHsFQixeQEcoHurq9NWSUt4S39Q+UGP6crmVq3Wwy9g23YbdW
# g+SgMxoLUqdoDfA4k4B6Dyoo0jEQzn2kxnsnT9lNHKrcZHH88dv0hjfiH2qAiQWa
# zPjS3LhK2J6nhpyipJPpyRaSQG4x4aG0NB2D4WUfUz9CGAYsERJGww/wkTaaxMip
# ttKDTaI1C49u1igDfRzIO+Q8vuyyBFLiYTno/df97xtjNC+KxxFhDhl/4tawK6kw
# xaVzCMAfj48I67Wbo4DMH6pM1s19as7c3qp92i3MylGKsB6+u+o7UkbSdLNkS4AL
# I33CJOUc+GoK3Nt5IXXCFJTQFHBXkBdAur3gmlXEm8vlNG/1Sbxr0H7T1e7ABGH/
# 48o/+PeMLuCc72EeK5dJ4cX9NEQ3QnTsZHwGnYzjEOvOvP0s1c7yNsDbcUHoIqQv
# b5xS5aqMU5G+8sdPQ1nwpPf7gGaEEbAVW4w51Pam42qeN9HIPa+ZinXnsN02Kk1Q
# w0QwUqzaQy9W/gIquI0KOjw0LmoW9M/8S0lrjpEq2eEeUw9WQLhhUEIirFxGPtjq
# iCLiiS9CZ+kf2vWLJKUspkYv+OHT3q805Zg1dJsBFAzEYUFLb1mhmigDEO9bsMor
# jECIL2ijE5zHtbGkalrrsPWu8tiDT/B7P9GSYzKfOOy4PoOIfWSK0IxlS7Ixghqo
# MIIapAIBATBpMFkxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52
# LXNhMS8wLQYDVQQDEyZHbG9iYWxTaWduIEdDQyBSNDUgQ29kZVNpZ25pbmcgQ0Eg
# MjAyMAIMXLlutZE5L/uLfM4EMA0GCWCGSAFlAwQCAQUAoIGXMBkGCSqGSIb3DQEJ
# AzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCsG
# CisGAQQBgjcCAQwxHTAboRmAF2h0dHBzOi8vcHVyZXN0b3JhZ2UuY29tMC8GCSqG
# SIb3DQEJBDEiBCB1MwopmRs0zWJ3EcWNv4GH7UsEuzepEn8md4DgJmXueDANBgkq
# hkiG9w0BAQEFAASCAgBjeR6pXZoWX48y/x1hDy92MHSoLYRQeNgNcVpCk10w62Y9
# AW+YiiyiZc7zUJAkMVc5hay9WuH/ewTJjhjtfnyioNT7Od2AL++57RomMbr8k8nH
# 7hbt6r7+xdogPZHQ+oLqxhVKNV2u6R+IXkz7eKlhFtqvpQ9KlAaS/EVdyhOWMJiK
# U7ZIwoI/T1i2dkLtaCCzwr3wV7olnZNNq4WNVAXU/YPVXTK8PyGkW1gBJeDDR6DY
# suJ1BMqNVSXJDKRokI05ILLX6sQB53GOwdCZG3lHY9xFOUrhcwZnm+26q2u7XGCZ
# nw0KD8Nbj/+5ysBT/xR63RgbL74xdCaTAllOfMjuBDKPL2yxF+MgBWl6WvwA/jT+
# ADXcfDdHrYqYsvxYgsGAFR+h399+YDWrAdYgWIQPDO+OpWh9xRia6ankoMTLnX7w
# Zltqe+Il/nVbFSXlk8jh9WcRo0FtDGStIX0r7xeolHPYMxRWYbor8gImLkozfibm
# sGt0g4I7solpqTGD2lzosZN4yBI5SOIxP30IKT9RFDw9RgAqvaMAknW8axj4sTk+
# wvjw2ZktX8I+/2GsmX1NoaSspXge/8kUIx1WJgUqmhs/8k1mohCe5pR4H8I0S8Gu
# N9Tv3QO4/MGYUi+AUGMAaxr4I2Zv1BJVSTaov/VeNr/DJxPm2UjaC8A7MxXiWKGC
# F3YwghdyBgorBgEEAYI3AwMBMYIXYjCCF14GCSqGSIb3DQEHAqCCF08wghdLAgED
# MQ8wDQYJYIZIAWUDBAIBBQAwdwYLKoZIhvcNAQkQAQSgaARmMGQCAQEGCWCGSAGG
# /WwHATAxMA0GCWCGSAFlAwQCAQUABCBjhsYuOLtcSg0LNVdJBu+WYmGajfV7L8i5
# OkRNsEVyKAIQIaXhe9h5vyETX0c1pwZ2GRgPMjAyNjA2MzAxNjI3MTFaoIITOjCC
# Bu0wggTVoAMCAQICEAqA7xhLjfEFgtHEdqeVdGgwDQYJKoZIhvcNAQELBQAwaTEL
# MAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhE
# aWdpQ2VydCBUcnVzdGVkIEc0IFRpbWVTdGFtcGluZyBSU0E0MDk2IFNIQTI1NiAy
# MDI1IENBMTAeFw0yNTA2MDQwMDAwMDBaFw0zNjA5MDMyMzU5NTlaMGMxCzAJBgNV
# BAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNl
# cnQgU0hBMjU2IFJTQTQwOTYgVGltZXN0YW1wIFJlc3BvbmRlciAyMDI1IDEwggIi
# MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDQRqwtEsae0OquYFazK1e6b1H/
# hnAKAd/KN8wZQjBjMqiZ3xTWcfsLwOvRxUwXcGx8AUjni6bz52fGTfr6PHRNv6T7
# zsf1Y/E3IU8kgNkeECqVQ+3bzWYesFtkepErvUSbf+EIYLkrLKd6qJnuzK8Vcn0D
# vbDMemQFoxQ2Dsw4vEjoT1FpS54dNApZfKY61HAldytxNM89PZXUP/5wWWURK+If
# xiOg8W9lKMqzdIo7VA1R0V3Zp3DjjANwqAf4lEkTlCDQ0/fKJLKLkzGBTpx6EYev
# vOi7XOc4zyh1uSqgr6UnbksIcFJqLbkIXIPbcNmA98Oskkkrvt6lPAw/p4oDSRZr
# eiwB7x9ykrjS6GS3NR39iTTFS+ENTqW8m6THuOmHHjQNC3zbJ6nJ6SXiLSvw4Smz
# 8U07hqF+8CTXaETkVWz0dVVZw7knh1WZXOLHgDvundrAtuvz0D3T+dYaNcwafsVC
# GZKUhQPL1naFKBy1p6llN3QgshRta6Eq4B40h5avMcpi54wm0i2ePZD5pPIssosz
# QyF4//3DoK2O65Uck5Wggn8O2klETsJ7u8xEehGifgJYi+6I03UuT1j7FnrqVrOz
# aQoVJOeeStPeldYRNMmSF3voIgMFtNGh86w3ISHNm0IaadCKCkUe2LnwJKa8TIlw
# CUNVwppwn4D3/Pt5pwIDAQABo4IBlTCCAZEwDAYDVR0TAQH/BAIwADAdBgNVHQ4E
# FgQU5Dv88jHt/f3X85FxYxlQQ89hjOgwHwYDVR0jBBgwFoAU729TSunkBnx6yuKQ
# VvYv1Ensy04wDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMI
# MIGVBggrBgEFBQcBAQSBiDCBhTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGln
# aWNlcnQuY29tMF0GCCsGAQUFBzAChlFodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5j
# b20vRGlnaUNlcnRUcnVzdGVkRzRUaW1lU3RhbXBpbmdSU0E0MDk2U0hBMjU2MjAy
# NUNBMS5jcnQwXwYDVR0fBFgwVjBUoFKgUIZOaHR0cDovL2NybDMuZGlnaWNlcnQu
# Y29tL0RpZ2lDZXJ0VHJ1c3RlZEc0VGltZVN0YW1waW5nUlNBNDA5NlNIQTI1NjIw
# MjVDQTEuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwHATANBgkq
# hkiG9w0BAQsFAAOCAgEAZSqt8RwnBLmuYEHs0QhEnmNAciH45PYiT9s1i6UKtW+F
# ERp8FgXRGQ/YAavXzWjZhY+hIfP2JkQ38U+wtJPBVBajYfrbIYG+Dui4I4PCvHpQ
# uPqFgqp1PzC/ZRX4pvP/ciZmUnthfAEP1HShTrY+2DE5qjzvZs7JIIgt0GCFD9kt
# x0LxxtRQ7vllKluHWiKk6FxRPyUPxAAYH2Vy1lNM4kzekd8oEARzFAWgeW3az2xe
# jEWLNN4eKGxDJ8WDl/FQUSntbjZ80FU3i54tpx5F/0Kr15zW/mJAxZMVBrTE2oi0
# fcI8VMbtoRAmaaslNXdCG1+lqvP4FbrQ6IwSBXkZagHLhFU9HCrG/syTRLLhAezu
# /3Lr00GrJzPQFnCEH1Y58678IgmfORBPC1JKkYaEt2OdDh4GmO0/5cHelAK2/gTl
# QJINqDr6JfwyYHXSd+V08X1JUPvB4ILfJdmL+66Gp3CSBXG6IwXMZUXBhtCyIaeh
# r0XkBoDIGMUG1dUtwq1qmcwbdUfcSYCn+OwncVUXf53VJUNOaMWMts0VlRYxe5nK
# +At+DI96HAlXHAL5SlfYxJ7La54i71McVWRP66bW+yERNpbJCjyCYG2j+bdpxo/1
# Cy4uPcU3AWVPGrbn5PhDBf3Froguzzhk++ami+r3Qrx5bIbY3TVzgiFI7Gq3zWcw
# gga0MIIEnKADAgECAhANx6xXBf8hmS5AQyIMOkmGMA0GCSqGSIb3DQEBCwUAMGIx
# CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
# dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBH
# NDAeFw0yNTA1MDcwMDAwMDBaFw0zODAxMTQyMzU5NTlaMGkxCzAJBgNVBAYTAlVT
# MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1
# c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYgMjAyNSBDQTEwggIi
# MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC0eDHTCphBcr48RsAcrHXbo0Zo
# dLRRF51NrY0NlLWZloMsVO1DahGPNRcybEKq+RuwOnPhof6pvF4uGjwjqNjfEvUi
# 6wuim5bap+0lgloM2zX4kftn5B1IpYzTqpyFQ/4Bt0mAxAHeHYNnQxqXmRinvuNg
# xVBdJkf77S2uPoCj7GH8BLuxBG5AvftBdsOECS1UkxBvMgEdgkFiDNYiOTx4OtiF
# cMSkqTtF2hfQz3zQSku2Ws3IfDReb6e3mmdglTcaarps0wjUjsZvkgFkriK9tUKJ
# m/s80FiocSk1VYLZlDwFt+cVFBURJg6zMUjZa/zbCclF83bRVFLeGkuAhHiGPMvS
# GmhgaTzVyhYn4p0+8y9oHRaQT/aofEnS5xLrfxnGpTXiUOeSLsJygoLPp66bkDX1
# ZlAeSpQl92QOMeRxykvq6gbylsXQskBBBnGy3tW/AMOMCZIVNSaz7BX8VtYGqLt9
# MmeOreGPRdtBx3yGOP+rx3rKWDEJlIqLXvJWnY0v5ydPpOjL6s36czwzsucuoKs7
# Yk/ehb//Wx+5kMqIMRvUBDx6z1ev+7psNOdgJMoiwOrUG2ZdSoQbU2rMkpLiQ6bG
# RinZbI4OLu9BMIFm1UUl9VnePs6BaaeEWvjJSjNm2qA+sdFUeEY0qVjPKOWug/G6
# X5uAiynM7Bu2ayBjUwIDAQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAd
# BgNVHQ4EFgQU729TSunkBnx6yuKQVvYv1Ensy04wHwYDVR0jBBgwFoAU7NfjgtJx
# XWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUF
# BwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGln
# aWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5j
# b20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJo
# dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNy
# bDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQEL
# BQADggIBABfO+xaAHP4HPRF2cTC9vgvItTSmf83Qh8WIGjB/T8ObXAZz8OjuhUxj
# aaFdleMM0lBryPTQM2qEJPe36zwbSI/mS83afsl3YTj+IQhQE7jU/kXjjytJgnn0
# hvrV6hqWGd3rLAUt6vJy9lMDPjTLxLgXf9r5nWMQwr8Myb9rEVKChHyfpzee5kH0
# F8HABBgr0UdqirZ7bowe9Vj2AIMD8liyrukZ2iA/wdG2th9y1IsA0QF8dTXqvcnT
# mpfeQh35k5zOCPmSNq1UH410ANVko43+Cdmu4y81hjajV/gxdEkMx1NKU4uHQcKf
# ZxAvBAKqMVuqte69M9J6A47OvgRaPs+2ykgcGV00TYr2Lr3ty9qIijanrUR3anzE
# wlvzZiiyfTPjLbnFRsjsYg39OlV8cipDoq7+qNNjqFzeGxcytL5TTLL4ZaoBdqbh
# OhZ3ZRDUphPvSRmMThi0vw9vODRzW6AxnJll38F0cuJG7uEBYTptMSbhdhGQDpOX
# gpIUsWTjd6xpR6oaQf/DJbg3s6KCLPAlZ66RzIg9sC+NJpud/v4+7RWsWCiKi9EO
# LLHfMR2ZyJ/+xhCx9yHbxtl5TPau1j/1MIDpMPx0LckTetiSuEtQvLsNz3Qbp7wG
# WqbIiOWCnb5WqxL3/BAPvIXKUjPSxyZsq8WhbaM2tszWkPZPubdcMIIFjTCCBHWg
# AwIBAgIQDpsYjvnQLefv21DiCEAYWjANBgkqhkiG9w0BAQwFADBlMQswCQYDVQQG
# EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
# cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcN
# MjIwODAxMDAwMDAwWhcNMzExMTA5MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEVMBMG
# A1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEw
# HwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEp
# pz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+
# n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYykt
# zuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw
# 2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQjdjUN6Qu
# BX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/CNdaSaTC
# 5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtmmnTK
# 3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3
# IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEP
# lAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98
# THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t9dmpsh3l
# GwIDAQABo4IBOjCCATYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7NfjgtJx
# XWRM3y5nP+e6mK4cD08wHwYDVR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8w
# DgYDVR0PAQH/BAQDAgGGMHkGCCsGAQUFBwEBBG0wazAkBggrBgEFBQcwAYYYaHR0
# cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAChjdodHRwOi8vY2FjZXJ0
# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3J0MEUGA1Ud
# HwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFz
# c3VyZWRJRFJvb3RDQS5jcmwwEQYDVR0gBAowCDAGBgRVHSAAMA0GCSqGSIb3DQEB
# DAUAA4IBAQBwoL9DXFXnOF+go3QbPbYW1/e/Vwe9mqyhhyzshV6pGrsi+IcaaVQi
# 7aSId229GhT0E0p6Ly23OO/0/4C5+KH38nLeJLxSA8hO0Cre+i1Wz/n096wwepqL
# sl7Uz9FDRJtDIeuWcqFItJnLnU+nBgMTdydE1Od/6Fmo8L8vC6bp8jQ87PcDx4eo
# 0kxAGTVGamlUsLihVo7spNU96LHc/RzY9HdaXFSMb++hUD38dglohJ9vytsgjTVg
# HAIDyyCwrFigDkBjxZgiwbJZ9VVrzyerbHbObyMt9H5xaiNrIv8SuFQtJ37YOtnw
# toeW/VvRXKwYw02fc7cBqZ9Xql4o4rmUMYIDfDCCA3gCAQEwfTBpMQswCQYDVQQG
# EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0
# IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYgU0hBMjU2IDIwMjUgQ0Ex
# AhAKgO8YS43xBYLRxHanlXRoMA0GCWCGSAFlAwQCAQUAoIHRMBoGCSqGSIb3DQEJ
# AzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcNMjYwNjMwMTYyNzExWjAr
# BgsqhkiG9w0BCRACDDEcMBowGDAWBBTdYjCshgotMGvaOLFoeVIwB/tBfjAvBgkq
# hkiG9w0BCQQxIgQgUU5BCZrQi/YONchPOGfguUHDQPIl1drgN1bqijldFjcwNwYL
# KoZIhvcNAQkQAi8xKDAmMCQwIgQgSqA/oizXXITFXJOPgo5na5yuyrM/420mmqM0
# 8UYRCjMwDQYJKoZIhvcNAQEBBQAEggIAoi5eAk0dAXP3ISfhi7nGxRuUfkYJ2QlF
# cqSWBZ6m5H3lzqkqLdqx3ScEzgmX0B07gZDlX3rUu0QThkN4Wam0fLENUD5eV2yG
# iFsOGDe/0l9aAZ5tvhr6ik78b04uKBxfM1GWoGMskzkIgHcHcbC2JxCYB58IUZlX
# GJXjDEJ5ItoorAOji52YRWHTOT6I9K2lHYxtC4VKn7Ob4PFnetycjiCrR/zd9YEd
# jMCtaz4WrQLRQkUD8cObecORQP5N4gfB88rV6uZlE+cGsHX1O2G+Gg2VjjSeOqx7
# SO2SFe9Fv8DaR940upkXM0/Crr0gV8aDRT7OUBTKrie7oPySuZKF9rrTxQus2P9N
# Y382ian+uEEAIGkb3cJXHeNvNvgRFoctRaRHz2z9LjR7jAxTOKzTjvQY26MiI0QD
# NC7k1PlihzDeMGoOlHp6ZIkv2DrFXXMx8LQYwPRtq7gjR55fjeBICqEdXslTizr8
# 6HOdTkaU461j/Xlgm6biRcDNfiYRq5hL5gNB7Xg6LMphiT8yAVhttdR6MJJg+9/n
# /vxksqFD5FdEuD9i8/Ab/06ez/ZPt2uSyyWtvE4OBsgDKtVCik+JaT0TgJMndhtq
# z/HdtS8r2V8gqexhS+u11z2Nr6qxiAo5z1yXxKCogMAZZWZcyDqEB3zk0z+T1E8d
# b9LwgQ4jC3k=
# SIG # End signature block