Obs/bin/SBRPClient/content/Microsoft.AzureStack.Diagnostics.SupportService.psm1

<###################################################
 # #
 # Copyright (c) Microsoft. All rights reserved. #
 # #
 ##################################################>


if (Test-Path "$PSScriptRoot\bin")
{
    # Loaded from WindowsPowerShell\Modules
    $clientBinaryDirectory = "$PSScriptRoot\bin"
}
elseif (Test-Path "$PSScriptRoot\..\lib\net46")
{
    # Loaded from NugetStore
    $clientBinaryDirectory = "$PSScriptRoot\..\lib\net46"
}
elseif (Test-Path "$PSScriptRoot\..\..\lib")
{
    # Loaded from NugetStore : used in Remote Support
    $clientBinaryDirectory = "$PSScriptRoot\..\..\lib"
}
elseif (Test-Path "$PSScriptRoot\..\lib")
{
    # Loaded from NugetStore : used in Log Collection
    $clientBinaryDirectory = "$PSScriptRoot\..\lib"
}
else
{
    throw "Unknown $PSScriptRoot"
}

Write-Verbose  "Client Binary resolved to - [$clientBinaryDirectory], All the binaries will be loaded from here" -Verbose

Add-Type -Path "$clientBinaryDirectory\Newtonsoft.Json.dll" -ErrorAction Stop -Verbose:$false | Out-Null
Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.SupportBridge.LogCollector.Client.Contract.dll" -ErrorAction Stop -Verbose:$false | Out-Null
Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.SupportBridge.LogCollector.Client.dll" -ErrorAction Stop -Verbose:$false | Out-Null
Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.SupportBridge.LogCollector.Client.Models.dll" -ErrorAction Stop -Verbose:$false | Out-Null
Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.Common.Operations.Contract.dll" -ErrorAction Stop -Verbose:$false | Out-Null
Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.SupportBridge.Management.Client.Contract.dll" -ErrorAction Stop -Verbose:$false | Out-Null
Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.SupportBridge.Management.Client.dll" -ErrorAction Stop -Verbose:$false | Out-Null
Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.SupportBridge.Mgmt.Client.Models.dll" -ErrorAction Stop -Verbose:$false | Out-Null
Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.Common.Infrastructure.ServiceRegistry.ServiceTypeRegistry.dll" -ErrorAction Stop -Verbose:$false | Out-Null

function Create-SupportControllerClientWithApplicationGateway
{
    [CmdletBinding()]
    Param(
      [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
      [string]
      $Endpoint = 'https://ASAppGateway:4443'
    )
    # Create Instance of Support Controller Client Object.
    [System.TimeSpan] $defaultDelay = [System.TimeSpan]::FromSeconds(5)
    [System.TimeSpan] $maxRetryTime = [System.TimeSpan]::FromSeconds(30)
        
    #Create Instance with gateway resolver.
    $subrControllerClient = [Microsoft.AzureStack.SupportBridge.LogCollector.Client.LogCollectorClientFactory]::CreateGatewayInstance($Endpoint, $defaultDelay, $maxRetryTime)
    return $subrControllerClient
}

# This works while reaching out to SUBR from other VM's where SF is installed and SDN is working
# as this requires VIP name resolution.
function Create-SupportControllerClientWithServiceResolver {
    [CmdletBinding()]
    Param([string[]]$clientConnectionEndpoints = 'ASSRSFClient:19000' )

    #Bugfix: Bug 3140655: Accessing ERCS VM Fails With "Unable to load DLL 'FabricClient.dll'"
    $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine")

    Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.Common.Infrastructure.ServiceDiscovery.ServiceFabric.Contract.dll" -ErrorAction Stop -Verbose:$false | Out-Null
    Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.Common.Infrastructure.ServiceDiscovery.ServiceFabric.dll" -ErrorAction Stop -Verbose:$false | Out-Null
    Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.Common.Infrastructure.ServiceRegistry.ServiceFabricTypeRegistry.dll" -ErrorAction Stop -Verbose:$false | Out-Null

    $resolver = [Microsoft.AzureStack.Common.Infrastructure.ServiceDiscovery.ServiceFabric.ServiceResolverFactory]::CreateServiceResolver(
        [Microsoft.AzureStack.Common.Infrastructure.ServiceRegistry.ServiceFabricTypeRegistry.ServiceFabricTypes]::TypeMap, $clientConnectionEndpoints)

    # Create Instance of Support Controller Client Object.
    [System.TimeSpan] $defaultDelay = [System.TimeSpan]::FromSeconds(5)
    [System.TimeSpan] $maxRetryTime = [System.TimeSpan]::FromSeconds(60)

    #Create Instance with gateway resolver.
    $subrControllerClient = [Microsoft.AzureStack.SupportBridge.LogCollector.Client.LogCollectorClientFactory]::CreateInstanceUsingServiceResolver($resolver, $defaultDelay, $maxRetryTime)
    return $subrControllerClient
}

# This works when service is running as WinSvC locally on the node.
function Create-LogCollectorClienttWithLocalServiceResolver {

    $serviceId = [System.Guid]"754dbc04-8f91-4cb6-a10f-899dac573fa0"
    $serviceUri = [System.Uri]'http://localhost:7345'
    $dict = New-Object "System.Collections.Generic.Dictionary``2[System.Guid,System.Uri]"
    $dict.Add($serviceId, $serviceUri)

    # Create Instance of Support Controller Client Object.
    [System.TimeSpan] $defaultDelay = [System.TimeSpan]::FromSeconds(5)
    [System.TimeSpan] $maxRetryTime = [System.TimeSpan]::FromSeconds(60)

    Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.Common.Infrastructure.ServiceDiscovery.WinSvc.dll" -ErrorAction Stop -Verbose:$false | Out-Null 
    $resolver = New-Object -TypeName 'Microsoft.AzureStack.Common.Infrastructure.ServiceDiscovery.WinSvc.ServiceResolver' -ArgumentList @($dict)

    #Create Instance with gateway resolver.
    $subrControllerLogCollectorClient = [Microsoft.AzureStack.SupportBridge.LogCollector.Client.LogCollectorClientFactory]::CreateInstanceUsingServiceResolver($resolver, $defaultDelay, $maxRetryTime, $null, $true)
    return $subrControllerLogCollectorClient
}

# This works while reaching out to SUBR from other VM's where SF is installed and SDN is working
# as this requires VIP name resolution.
function Create-SupportControllerMgmtClientWithServiceResolver {
    [CmdletBinding()]
    Param([string[]]$clientConnectionEndpoints = 'ASSRSFClient:19000' )

    $isASZ = Is-ASZ
    if ($isASZ){
       return Create-SupportControllerMgmtClientWithLocalServiceResolver -skipSPNAuth $true;
    }

    $ProviderName = [system.environment]::GetEnvironmentVariable("AzureStackProviderName",[System.EnvironmentVariableTarget]::Machine)
    if (($ProviderName -ne $null) -and ($ProviderName -ieq "Microsoft.HCI")){
       return Create-SupportControllerMgmtClientWithLocalServiceResolver -skipSPNAuth $true;
    }

    #Bugfix: Bug 3140655: Accessing ERCS VM Fails With "Unable to load DLL 'FabricClient.dll'"
    $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine")

    Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.Common.Infrastructure.ServiceDiscovery.ServiceFabric.Contract.dll" -ErrorAction Stop -Verbose:$false | Out-Null
    Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.Common.Infrastructure.ServiceDiscovery.ServiceFabric.dll" -ErrorAction Stop -Verbose:$false | Out-Null
    Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.Common.Infrastructure.ServiceRegistry.ServiceFabricTypeRegistry.dll" -ErrorAction Stop -Verbose:$false | Out-Null

    $resolver = [Microsoft.AzureStack.Common.Infrastructure.ServiceDiscovery.ServiceFabric.ServiceResolverFactory]::CreateServiceResolver(
        [Microsoft.AzureStack.Common.Infrastructure.ServiceRegistry.ServiceFabricTypeRegistry.ServiceFabricTypes]::TypeMap, $clientConnectionEndpoints)

    # Create Instance of Support Controller Client Object.
    [System.TimeSpan] $defaultDelay = [System.TimeSpan]::FromSeconds(5)
    [System.TimeSpan] $maxRetryTime = [System.TimeSpan]::FromSeconds(60)

    #Create Instance with gateway resolver.
    $subrControllerMgmtClient = [Microsoft.AzureStack.SupportBridge.Management.Client.ManagementClientFactory]::CreateInstanceUsingServiceResolver($resolver, $defaultDelay, $maxRetryTime)
    return $subrControllerMgmtClient
}

# This works when service is running as WinSvC locally on the node.
function Create-SupportControllerMgmtClientWithLocalServiceResolver {
    [CmdletBinding()]
    Param([bool]$skipSPNAuth = $false )

    $serviceId = [System.Guid]"ea126685-c89e-4294-959f-bba6bf75b4aa"
    $serviceUri = [System.Uri]'http://localhost:80/diagnostics'
    $dict = New-Object "System.Collections.Generic.Dictionary``2[System.Guid,System.Uri]"
    $dict.Add($serviceId, $serviceUri)

    # Create Instance of Support Controller Client Object.
    [System.TimeSpan] $defaultDelay = [System.TimeSpan]::FromSeconds(5)
    [System.TimeSpan] $maxRetryTime = [System.TimeSpan]::FromSeconds(60)

    Add-Type -Path "$clientBinaryDirectory\Microsoft.AzureStack.Common.Infrastructure.ServiceDiscovery.WinSvc.dll" -ErrorAction Stop -Verbose:$false | Out-Null 
    $resolver = New-Object -TypeName 'Microsoft.AzureStack.Common.Infrastructure.ServiceDiscovery.WinSvc.ServiceResolver' -ArgumentList @(,$dict)

    #Create Instance with gateway resolver.
    $subrControllerMgmtClient = [Microsoft.AzureStack.SupportBridge.Management.Client.ManagementClientFactory]::CreateInstanceUsingServiceResolver($resolver, $defaultDelay, $maxRetryTime, $null, $skipSPNAuth)
    return $subrControllerMgmtClient
}

<#
.SYNOPSIS
    Sends Azure Stack Diagnostic Logs to Microsoft.
 
.DESCRIPTION
    Makes a request to Support Bridge Service to upload diagnostic logs for stamp.
 
.PARAMETER FilterByRole
    Optional. Filter by Infrastructure Role type.
 
.PARAMETER FilterByResourceProvider
    Optional. Filter by Value-add resource provider type.
 
.PARAMETER FromDate
    Optional. Start time to use for logs to collect.
 
.PARAMETER ToDate
    Optional. End time to use for logs to collect.
 
.EXAMPLE
    The example below sends last 4 hour logs to Microsoft.
    PS C:\> Send-AzureStackDiagnosticLog
 
.EXAMPLE
    The example below sends last 4 hour Support Service and WAS logs to Microsoft.
    PS C:\> Send-AzureStackDiagnosticLog -FilterByRole SupportBridgeController,WAS
     
 
.NOTES
#>

function Send-AzureStackDiagnosticLog
{
    Param(
        [Parameter(Mandatory=$false)]
        [string[]]
        $FilterByRole,

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

        [Parameter(Mandatory=$false)]
        [nullable[DateTime]]
        $FromDate,

        [Parameter(Mandatory=$false)]
        [nullable[DateTime]]
        $ToDate
    )

    if ($FromDate -eq $null)
    {
        $FromDate = (Get-Date).AddHours(-4)
        Write-Host "FromDate parameter not specified. Setting to default value $FromDate"
    }

    if ($ToDate -eq $null)
    {
        $ToDate = Get-Date
        Write-Host "ToDate parameter not specified. Setting to default value $ToDate"
    }

    $logCollectionModel = Create-LogCollectionModel -FilterByRole $FilterByRole -FilterByResourceProvider $FilterByResourceProvider -FromDate $FromDate -ToDate $ToDate
    $subrControllerClient = Create-SupportControllerClientWithServiceResolver
    $collectionTask = $subrControllerClient.SubmitOnDemandCollectionJob("default", $logCollectionModel, [System.Threading.CancellationToken]::None)
    $collectionTask.Wait()
    $operationId = $collectionTask.Result
    Write-Host "Successfully submitted on-demand. Operation tracking Id: $operationId"

    [Microsoft.AzureStack.Common.Operations.OperationState]$runningState = [Microsoft.AzureStack.Common.Operations.OperationState]::Running
    [Microsoft.AzureStack.Common.Operations.OperationState]$currentState = Get-LogCollectionStatus -SubrControllerClient $subrControllerClient -OperationId $operationId
    Write-Host "Current log collection status: $currentState"

    while ($currentState -eq $runningState)
    {
        Write-Host "Waiting for log collection to complete..."
        Start-Sleep -s 120
        $currentState = Get-LogCollectionStatus -SubrControllerClient $subrControllerClient -OperationId $operationId
    }

    Write-Host "Log collection ended with status: $currentState"
}

function Send-DiagnosticData
{
    [CmdletBinding(DefaultParametersetName='LogCollectionAndParsing')] 
    Param(
        [Parameter(Mandatory=$false, ParameterSetName='SaveToPath')]
        [Parameter(Mandatory=$false, ParameterSetName='LogCollectionAndParsing')]
        [string[]]
        $FilterByRole,

        [Parameter(Mandatory=$false, ParameterSetName='SaveToPath')]
        [Parameter(Mandatory=$false, ParameterSetName='LogCollectionAndParsing')]
        [nullable[DateTime]]
        $FromDate,

        [Parameter(Mandatory=$false, ParameterSetName='SaveToPath')]
        [Parameter(Mandatory=$false, ParameterSetName='LogCollectionAndParsing')]
        [nullable[DateTime]]
        $ToDate,

        [Parameter(Mandatory=$false, ParameterSetName='SaveToPath')]
        [Parameter(Mandatory=$false, ParameterSetName='LogCollectionAndParsing')]
        [bool]
        $CollectSddc = $true,

        [Parameter(Mandatory=$false, ParameterSetName='SaveToPath')]
        [string]
        $SaveToPath,

        [Parameter(Mandatory=$false, ParameterSetName='SaveToPath')]
        [Parameter(Mandatory=$false, ParameterSetName='NoLogCollection')]
        [Parameter(Mandatory=$false, ParameterSetName='LogCollectionAndParsing')]
        [ValidateNotNullOrEmpty()]
        [string]
        $SupplementaryLogs,

        [Parameter(Mandatory=$false, ParameterSetName='SaveToPath')]
        [Parameter(Mandatory=$false, ParameterSetName='NoLogCollection')]
        [ValidateNotNullOrEmpty()]
        [PSCredential]
        $ShareCredential,

        [Parameter(Mandatory=$false, ParameterSetName='NoLogCollection')]
        [switch]
        $NoLogCollection,

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

        [Parameter(Mandatory=$false, ParameterSetName='SaveToPath')]
        [switch]
        $ToSMBShare,

        [Parameter(Mandatory=$false, ParameterSetName='NoLogCollection')]
        [switch]
        $FromSMBShare,

        [Parameter(Mandatory=$false, ParameterSetName='NoLogCollection')]
        [Parameter(Mandatory=$false, ParameterSetName='SaveToPath')]
        [string]
        $SharePath,

        [Parameter(Mandatory=$false)]
        [switch]
        $SkipTestObservability
    )

    if ($FromDate -eq $null)
    {
        $FromDate = (Get-Date).AddHours(-1)
        Write-Host "FromDate parameter not specified. Setting to default value $FromDate"
    }
    else
    {
        Write-Host "FromDate is $FromDate"
    }

    if ($ToDate -eq $null)
    {
        $ToDate = Get-Date
        Write-Host "ToDate parameter not specified. Setting to default value $ToDate"
    }
    else
    {
        Write-Host "ToDate is $ToDate"
    }

    Write-Host "The FromDate is in $($FromDate.Kind) and the ToDate is in $($ToDate.Kind)"
    Write-Host "The local time zone is $(Get-Timezone)"

    $FromDate = Convert-ToUtc $FromDate
    $ToDate = Convert-ToUtc $ToDate
    Write-Host "FromDate in UTC is now $FromDate. ToDate in UTC is now $ToDate"
    $CurrentDateTime = (Get-Date).ToUniversalTime()

    if ($ToDate -gt $CurrentDateTime.addMinutes(5))
    {   
        $OldToDate = $ToDate
        $ToDate = $CurrentDateTime
        Write-Warning "Log Collection To Date cannot be greater than current datetime of $CurrentDateTime. Changed Log Collection ToDate from $OldToDate to $ToDate"
    }

    if ($FromDate -gt $CurrentDateTime)
    {
        Write-Host "Log Collection From Date cannot be greater than current datetime of $CurrentDateTime. Aborting log collection."
        return
    }

    if ($ToDate -le $FromDate)
    {
        Write-Host "ToDate cannot be less than or equal to FromDate. Aborting log collection."
        return
    }

    # Warning if log collection is more than 24 hours in time range
    $timeDifference = New-TimeSpan -Start $fromDate -End $toDate
    # set to 24 hours + 2 minutes, in case time range is slightly above 24 hours
    $maxMinutes = 24 * 60 + 2
    if ($timeDifference.TotalMinutes -gt $maxMinutes)
    {
        Write-Warning "We advise limiting log collection to no more than 24 hours."
    }

    # To make backwards compatible, accept parameters -ToSMBShare, -SMBShare, and SharePath
    if ($SharePath)
    {
        if ($ToSMBShare)
        {
            if ($SaveToPath)
            {
                Write-Host "Cannot use both SharePath and SaveToPath parameters. Aborting log collection."
                return
            }
            else
            {
                $SaveToPath = $SharePath
                Write-Warning "Note: ToSMBShare and SharePath parameters are deprecated. Use SaveToPath parameter instead of SharePath parameter going forward."
            }
        }
        elseif ($FromSMBShare)
        {
            if ($SupplementaryLogs)
            {
                Write-Host "Cannot use both SharePath and SupplementaryLogs parameters. Aborting log collection."
                return
            }
            else
            {
                $SupplementaryLogs = $SharePath
                $NoLogCollection = $true
                Write-Warning "Note: FromSMBShare and SharePath parameters are deprecated. Use NoLogCollection parameter instead of FromSMBShare parameter, and SupplementaryLogs parameter instead of SharePath parameter going forward."
            }
        }
        else
        {
            Write-Host "SharePath parameter must be used with either ToSMBShare or FromSMBShare. Aborting log collection."
            return
        }
    }

    # After checking if sharepath exists and reassigning as needed, need to enforce required parameters depending on parameter Set
    $parameterSetName = $PSCmdlet.ParameterSetName
    if ($parameterSetName -ieq "SaveToPath")
    {
        if ([string]::IsNullOrEmpty($SaveToPath))
        {
            Write-Host "SaveToPath parameter cannot be null or empty when using parameter set $parameterSetName. Aborting log collection."
            return
        }
    }
    elseif ($parameterSetName -ieq "NoLogCollection")
    {
        if ([string]::IsNullOrEmpty($SupplementaryLogs))
        {
            Write-Host "SupplementaryLogs parameter cannot be null or empty when using parameter set $parameterSetName. Aborting log collection."
            return
        }
    }
    
    $correlationId = [guid]::NewGuid() 
    Write-Host "The correlation Id is $correlationId. This is used to query for this log collection in the diagnostic pipeline."

    if (!$SaveToPath)
    {
        # First need to check if GMA is running. If not, stop log collection.
        if (!(Is-GMARunning))
        {
            Write-Host "GMA process for diagnostic pipeline was not found. Aborting log collection."
            return
        }
        try
        {
            $pathToDiagnosticJson = "$env:SystemDrive\Gmacache\JsonDropLocation\AEODiagnostics.json"
            $diagnosticsJson = Get-Content -Raw $pathToDiagnosticJson | ConvertFrom-json
            
            $diagnosticInfo = [ordered]@{
                AEORegion =  $diagnosticsJson.ConstantVariables.MONITORING_AEO_REGION
                AEODeviceARMResourceUri = $diagnosticsJson.ConstantVariables.MONITORING_AEO_DEVICE_ARM_RESOURCE_URI
                AEOClusterNodeArcResourceUri = $diagnosticsJson.ConstantVariables.MONITORING_AEO_NODE_ARC_RESOURCE_URI
                CorrelationId = $correlationId
            }

            Write-Host "Provide the below information to the customer support engineer working on your case." -Verbose
            $diagnosticInfo.GetEnumerator() | ForEach-Object {
                $message = ' {0}: {1}' -f $_.key, $_.value
                Write-Host $message
            } 
        }
        catch
        {
            Write-Host "Could not get AEODeviceARMResourceUri, AEOClusterNodeArcResourceUri, or AEORegion. Error: $_"
        }
    }

    # If passed in SupplementaryLogs and parameter set name is SaveToPath or LogCollectionAndParsing, add warning if it doesn't exist.
    # If NoLogCollection parameter set, should be failure because there's nothing that can be collected and parsed.
    if (![string]::IsNullOrEmpty($SupplementaryLogs) )
    {
        $validPath = Test-SupplementaryLogsPath -SupplementaryLogs $SupplementaryLogs -ShareCredential $ShareCredential -ParameterSetName $ParameterSetName
        $supplPathWarning = "Cannot find path for supplementary logs $SupplementaryLogs, or path is not a folder."
        
        if (!$validPath)
        {
            if ($parameterSetName -ieq "NoLogCollection")
            {
                Write-Host "$supplPathWarning Aborting log collection."
                return
            }
            else
            {
                Write-Warning $supplPathWarning
                Write-Host "Continuing with collecting role logs and sddc logs"
            }
        }
    }
    
    # Parameter sets SaveToPath and NoLogCollection should be a local log collection only.
    if ($NoLogCollection -or $SaveToPath)
    {
        Write-Host "Bypassing the observability agent, as parameter set $parameterSetName is being used"
        $BypassObsAgent = $true
    }
    
    if (-not $BypassObsAgent -and (Is-ObservabilityAgentRunning)) {
        Write-Host "Observability Agent is running."
        Write-Host "This log collection will attempt to collect logs from all hosts."
        $logCollectionModel = Create-LogCollectionModel -FilterByRole $FilterByRole -FromDate $FromDate -ToDate $ToDate `
            -CollectSddc $CollectSddc -SupplementaryLogs $SupplementaryLogs -CorrelationId $correlationId -SkipTestObservability $SkipTestObservability.IsPresent
        $subrControllerClient = Create-LogCollectorClienttWithLocalServiceResolver
        $collectionTask = $subrControllerClient.SubmitOnDemandCollectionJob("default", $logCollectionModel, [System.Threading.CancellationToken]::None)
        $collectionTask.Wait()
        $operationId = $collectionTask.Result
        Write-Host "Successfully submitted on-demand. Log collection Job Id: $operationId. This is used to track the log collection with Get-LogCollectionHistory."
        [Microsoft.AzureStack.Common.Operations.OperationState]$runningState = [Microsoft.AzureStack.Common.Operations.OperationState]::Running
        [Microsoft.AzureStack.Common.Operations.OperationState]$currentState = Get-LogCollectionStatus -SubrControllerClient $subrControllerClient -OperationId $operationId
        Write-Host "Current log collection status: $currentState"

        while ($currentState -eq $runningState)
        {
            Write-Host "Waiting for log collection to complete..."
            Start-Sleep -s 120
            $currentState = Get-LogCollectionStatus -SubrControllerClient $subrControllerClient -OperationId $operationId
        }

        Write-Host "Log Collection is Complete."

        # Figure out if any data parsed.
        $records = Get-LogCollectionHistory
        $count = ($records | Measure-Object).Count
        if ($count -eq 1)
        {
            $filesParsed = $records.UploadNumberOfFiles
        }
        else
        {
            $record = $records | Where-Object {$_.LogCollectionId -ieq $operationId}
            $filesParsed = $record.UploadNumberOfFiles
        }

        if ($filesParsed -gt 0)
        {
            Write-Host "
                Thank you for sending diagnostic logs to Microsoft. In the event that further logs are deemed necessary,
                Microsoft Customer Support will communicate with you accordingly.
            "

        }
        else
        {
            Write-Host "Log collection failed."
        }

    }
    else
    {
        Write-Host "
            Bypassing the observability agent and collecting logs directly from LogOrchestrator.
            There will be no record of this log collection in the log collection history.
            Only collecting logs from the current host, $env:COMPUTERNAME.
        "


         # Call Get-DiagnosticLog with the right parameters (including filtering for local node)
         # We are running in local mode, which means collect logs only for this node.
         $filterByNode = @($env:COMPUTERNAME)

         $getDiagLogParams = @{
              TracingContext = $correlationId
              BypassedObsAgent = $true
              SupplementaryLogs = $SupplementaryLogs
              SkipTestObservability = $SkipTestObservability.IsPresent
         }

         # Don't pass in parameters used in collecting logs if we are using NoLogCollection parameter set, because
         # in that case, we skip log collection.
         if ($parameterSetName -ne "NoLogCollection") 
         {
            $getDiagLogParams.Add("IncludeGetSDDCLogs", $CollectSddc)
            $getDiagLogParams.Add("FilterByRole", $FilterByRole)
            $getDiagLogParams.Add("FilterByNode", $filterByNode)
            $getDiagLogParams.Add("LogCollectionStartTimeUTC", $FromDate)
            $getDiagLogParams.Add("LogCollectionEndTimeUTC", $ToDate)
            $getDiagLogParams.Add("LogCollectionJobType", "OnDemand")
            $getDiagLogParams.Add("LocalMode", $true)
         }

         if ($parameterSetName -ieq "SaveToPath")
         {
            $getDiagLogParams.Add("SaveToPath", $SaveToPath)
         }

          if ($parameterSetName -ieq "NoLogCollection") 
         {
            $getDiagLogParams.Add("NoLogCollection", $NoLogCollection)
         }

         if ($ShareCredential)
         {
              $getDiagLogParams.Add("ShareCredential", $ShareCredential)
         }

         Write-Host "Calling Get-DiagnosticLog."
         $diagnosticLogResults = Get-DiagnosticLog @getDiagLogParams 
         Write-Host "Get-DiagnosticLog is complete." 

         if ($parameterSetName -ne "SaveToPath")
         {
              $filesParsed = $diagnosticLogResults.GenevaConnector_ParsedFileCount
              if ($filesParsed -gt 0)
              {
                   Write-Host "Thank you for sending diagnostic logs to Microsoft. In the event that further logs are deemed necessary, Microsoft Customer Support will communicate with you accordingly."
              }
              else
              {
                  Write-Host "Log collection failed."
              }
         }
    }
}

<#
.SYNOPSIS
    Gets the log colllection history.
 
.DESCRIPTION
    Gets history of logs collected on the stamp.
 
.PARAMETER MaxRecords
    Optional. Limit the number of records to display. By default is set to 10. If lower or equal to 0, return the default
 
.PARAMETER AllRecords
    Optional. Defaults to false. Indicates whether to display all records. Ignores the value of MaxRecords
 
.EXAMPLE
    PS C:\> Get-LogCollectionHistory
 
.NOTES
#>

function Get-LogCollectionHistory
{
    Param(
        [Parameter(Mandatory=$false)]
        [nullable[DateTime]]
        $FromDate = (Get-Date).AddDays(-7),
        
        [Parameter(Mandatory=$false)]
        [int]
        $MaxRecords = 10,

        [Parameter(Mandatory=$false)]
        [switch]
        $AllRecords
    )

    if ($MaxRecords -le 0){
        $MaxRecords = 10
    }

    $logCollectionTable = $null

    $isASZ = Is-ASZ
    $subrControllerClient = $null
    if ($isASZ)
    {
        $subrControllerClient = Create-LogCollectorClienttWithLocalServiceResolver
    }
    else
    {
        $subrControllerClient = Create-SupportControllerClientWithServiceResolver
    }
        
    $fromDateString = $FromDate.ToString("MMddyyyyHHmmss")
    $getLogHistoryTask = $subrControllerClient.GetLogCollectionHistory([System.Threading.CancellationToken]::None, $fromDateString)
    $getLogHistoryTask.Wait()
    $logCollectionRecords = $getLogHistoryTask.Result
                
    $logCollectionData = [object[]]::new($logcollectionRecords.Count)
    $index = 0
    foreach ($logcollectionRecord in $logCollectionRecords)
    {
        $newRecord = [ordered]@{
            TimeCollected = $logcollectionRecord.CollectionTime
            Status = $logcollectionRecord.State
            LogCollectionId = $logCollectionRecord.OperationId
            CollectionFromDate = $logcollectionRecord.FromDate
            CollectionToDate = $logcollectionRecord.ToDate
            Type = $logcollectionRecord.JobType
            LogUploadSizeMb = $logcollectionRecord.UploadDetails.UploadSizeInMb
            UploadNumberOfFiles = $logcollectionRecord.UploadDetails.UploadNumberOfFiles
            Directory = $logcollectionRecord.UploadDetails.UploadDirectory
            Location = $logcollectionRecord.LogLocation
            Error = $logcollectionRecord.Error.Message -replace "`r`n",'; '
            "----------" = "---------------------------------------------------------"
        }
        $logCollectionData[$index++] = $newRecord
    }
    $logCollectionTable = $logCollectionData | Sort-Object { [DateTime]$_.TimeCollected.DateTime } -Descending 

    if (-not $AllRecords)
    {
        $logCollectionTable = $logCollectionTable | Select-Object -First $MaxRecords
    }

    if ($logCollectionTable.count -eq 0)
    {
        Write-Host "No records in time range." 
    }
        
    return $logCollectionTable
}

<#
.SYNOPSIS
    Enables proactive log collection.
 
.DESCRIPTION
    Enables proactive log collection.
 
.EXAMPLE
    The example below enables proactive log collection.
    PS C:\> Enable-ProactiveLogCollection
 
.NOTES
#>

function Enable-ProactiveLogCollection
{
    $isASZ = Is-ASZ
    $subrControllerClient = $null
    if ($isASZ)
    {
        $subrControllerClient = Create-LogCollectorClienttWithLocalServiceResolver
        $enableProactiveTask = $subrControllerClient.EnableASZProactiveLogCollection([System.Threading.CancellationToken]::None)
    }
    else
    {
        $subrControllerClient = Create-SupportControllerClientWithServiceResolver
        $enableProactiveTask = $subrControllerClient.EnableLogCollection([System.Threading.CancellationToken]::None)
    }

        
    $enableProactiveTask.Wait()

    # check if enabled
    $enabled = Check-ProactiveEnabled -SubrControllerClient $subrControllerClient -IsASZ $isASZ
    if ($enabled)
    {
        return "enabled" 
    }
    else
    {
        throw "Proactive log collection could not be enabled." 
    }
}

<#
.SYNOPSIS
    Disables proactive log collection.
 
.DESCRIPTION
    Disables proactive log collection.
 
.EXAMPLE
    The example below enables disables log collection.
    PS C:\> Disable-ProactiveLogCollection
 
.NOTES
#>

function Disable-ProactiveLogCollection
{
    $isASZ = Is-ASZ
    $subrControllerClient = $null
    if ($isASZ)
    {
        $subrControllerClient = Create-LogCollectorClienttWithLocalServiceResolver
        $disableProactiveTask = $subrControllerClient.DisableASZProactiveLogCollection([System.Threading.CancellationToken]::None)
    }
    else
    {
        $subrControllerClient = Create-SupportControllerClientWithServiceResolver
        $disableProactiveTask = $subrControllerClient.DisableLogCollection([System.Threading.CancellationToken]::None)
    }

        
    $disableProactiveTask.Wait()

    # check if enabled
    $enabled = Check-ProactiveEnabled -SubrControllerClient $subrControllerClient -IsASZ $IsASZ
        
    if ($enabled)
    {
        throw "Proactive log collection could not be disabled." 
    }
    else
    {
        return "disabled"
    }
}

function Check-ProactiveEnabled
{
     Param(
        [Parameter(Mandatory=$true)]
        [Microsoft.AzureStack.SupportBridge.LogCollector.Client.Contract.ILogCollectorClient]
        $SubrControllerClient,

        [Parameter(Mandatory=$true)]
        [bool]
        $IsASZ
    )

    if ($IsASZ)
    {
        $logCollectionConfigTask = $SubrControllerClient.GetASZProactiveLogCollectionState([System.Threading.CancellationToken]::None)
        $logCollectionConfigTask.Wait()
        $proactiveStatus = $logCollectionConfigTask.Result
        return ($proactiveStatus -eq "enabled")
    }
    else
    {
        $logCollectionConfigTask = $SubrControllerClient.GetScheduledLogCollectionConfiguration([System.Threading.CancellationToken]::None)
        $logCollectionConfigTask.Wait()
        $logCollectionConfig = $logCollectionConfigTask.Result
        return $logCollectionConfig.Enabled
    }
    
}

<#
.SYNOPSIS
    Gets the proactive log collection state.
 
.DESCRIPTION
     Gets the proactive log collection state.
 
.EXAMPLE
    The example below gets the proactive log collection state..
    PS C:\> Get-ProactiveLogCollectionState
 
.NOTES
#>

function Get-ProactiveLogCollectionState
{
    $isASZ = Is-ASZ
    $subrControllerClient = $null
    if ($isASZ)
    {
        $subrControllerClient = Create-LogCollectorClienttWithLocalServiceResolver
    }
    else
    {
        $subrControllerClient = Create-SupportControllerClientWithServiceResolver
    }

    $enabled = Check-ProactiveEnabled -SubrControllerClient $subrControllerClient -IsASZ $IsASZ
    if ($enabled)
    {
        return "enabled"
    }
    else
    {
        return "disabled"
    }
}

<#
.SYNOPSIS
    Gets Support Service configuration settings.
 
.DESCRIPTION
    Support Service configuration settings.
 
.PARAMETER IncludeRegistrationObjectId
    Optional. Requires internet connectivity. Retrieves registration identity object id.
 
.EXAMPLE
    The example below gets registration details if stamp was registered or else null.
    PS C:\> Get-AzureStackSupportConfiguration
 
.NOTES
    Requires Support VM to have internet connectivity.
#>

function Get-AzureStackSupportConfiguration
{
    Param(
        [Parameter(Mandatory=$false)]
        [Switch]
        $IncludeRegistrationObjectId
    )

    try 
    {
        Write-Host "Fetching registration details from Support Service."
        $includePropertyName = $null
        if ($IncludeRegistrationObjectId)
        {
           $includePropertyName = "objectId"
        }

        $subrControllerClient = Create-SupportControllerClientWithServiceResolver
        $getRegistrationDetailsTask = $subrControllerClient.GetLogCollectionRegistrationDetails([System.Threading.CancellationToken]::None, $includePropertyName)
        $getRegistrationDetailsTask.Wait()
        [Microsoft.AzureStack.SupportBridge.LogCollector.Client.Models.LogCollectionRegistrationDetailsClientModelExt]$registrationDetails = $getRegistrationDetailsTask.Result
        if ($registrationDetails.RegistrationResourceId -eq $null)
        {
           Write-Error "Registration Details not found. This can happen either if Azure Stack is not registered or Disconnected."
        }
        else
        {
           Write-Host "Successfully fetched registration details."
           return ($registrationDetails | ConvertTo-Json -Depth 2)
        }
    }
    catch {
        echo $_.Exception|format-list -force
        Write-Error "Get-AzureStackSupportConfiguration failed with $_"
    }
}

<#
.SYNOPSIS
    Enables Cloud connection.
 
.DESCRIPTION
    Enables Cloud connection.
 
 
.EXAMPLE
    The example below enables cloud connection.
    PS C:\> Enable-AzsCloudConnection
 
.NOTES
    Requires Support VM to have internet connectivity.
#>

function Enable-AzsCloudConnection
{
    try
    {
        Write-Host "Enabling cloud connection..."

        $subrControllerMgmtClient = Create-SupportControllerMgmtClientWithServiceResolver
        $enableMgmtTask = $subrControllerMgmtClient.EnableRemoteManagement([System.Threading.CancellationToken]::None)
        $enableMgmtTask.Wait()
        Write-Host "Successfully enabled Cloud connection."
    }
    catch {
        echo $_.Exception|format-list -force
        Write-Error "Enable-AzsCloudConnection failed with $_"
    }
    finally {
    }
}

<#
.SYNOPSIS
    Disables Cloud connection.
 
.DESCRIPTION
    Disables Cloud connection.
 
 
.EXAMPLE
    The example below disables Cloud connection.
    PS C:\> Disable-AzsCloudConnection
 
.NOTES
    Requires Support VM to have internet connectivity.
#>

function Disable-AzsCloudConnection
{
    try
    {
        Write-Host "Disabling Cloud connection..."

        $subrControllerMgmtClient = Create-SupportControllerMgmtClientWithServiceResolver
        $enableMgmtTask = $subrControllerMgmtClient.DisableRemoteManagement([System.Threading.CancellationToken]::None)
        $enableMgmtTask.Wait()
        Write-Host "Successfully disabled cloud connection."
    }
    catch {
        echo $_.Exception|format-list -force
        Write-Error "Disable-AzsCloudConnection failed with $_"
    }
    finally {
    }
}

<#
.SYNOPSIS
    Adds cloud action.
 
.DESCRIPTION
    Adds cloud action.
 
.EXAMPLE
    The example below to add cloud action.
    PS C:\> Add-AzsCloudAction -SubsystemName $name -Expiration $date -Comments "sample access"
 
.NOTES
    Requires Support VM to have internet connectivity.
#>

function Add-AzsCloudAction
{
    Param(
        [Parameter(Mandatory=$true)]
        [string]
        $SubsystemName,

        [Parameter(Mandatory=$false)]
        [nullable[DateTime]]
        $Expiration,

        [Parameter(Mandatory=$false)]
        [string]
        $Comments
    )

    try
    {
        if ($Expiration -eq $null)
        {
            $Expiration = (Get-Date).AddDays(8)
            Write-Host "Expiration parameter not specified. Setting to default value $Expiration"
        }

        Write-Host "Adding Cloud Action..."
        Write-Host "Parameters - SubsystemName: $SubsystemName Expiration: $Expiration Comments: $Comments"

        $subrControllerMgmtClient = Create-SupportControllerMgmtClientWithServiceResolver
        $mgmtAccessModel = Create-ManagementAccessModel -SubsystemName $SubsystemName -Expiration $Expiration -Comments $Comments
        $addMgmtAccessTask = $subrControllerMgmtClient.AddorUpdateActionConsent($mgmtAccessModel, [System.Threading.CancellationToken]::None)
        $addMgmtAccessTask.Wait()
        [Microsoft.AzureStack.SupportBridge.Management.Client.Models.SubsystemAccessClientModel]$mgmtAccessDetails = $addMgmtAccessTask.Result
        if ($mgmtAccessDetails -eq $null -or $mgmtAccessDetails.SubsystemName -eq $null)
        {
           Write-Error "Cloud action could not be added. This can happen if Cloud connection is not enabled. Try Enabling Cloud connection and then run this again."
        }
        else
        {
           Write-Host "Successfully added Cloud action."
           Write-Output $mgmtAccessDetails
        }
    }
    catch {
        echo $_.Exception|format-list -force
        Write-Error "Add-AzsCloudAction failed with $_"
    }
    finally {
    }
}

<#
.SYNOPSIS
    Removes Cloud action.
 
.DESCRIPTION
    Removes Cloud action.
 
.EXAMPLE
    The example below removes cloud action.
    PS C:\> Remove-AzsCloudAction SubsystemName $name
 
.NOTES
    Requires Support VM to have internet connectivity.
#>

function Remove-AzsCloudAction
{
    Param(
        [Parameter(Mandatory=$true)]
        [string]
        $SubsystemName
    )

    try
    {
        Write-Host "Removing Cloud action..."
        Write-Host "Parameters - SubsystemName: $SubsystemName"

        $subrControllerMgmtClient = Create-SupportControllerMgmtClientWithServiceResolver

        $deleteMgmtAccessTask = $subrControllerMgmtClient.RemoveActionConsent($SubsystemName, [System.Threading.CancellationToken]::None)
        $deleteMgmtAccessTask.Wait()

        Write-Host "Successfully removed Cloud action."
    }
    catch {
        echo $_.Exception|format-list -force
        Write-Error "Remove-AzsCloudAction failed with $_"
    }
    finally {
    }
}

<#
.SYNOPSIS
    Gets Cloud actions.
 
.DESCRIPTION
    Gets Cloud actions.
 
.EXAMPLE
    The example below Gets Cloud actions.
    PS C:\> Get-AzsCloudAction
 
.NOTES
    Requires Support VM to have internet connectivity.
#>

function Get-AzsCloudAction
{
    Param(
        [Parameter(Mandatory=$false)]
        [switch]
        $IncludeExpired
    )

    try
    {
        Write-Host "Getting list of Cloud actions..."
        Write-Host "Parameters - IncludeExpired: $IncludeExpired"

        $subrControllerMgmtClient = Create-SupportControllerMgmtClientWithServiceResolver

        $getMgmtAccessTask = $subrControllerMgmtClient.GetActionConsent($IncludeExpired, [System.Threading.CancellationToken]::None)
        $getMgmtAccessTask.Wait()
        [Microsoft.AzureStack.SupportBridge.Management.Client.Models.SubsystemAccessClientModel[]]$mgmtAccessDetails = $getMgmtAccessTask.Result
        if ($mgmtAccessDetails -eq $null -or $mgmtAccessDetails.SubsystemName -eq $null)
        {
           Write-Host "Cloud actions list is empty. "
        }
        else
        {
           Write-Host "Successfully retrieved Cloud actions list."
           Write-Output $mgmtAccessDetails
        }
    }
    catch {
        echo $_.Exception|format-list -force
        Write-Error "Get-AzsCloudAction failed with $_"
    }
    finally {
    }
}

<#
.SYNOPSIS
    Enables Remote Support.
 
.DESCRIPTION
    Enables Remote Support allows authorized Microsoft Support users to remotely access the device for diagnostics or repair depending on the access level granted.
 
.PARAMETER AccessLevel
    Controls the remote operations that can be performed. This can be either Diagnostics or DiagnosticsAndRepair.
 
.PARAMETER ExpireInMinutes
    Optional. Defaults to 8 hours.
 
.PARAMETER SasCredential
    Optional. If provided by Microsoft Support, uses it to open remote support connection.
 
.PARAMETER AgreeToRemoteSupportConsent
    Optional. If set to true then records user consent as provided and proceeds without prompt.
 
.EXAMPLE
    The example below enables remote support for diagnostics only for 1 day. After expiration no more remote access is allowed.
    PS C:\> Enable-RemoteSupport -AccessLevel Diagnostics -ExpireInMinutes 1440
 
.NOTES
    Requires Support VM to have stable internet connectivity.
#>

function Enable-RemoteSupport
{
    Param(
        [Parameter(Mandatory=$true)]
        [ValidateSet("Diagnostics","DiagnosticsRepair")]
        [string]
        $AccessLevel,

        [Parameter(Mandatory=$false)]
        [int]
        $ExpireInMinutes = 480,

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

        [Parameter(Mandatory=$false)]
        [switch]
        $AgreeToRemoteSupportConsent
    )

    try
    {
        if ($ExpireInMinutes -lt 60)
        {
           Write-Error "ExpireInMinutes cannot be less than 1 hour(60 minutes)."
           return;
        }

        if ($ExpireInMinutes -gt 20160)
        {
           Write-Error "ExpireInMinutes cannot exceed beyond 14 days(20160 minutes)."
           return;
        }

        if ($AgreeToRemoteSupportConsent -ne $true)
        {
           Write-Host "`r`n`r`nBy approving this request, the Microsoft support organization or the Azure engineering team supporting this feature ('Microsoft Support Engineer') will be given direct access to your device for troubleshooting purposes and/or resolving the technical issue described in the Microsoft support case. `r`n`r`nDuring a remote support session, a Microsoft Support Engineer may need to collect logs. By enabling remote support, you have agreed to a diagnostic logs collection by Microsoft Support Engineer to address a support case You also acknowledge and consent to the upload and retention of those logs in an Azure storage account managed and controlled by Microsoft. These logs may be accessed by Microsoft in the context of a support case and to improve the health of Azure Stack Hub. `r`n`r`nThe data will be used only to troubleshoot failures that are subject to a support ticket, and will not be used for marketing, advertising, or any other commercial purposes without your consent. The data may be retained for up to ninety (90) days and will be handled following our standard privacy practices (https://privacy.microsoft.com/en-US/). Any data previously collected with your consent will not be affected by the revocation of your permission."
           while(1)
           {
              Write-Host "`r`nProceed with enabling remote support?"
              Write-Host "[Y] Yes [N] No: " -ForegroundColor Yellow -NoNewline
              $confirmation = Read-Host
              if ($confirmation -ieq 'Y' -or $confirmation -ieq 'YES')
              {
                 break;
              }
              elseif ($confirmation -ieq 'N' -or $confirmation -ieq 'NO')
              {
                return;
              }
           }
        }
        
        # Arc Extension registry key check
        $agentInstalltype = (Get-ItemProperty -Path 'HKLM:\SYSTEM\Software\Microsoft\AzureStack\Observability\RemoteSupport' -ErrorAction SilentlyContinue).InstallType
        $arcExtensionRootPath = (Get-ItemProperty -Path 'HKLM:\SYSTEM\Software\Microsoft\AzureStack\Observability\RemoteSupport' -ErrorAction SilentlyContinue).ExtensionRootPath
        $JeaConfigSource = "RegistrationModule"

        if($agentInstalltype -eq "ArcExtension"){
            if($(Get-Service "Observability Remote Support Agent" -ErrorAction SilentlyContinue).Status -eq "Running"){
                $scriptPath = Join-Path -Path $arcExtensionRootPath -ChildPath 'Common\ConfigureJeaEndpoints.ps1'
                $scriptArguments = " -Constants RemoteSupportConstants -JeaConfigSource $JeaConfigSource"
                Write-Host "JEA configuration script path and arguments are : $scriptPath $scriptArguments"
                Write-Host "Configuring JEA Endpoints from '$JeaConfigSource'"
                # Configure JEA, will overwrite if JEA is already installed by startup task
                if(Test-Path -Path $scriptPath){
                    Invoke-Expression -Command "& '$scriptPath' $scriptArguments"
                }
            }
        }

        $targetService = "PowerShell"
        Write-Host "`r`n`r`Enabling Remote Support for '$AccessLevel' expiring in '$ExpireInMinutes' minutes."

        $subrControllerMgmtClient = Create-SupportControllerMgmtClientWithServiceResolver
        if(![string]::IsNullOrEmpty($SasCredential)){
            $mgmtAccessModel = Create-RemoteSupportAccessRequestModel -TargetService $targetService -AccessLevel $AccessLevel -ExpireInMinutes $ExpireInMinutes -SasCredential $SasCredential
        }
        else {
            $mgmtAccessModel = Create-RemoteSupportAccessRequestModel -TargetService $targetService -AccessLevel $AccessLevel -ExpireInMinutes $ExpireInMinutes
        }
        $addMgmtAccessTask = $subrControllerMgmtClient.AddorUpdateRemoteSupportAccess($mgmtAccessModel, [System.Threading.CancellationToken]::None)
        $addMgmtAccessTask.Wait()
        [Microsoft.AzureStack.SupportBridge.Management.Client.Models.RemoteSupportAccessResponseClientModel]$mgmtAccessDetails = $addMgmtAccessTask.Result
        if ($mgmtAccessDetails -eq $null)
        {
           Write-Error "Remote Support could not be Enabled."
        }
        else
        {
           Write-Host "Remote Support successfully Enabled."
           Write-Output $mgmtAccessDetails
        }
    }
    catch {
        echo $_.Exception|format-list -force
        Write-Error "Enable-RemoteSupport failed with $_"
    }
    finally {
    }
}

<#
.SYNOPSIS
    Disables Remote Support.
 
.DESCRIPTION
    Disable Remote Support revokes all access levels previously granted. Any existing support sessions will be terminated, and new sessions can no longer be established.
 
.EXAMPLE
    The example below disables remote support.
    PS C:\> Disable-RemoteSupport
 
.NOTES
 
#>

function Disable-RemoteSupport
{
    try
    {
        Write-Host "Disabling Remote Support."

        $subrControllerMgmtClient = Create-SupportControllerMgmtClientWithServiceResolver
        $disableRemoteSupportMgmtTask = $subrControllerMgmtClient.RevokeRemoteSupportAccess([System.Threading.CancellationToken]::None)
        $disableRemoteSupportMgmtTask.Wait()
        
        Write-Host "Remote Support successfully Disabled."
    }
    catch {
        echo $_.Exception|format-list -force
        Write-Error "Disable-RemoteSupport failed with $_"
    }
    finally {
    }
}

<#
.SYNOPSIS
    Gets Remote Support Access.
 
.DESCRIPTION
    Gets remote support access.
 
.PARAMETER IncludeExpired
    Optional. Defaults to false. Indicates whether to include past expired entries.
 
.EXAMPLE
    The example below retrieves access level granted for remote support. The result will also include expired consents in the last 30 days.
    PS C:\> List-RemoteSupportAccess -IncludeExpired $true
 
.NOTES
 
#>

function Get-RemoteSupportAccess
{
    Param(
        [Parameter(Mandatory=$false)]
        [switch]
        $IncludeExpired
    )

    try
    {
        Write-Host "Retrieving Remote Support access. IncludeExpired is set to '$IncludeExpired'"

        $subrControllerMgmtClient = Create-SupportControllerMgmtClientWithServiceResolver

        $listRemoteSupportAccessMgmtTask = $subrControllerMgmtClient.ListRemoteSupportAccess($IncludeExpired, [System.Threading.CancellationToken]::None)
        $listRemoteSupportAccessMgmtTask.Wait()
        [Microsoft.AzureStack.SupportBridge.Management.Client.Models.RemoteSupportAccessResponseClientModel[]]$mgmtAccessDetails = $listRemoteSupportAccessMgmtTask.Result
        if ($mgmtAccessDetails -eq $null -or $mgmtAccessDetails.Length -eq 0)
        {
           Write-Host "No remote support access exists. "
        }
        else
        {
           Write-Output $mgmtAccessDetails
        }
    }
    catch {
        echo $_.Exception|format-list -force
        Write-Error "Get-RemoteSupportAccess failed with $_"
    }
    finally {
    }
}

<#
.SYNOPSIS
    Gets Remote Support Session History Details.
 
.DESCRIPTION
    Session history represents all remote accesses made by Microsoft Support for either Diagnostics or DiagnosticsRepair based on the Access Level granted.
 
.PARAMETER SessionId
    Optional. Session Id to get details for a specific session. If omitted then lists all sessions starting from date 'FromDate'.
 
.PARAMETER IncludeSessionTranscript
    Optional. Defaults to false. Indicates whether to include complete session transcript. Transcript provides details on all operations performed during the session.
 
.PARAMETER FromDate
    Optional. Defaults to last 7 days. Indicates date from where to start listing sessions from until now.
 
.PARAMETER MaxRecords
    Optional. Limit the number of records to display. By default is set to 10. If lower or equal to 0, return the default. Including a sessionId will always just return 1 record
 
.PARAMETER AllRecords
    Optional. Defaults to false. Indicates whether to display all records. Ignores the value of MaxRecords
 
.EXAMPLE
    The example below retrieves session history with transcript details for the specified session.
    PS C:\> Get-RemoteSupportSessionHistory -SessionId 467e3234-13f4-42f2-9422-81db248930fa -IncludeSessionTranscript $true
 
.EXAMPLE
    The example below lists session history starting from last 7 days (default) to now.
    PS C:\> Get-RemoteSupportSessionHistory
 
.NOTES
 
#>

function Get-RemoteSupportSessionHistory
{
    Param(
        [Parameter(Mandatory=$false)]
        [string]
        $SessionId,

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

        [Parameter(Mandatory=$false)]
        [DateTime]
        $FromDate = (Get-Date).AddDays(-7),
        
        [Parameter(Mandatory=$false)]
        [int]
        $MaxRecords = 10,
        
        [Parameter(Mandatory=$false)]
        [switch]
        $AllRecords
    )

    try
    {
       if ($MaxRecords -le 0){
         $MaxRecords = 10
       }
        
       $subrControllerMgmtClient = Create-SupportControllerMgmtClientWithServiceResolver
       if($SessionId.Length -eq 0)
       {
         $currentTime = Get-Date;
         if ($FromDate -gt $currentTime)
         {
            Write-Error "From date cannot be a date in future."
            return;
         }

         [TimeSpan]$daysDifference = $currentTime - $FromDate;
         $totalDays = [math]::Round($daysDifference.TotalDays)
         if ($totalDays -gt 45)
         {
            Write-Error "From date cannot be past 45 days."
            return;
         }

         Write-Host "Listing Session History for last '$totalDays' days."

         $listRemoteSupportSessionMgmtTask = $subrControllerMgmtClient.ListRemoteSupportSession($FromDate, $false, [System.Threading.CancellationToken]::None)
         $listRemoteSupportSessionMgmtTask.Wait()
         [Microsoft.AzureStack.SupportBridge.Management.Client.Models.RemoteSupportSessionResponseClientModel[]]$listMgmtSessionDetails = $listRemoteSupportSessionMgmtTask.Result 

         if (-not $AllRecords) {
            $listMgmtSessionDetails = $listMgmtSessionDetails | Sort-Object { [DateTime]$_.EndTime.DateTime } -Descending | Select-Object -First $MaxRecords   
         }
         
         if ($listMgmtSessionDetails -eq $null -or $listMgmtSessionDetails.Length -eq 0)
         {
            Write-Host "No remote support session exists. "
         }
         else
         {
            Write-Output $listMgmtSessionDetails
         }
       }
       else
       {
          Write-Host "Retrieving Session History for Id '$SessionId'. Include session transcript was set to '$IncludeSessionTranscript'"

          $getRemoteSupportSessionMgmtTask = $subrControllerMgmtClient.GetRemoteSupportSession($SessionId, $IncludeSessionTranscript, [System.Threading.CancellationToken]::None)
          $getRemoteSupportSessionMgmtTask.Wait()
          [Microsoft.AzureStack.SupportBridge.Management.Client.Models.RemoteSupportSessionResponseClientModel]$getMgmtSessionDetails = $getRemoteSupportSessionMgmtTask.Result
          if ($getMgmtSessionDetails -eq $null)
          {
             Write-Host "No remote support session with id '$SessionId' exists."
          }
          else
          {
            Write-Output $getMgmtSessionDetails
          }
       }
    }
    catch {
        echo $_.Exception|format-list -force
        Write-Error "Get-RemoteSupportSessionHistory failed with $_"
    }
    finally {
    }
}

#Helper functions for creating remote management access objects.
function Create-ManagementAccessModel
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)]
        [string]
        $SubsystemName,

        [Parameter(Mandatory=$false)]
        [DateTime]
        $Expiration,

        [Parameter(Mandatory=$false)]
        [string]
        $Comments
    )

    $mgmtAccessModel = New-Object -TypeName 'Microsoft.AzureStack.SupportBridge.Management.Client.Models.SubsystemAccessClientModel'
    $mgmtAccessModel.SubsystemName = $SubsystemName
    $mgmtAccessModel.ExpiresAt = $Expiration
    $mgmtAccessModel.Comments = $Comments

    Write-Output $mgmtAccessModel
}

#Helper functions for creating client model objects.
function Create-LogCollectionModel
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$false)]
        [string[]]
        $FilterByRole,

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

        [Parameter(Mandatory=$true)]
        [DateTime]
        $FromDate,

        [Parameter(Mandatory=$true)]
        [DateTime]
        $ToDate,

        [Parameter(Mandatory=$false)]
        [bool]
        $CollectSddc = $false,

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

        [Parameter(Mandatory=$false)]
        [Guid]
        $CorrelationId = [System.Guid]::Empty,

        [Parameter(Mandatory=$false)]
        [bool]
        $SkipTestObservability = $false

    )

    $logCollectionModel = New-Object -TypeName 'Microsoft.AzureStack.SupportBridge.LogCollector.Client.Models.LogCollectionJobClientModel'
    $logCollectionModel.FilterRoles = $FilterByRole
    $logCollectionModel.FilterResourceProviders = $FilterByResourceProvider
    $logCollectionModel.FromDate = $FromDate
    $logCollectionModel.ToDate = $ToDate  
    $logCollectionModel.CollectSddc = $CollectSddc
    $logCollectionModel.SupplementaryLogs = $SupplementaryLogs
    $logCollectionModel.CorrelationId = $CorrelationId
    $logCollectionModel.SkipTestObservability = $SkipTestObservability
    Write-Output $logCollectionModel
    
}

function Get-LogCollectionStatus
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)]
        [Microsoft.AzureStack.SupportBridge.LogCollector.Client.Contract.ILogCollectorClient]
        $SubrControllerClient,

        [Parameter(Mandatory=$true)]
        [string]
        $OperationId
    )


     $collectionStatusTask = $SubrControllerClient.GetLogCollectionJobOperationStatus($OperationId, [System.Threading.CancellationToken]::None)
     $collectionStatusTask.Wait()
     $collectionStatus = $collectionStatusTask.Result
     $collectionStatusRecord = $collectionStatus.StatusRecord
     [Microsoft.AzureStack.Common.Operations.OperationState]$collectionState = $collectionStatusRecord.State
     Write-Output $collectionState
}

#Helper functions for creating remote support access request object.
function Create-RemoteSupportAccessRequestModel
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)]
        [string]
        $TargetService,

        [Parameter(Mandatory=$true)]
        [string]
        $AccessLevel,

        [Parameter(Mandatory=$true)]
        [int]
        $ExpireInMinutes,

        [Parameter(Mandatory=$false)]
        [string]
        $SasCredential
    )

    $ExpiresAt=[system.datetimeoffset]::now.AddMinutes($ExpireInMinutes)

    $mgmtRemoteSupportReqAccessModel = New-Object -TypeName 'Microsoft.AzureStack.SupportBridge.Management.Client.Models.RemoteSupportAccessRequestClientModel'
    $mgmtRemoteSupportReqAccessModel.TargetService = $TargetService
    $mgmtRemoteSupportReqAccessModel.AccessLevel = $AccessLevel
    $mgmtRemoteSupportReqAccessModel.ExpiresAt = $ExpiresAt
    
    if(![String]::IsNullOrWhiteSpace($SasCredential))
    {
       Write-Host "Using provided SAS credential to make remote support connection."
       $mgmtRemoteSupportReqAccessModel.SasCredential = $SasCredential
    }

    Write-Output $mgmtRemoteSupportReqAccessModel
}

function Is-ASZ
{

    $path = "HKLM:\SOFTWARE\Microsoft\AzureStack"
    $name = "DeviceType"
    $keyNotFound = $false
    if (Test-Path $path)
    {
        $Key = Get-Item -LiteralPath $Path
        if ($null -ne $Key.GetValue($Name)) 
        {
            $value = $Key.GetValue($Name)
            if ($value -eq "AzureEdge" -or $value -eq "HCI")
            {
                return $true
            }
            else
            {
                return $false
            }
        }
        else
        {
            $keyNotFound = $true
        }
    }
    else
    {
        
        $keyNotFound = $true
    } 

    if ($keyNotFound)
    {
        # could be that key doesn't exist because Hub environment or because ASZ deployment failed before the key was created.
        # If failed in ASZ before key created, then cmdlets (get-logcollectionhistory, enable/disable proactive,
        #get-proactivelogcollectionstate) will fail anyway, because observability agent isn't up.
        Write-Host "DeviceType not found. Assuming DeviceType is not AzureEdge"
        return $false
    }
}


##### Send-DiagnosticData Helper Functions #######

function Is-ObservabilityAgentRunning
{
    try
    {
        $service = get-service -name "AzureStack Observability Agent"
        return ($service.Status -eq "Running")
    }
    catch
    {
        Write-Error "Could not get AzureStack Observability Agent service: $_"
        return $false
    }
}

function Is-GMARunning
{
    # Note: If GMA is not present, the log collection will not fail. It will just delete
    # logs without parsing them. Thus, we should throw failure without trying to collect logs
    # if GMA is not running.
    $processes =  (gwmi win32_process|?{$_.name -like "*MonAgentHost*"}).CommandLine
    $diagRunning = $false
    $pattern = '-serviceIdentity "DiagnosticsPROD#AzureEdgeObs.*Diag#\w+"'
    foreach ($process in $processes)
    {
        if ($process -match $pattern)
        {
            $diagRunning = $true
        }
    }
    return $diagRunning
}

 function Connect-ToSharePath
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        $SharePath,

        [Parameter(Mandatory=$true)]
        [PSCredential]
        $Credential
    )

    $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Credential.Password)
    $password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)

    $drives = @('H', 'K', 'O', 'R', 'W')

    foreach ($driveLetter in $drives)
    {
        $drive = $driveLetter + ":"
        $null = net use $drive $SharePath /user:$($Credential.UserName) $password

        if($LASTEXITCODE -ne 0)
        {
            Continue
        }
                    
        if (Test-Path $SharePath)
        {
            return $drive
        }
    }
    return $null
}

 function Test-SupplementaryLogsPath
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        $SupplementaryLogs,

        [Parameter(Mandatory=$false)]
        [PSCredential]
        $ShareCredential,

        [Parameter(Mandatory=$true)]
        [string]
        $ParameterSetName
    )

    # If parameter set is NoLogCollection, ShareCredential is for SupplementaryLogs.
    # If parameter set is SaveToPath, the ShareCredential is for SaveToPath.
    # If parameter set is LogCollectionAndParsing, ShareCredential is not allowed.
    # Thus, only test mapping to SupplementaryLogs path using ShareCredential if parameter set is NoLogCollection.
    if ($ParameterSetName -ieq "NoLogCollection" -and $ShareCredential)
    {
        $drive = Connect-ToSharePath -SharePath $SupplementaryLogs -Credential $ShareCredential
    }

    # Split Test-Path <path> -PathType Container into two statements, because -ErrorAction SilentlyContinue
    # shows errors when it is all in one statement.
    if ((Test-Path $SupplementaryLogs -ErrorAction SilentlyContinue) -and (Test-Path $SupplementaryLogs -PathType Container))
    {
        $validPath = $true
    }
    else
    {
        $validPath = $false
    }

    if ($null -ne $drive)
    {
        net use $drive /delete /yes
    }

    return $validPath
}

function Convert-ToUTC($date) {
    if ($date.Kind -eq [System.DateTimeKind]::Utc) {
        # Date is already in UTC, no need to convert
        return $date
    } else {
        # Convert to UTC
        return $date.ToUniversalTime()
    }
}

Set-Alias -Name Send-AzureStackDiagnosticLogs -Value Send-AzureStackDiagnosticLog

$isASZ = Is-ASZ
if ($isASZ)
{
    Export-ModuleMember -Function Send-DiagnosticData
    Export-ModuleMember -function Get-LogCollectionHistory
    Export-ModuleMember -Function Enable-ProactiveLogCollection
    Export-ModuleMember -Function Disable-ProactiveLogCollection
    Export-ModuleMember -Function Get-ProactiveLogCollectionState
    Export-ModuleMember -Function Enable-RemoteSupport
    Export-ModuleMember -Function Disable-RemoteSupport
    Export-ModuleMember -Function Get-RemoteSupportAccess
    Export-ModuleMember -Function Get-RemoteSupportSessionHistory
}
else
{
    # if returns false, could be because Hub or because ASZ failed early in deployment.
    # To Be safe, export everything.
    Export-ModuleMember -Function Send-AzureStackDiagnosticLog
    Export-ModuleMember -Function Send-DiagnosticData
    Export-ModuleMember -function Get-LogCollectionHistory
    Export-ModuleMember -Function Enable-ProactiveLogCollection
    Export-ModuleMember -Function Disable-ProactiveLogCollection
    Export-ModuleMember -Function Get-ProactiveLogCollectionState
    Export-ModuleMember -Function Get-AzureStackSupportConfiguration
    Export-ModuleMember -Function Enable-AzsCloudConnection
    Export-ModuleMember -Function Disable-AzsCloudConnection
    Export-ModuleMember -Function Add-AzsCloudAction
    Export-ModuleMember -Function Remove-AzsCloudAction
    Export-ModuleMember -Function Get-AzsCloudAction
    Export-ModuleMember -Function Enable-RemoteSupport
    Export-ModuleMember -Function Disable-RemoteSupport
    Export-ModuleMember -Function Get-RemoteSupportAccess
    Export-ModuleMember -Function Get-RemoteSupportSessionHistory
}


# SIG # Begin signature block
# MIIoKQYJKoZIhvcNAQcCoIIoGjCCKBYCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAeVNCzuoJCz2Ij
# mMZHRWPFmgVJhIoNyHCpo8SkFt6Q4aCCDXYwggX0MIID3KADAgECAhMzAAADrzBA
# DkyjTQVBAAAAAAOvMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjMxMTE2MTkwOTAwWhcNMjQxMTE0MTkwOTAwWjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDOS8s1ra6f0YGtg0OhEaQa/t3Q+q1MEHhWJhqQVuO5amYXQpy8MDPNoJYk+FWA
# hePP5LxwcSge5aen+f5Q6WNPd6EDxGzotvVpNi5ve0H97S3F7C/axDfKxyNh21MG
# 0W8Sb0vxi/vorcLHOL9i+t2D6yvvDzLlEefUCbQV/zGCBjXGlYJcUj6RAzXyeNAN
# xSpKXAGd7Fh+ocGHPPphcD9LQTOJgG7Y7aYztHqBLJiQQ4eAgZNU4ac6+8LnEGAL
# go1ydC5BJEuJQjYKbNTy959HrKSu7LO3Ws0w8jw6pYdC1IMpdTkk2puTgY2PDNzB
# tLM4evG7FYer3WX+8t1UMYNTAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQURxxxNPIEPGSO8kqz+bgCAQWGXsEw
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKzUwMTgyNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAISxFt/zR2frTFPB45Yd
# mhZpB2nNJoOoi+qlgcTlnO4QwlYN1w/vYwbDy/oFJolD5r6FMJd0RGcgEM8q9TgQ
# 2OC7gQEmhweVJ7yuKJlQBH7P7Pg5RiqgV3cSonJ+OM4kFHbP3gPLiyzssSQdRuPY
# 1mIWoGg9i7Y4ZC8ST7WhpSyc0pns2XsUe1XsIjaUcGu7zd7gg97eCUiLRdVklPmp
# XobH9CEAWakRUGNICYN2AgjhRTC4j3KJfqMkU04R6Toyh4/Toswm1uoDcGr5laYn
# TfcX3u5WnJqJLhuPe8Uj9kGAOcyo0O1mNwDa+LhFEzB6CB32+wfJMumfr6degvLT
# e8x55urQLeTjimBQgS49BSUkhFN7ois3cZyNpnrMca5AZaC7pLI72vuqSsSlLalG
# OcZmPHZGYJqZ0BacN274OZ80Q8B11iNokns9Od348bMb5Z4fihxaBWebl8kWEi2O
# PvQImOAeq3nt7UWJBzJYLAGEpfasaA3ZQgIcEXdD+uwo6ymMzDY6UamFOfYqYWXk
# ntxDGu7ngD2ugKUuccYKJJRiiz+LAUcj90BVcSHRLQop9N8zoALr/1sJuwPrVAtx
# HNEgSW+AKBqIxYWM4Ev32l6agSUAezLMbq5f3d8x9qzT031jMDT+sUAoCw0M5wVt
# CUQcqINPuYjbS1WgJyZIiEkBMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
# IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg
# Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
# CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03
# a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr
# rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg
# OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy
# 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9
# sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh
# dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k
# A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB
# w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn
# Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90
# lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w
# ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o
# ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD
# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa
# BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny
# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG
# AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV
# HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG
# AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl
# AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb
# C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l
# hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6
# I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0
# wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560
# STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam
# ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa
# J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah
# XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA
# 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt
# Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr
# /Xmfwb1tbWrJUnMTDXpQzTGCGgkwghoFAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAAOvMEAOTKNNBUEAAAAAA68wDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIMUC8Wl6gcJMRPVzftp/sDwC
# YcYarF7upmA1Ip00c1CLMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEAqAKl6PmHfiRh6SIobgr6e8H+9cfA7Na2+FDuvF0vxaD5cyZEMZFphEYe
# iDp/TRrxO+E5IbtmgsIoW2UDUYLx5jc0xg/QJ3JnDcC36ixlGpwZTWPUC5K6nLA6
# 1NSdaDCfxajgWKb4iRKXCr6ptd0lC460GsKCezURHofCMyT7OZnvYfZ2s/PbKVPz
# cx6JWUcPmFo98b12aVx21leelnBEu67Dr1UxycUcwDjxLd8rKTB/gXIG2X/zyI8j
# qdmI2tdPki6N4otCEIRHuQxa1gHxrG69yYpXn8fDSPEWjUYSwDEPFiGhUTrPa0b0
# dPXSj/0rnl91TN2HL9OE0mJw+uVFJaGCF5MwghePBgorBgEEAYI3AwMBMYIXfzCC
# F3sGCSqGSIb3DQEHAqCCF2wwghdoAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFRBgsq
# hkiG9w0BCRABBKCCAUAEggE8MIIBOAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCBlgXAL4gsxxqZO9C/SbdLkXayq64N8o5UcjBKMqhYT7AIGZogq6ZWW
# GBIyMDI0MDcwOTA4NTQzNS4zMVowBIACAfSggdGkgc4wgcsxCzAJBgNVBAYTAlVT
# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK
# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVy
# aWNhIE9wZXJhdGlvbnMxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjozNzAzLTA1
# RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaCC
# EeowggcgMIIFCKADAgECAhMzAAAB6pokctVZP2FjAAEAAAHqMA0GCSqGSIb3DQEB
# CwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH
# EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNV
# BAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTIzMTIwNjE4NDUz
# MFoXDTI1MDMwNTE4NDUzMFowgcsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMx
# JzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjozNzAzLTA1RTAtRDk0NzElMCMGA1UE
# AxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCCAiIwDQYJKoZIhvcNAQEB
# BQADggIPADCCAgoCggIBALULX/FIPyAH1fsu52ijatZvaSypoXrlC0mRtCmaxzob
# huDkw6/pY/+4nhc4m8pf9zW3R6PihYGp0YPpVuNdfhPQp/KVO6WvMq2DGfFmHurW
# 4PQPL/DkbQMkM9vqjFCvPq8xXZnfL1nGN9moGcN+oaif/hUMedmF1qzbay9ILkYf
# LCxDYn3Qwzsvh5xjxOcsjzmRddNURJvT23Eva0cxisH4ocLLTx2zfpqfshw4Z9Ga
# EdsWg9rmib1galUpLzF5PsQDBbtZtcv+Wjmn0pFEiMCWwEEcPVN0YG5ysYLdNBdJ
# On2zsOOS+80W5RrQEqzPpSIIvEkZBJmF3aI4lMR8nV/FiTadjpIIqxX5Wa1XlqI/
# Nj+xagVjnjb7POsA+vh6Wu+v24HpyL8pyL/8Q4RFkRRME9cwT+Jr63yOtPbLe6DX
# kxIJW6E6w2ua5kXBpEKtEQPTLPhX3CUxMYcglbnmI0zcc9UknX285K+sI/2WwRwT
# BZkhDUULI86eQzV+zvzzR1qEBrlSY+oyTlYQrHMM9WnTzVflFDocZVTPpl2BDSNx
# Pn0Qb4IoM9EPqbHyi/MilL+v/AQc8q3mQ6FiuPJAddz0ocpNZ9ekBWPVLKq3lfie
# v4yl65u/438+NAQ+vSJgkONLMmuoguEGzmnK1vq/JHwdRUyn6YADiteM7Dja+Qd9
# AgMBAAGjggFJMIIBRTAdBgNVHQ4EFgQUK4FFJaJR5ukXQFTUxMhyiwVuWV4wHwYD
# VR0jBBgwFoAUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYDVR0fBFgwVjBUoFKgUIZO
# aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIw
# VGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwGCCsGAQUFBwEBBGAwXjBc
# BggrBgEFBQcwAoZQaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0
# cy9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcnQwDAYD
# VR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAOBgNVHQ8BAf8EBAMC
# B4AwDQYJKoZIhvcNAQELBQADggIBACiDrVZeP37+fFVtfcbfsqC/Kg0Ce67bDceh
# ZmPcfRgJ5Ddv0pJlOFVOFbiIVwesqeEUwFtclfi5AjneQ5ZJpYJpXfELOelG3dzj
# +BKfd287/UY/cwmSkl+CjnoKBL3Ms6I/fWR+alR0+p6RlviK8xHoug9vkc2WrRZs
# GnMVu2xOM2tPJ+qpyoDBzqv30N/ZRBOoNrS/PCkDwLGICDYqVs/IzAE49yv2ElPy
# walf9mEsOHXV1lxtQDNcejVEmitJJ+1Vr2EtafPEbMQZp89TAuagROKE4YuohCUK
# m+v3geJqTQarTBjqV25RCOT+XFngTMDD9wYx6TwndB2I1Ly726NiHUHs0uvq3ciC
# V9JwNXdt1VZ63WK1NSgpVEsiK9EPABPt1EfXcKrfaPYkbkFi79eK1ETxx3NomYNU
# HNiGU+X1Be8L7qpHwjo0g3/33XhtOr9LiDoUXh/V2LFTETiqV9Q8yLEavQW3j9LQ
# /h/CaGz5YdGfrY8HiPfMIeLEokKxGf0hHcTEFApB0yLlq6KoHrFAEANR/4XuFIpl
# 9sDywVIWt4tKqG+P6pRAXzg1zG5rGlslZWmw7XwgvhBu3jkLP9AxrsSYwY2ftrww
# ze5NA6VDLS7pz+OrXXWLUmoyNrJNx5Bk0wEwzkQxzkOvmbdPhsOP1ZM0uA/xIV7c
# SpNpZUw5MIIHcTCCBVmgAwIBAgITMwAAABXF52ueAptJmQAAAAAAFTANBgkqhkiG
# 9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO
# BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEy
# MDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw
# MTAwHhcNMjEwOTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1WjB8MQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGlt
# ZS1TdGFtcCBQQ0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
# AOThpkzntHIhC3miy9ckeb0O1YLT/e6cBwfSqWxOdcjKNVf2AX9sSuDivbk+F2Az
# /1xPx2b3lVNxWuJ+Slr+uDZnhUYjDLWNE893MsAQGOhgfWpSg0S3po5GawcU88V2
# 9YZQ3MFEyHFcUTE3oAo4bo3t1w/YJlN8OWECesSq/XJprx2rrPY2vjUmZNqYO7oa
# ezOtgFt+jBAcnVL+tuhiJdxqD89d9P6OU8/W7IVWTe/dvI2k45GPsjksUZzpcGkN
# yjYtcI4xyDUoveO0hyTD4MmPfrVUj9z6BVWYbWg7mka97aSueik3rMvrg0XnRm7K
# MtXAhjBcTyziYrLNueKNiOSWrAFKu75xqRdbZ2De+JKRHh09/SDPc31BmkZ1zcRf
# NN0Sidb9pSB9fvzZnkXftnIv231fgLrbqn427DZM9ituqBJR6L8FA6PRc6ZNN3SU
# HDSCD/AQ8rdHGO2n6Jl8P0zbr17C89XYcz1DTsEzOUyOArxCaC4Q6oRRRuLRvWoY
# WmEBc8pnol7XKHYC4jMYctenIPDC+hIK12NvDMk2ZItboKaDIV1fMHSRlJTYuVD5
# C4lh8zYGNRiER9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6bMURHXLvjflSxIUXk8A8
# FdsaN8cIFRg/eKtFtvUeh17aj54WcmnGrnu3tz5q4i6tAgMBAAGjggHdMIIB2TAS
# BgkrBgEEAYI3FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQWBBQqp1L+ZMSavoKRPEY1
# Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXAYDVR0gBFUw
# UzBRBgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNy
# b3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoG
# CCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIB
# hjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fO
# mhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9w
# a2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggr
# BgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNv
# bS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MA0GCSqGSIb3
# DQEBCwUAA4ICAQCdVX38Kq3hLB9nATEkW+Geckv8qW/qXBS2Pk5HZHixBpOXPTEz
# tTnXwnE2P9pkbHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6U03dmLq2HnjYNi6cqYJW
# AAOwBb6J6Gngugnue99qb74py27YP0h1AdkY3m2CDPVtI1TkeFN1JFe53Z/zjj3G
# 82jfZfakVqr3lbYoVSfQJL1AoL8ZthISEV09J+BAljis9/kpicO8F7BUhUKz/Aye
# ixmJ5/ALaoHCgRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTpkbKpW99Jo3QMvOyRgNI9
# 5ko+ZjtPu4b6MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0sHrYUP4KWN1APMdUbZ1j
# dEgssU5HLcEUBHG/ZPkkvnNtyo4JvbMBV0lUZNlz138eW0QBjloZkWsNn6Qo3GcZ
# KCS6OEuabvshVGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJsWkBRH58oWFsc/4Ku+xB
# Zj1p/cvBQUl+fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7Fx0ViY1w/ue10CgaiQuP
# Ntq6TPmb/wrpNPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0dFtq0Z4+7X6gMTN9vMvp
# e784cETRkPHIqzqKOghif9lwY1NNje6CbaUFEMFxBmoQtB1VM1izoXBm8qGCA00w
# ggI1AgEBMIH5oYHRpIHOMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScw
# JQYDVQQLEx5uU2hpZWxkIFRTUyBFU046MzcwMy0wNUUwLUQ5NDcxJTAjBgNVBAMT
# HE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiIwoBATAHBgUrDgMCGgMVAInb
# HtxB+OlGyQnxQYhy04KSYSSPoIGDMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# UENBIDIwMTAwDQYJKoZIhvcNAQELBQACBQDqN0Z9MCIYDzIwMjQwNzA5MDUxNzQ5
# WhgPMjAyNDA3MTAwNTE3NDlaMHQwOgYKKwYBBAGEWQoEATEsMCowCgIFAOo3Rn0C
# AQAwBwIBAAICNqkwBwIBAAICE64wCgIFAOo4l/0CAQAwNgYKKwYBBAGEWQoEAjEo
# MCYwDAYKKwYBBAGEWQoDAqAKMAgCAQACAwehIKEKMAgCAQACAwGGoDANBgkqhkiG
# 9w0BAQsFAAOCAQEAWqpU8EU+bSHTnx5r+ldpaGs9aTsJp/3CKIcVvvwXSIGA5s8Z
# P+jnM78xsXwWQU/XauVXOjHbYDv+iv8GECMVRpua20VxsCxHY0qWxEViK4lJg5qo
# M+yLkO88bWU/90IBSWtwNu1tmQ1Osc05dMqoAgDiqCAbT2rJ2kKVd0dmaHPtyI37
# Nkm0kg+BLjy4XAT92lmweHCHlzEOSFNEV7OHzjQyBECQLylQwy3s9Zi3vfKclbEL
# zXA5o85ZhtjpETjjKu1W+LAZ17DStBo/bMiD1UXt4FxdhvlPM+RmsnlCCtuEop29
# yH/CmeuZTTO/SdqUGxhdQCC9F5+dTw9THXSEaTGCBA0wggQJAgEBMIGTMHwxCzAJ
# BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jv
# c29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB6pokctVZP2FjAAEAAAHqMA0G
# CWCGSAFlAwQCAQUAoIIBSjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJ
# KoZIhvcNAQkEMSIEIKS4yNwMesX3Wlqz3rhCW9TKRvsvW7uO93+Nz2mYnxS4MIH6
# BgsqhkiG9w0BCRACLzGB6jCB5zCB5DCBvQQgKY+h1eNkNHiLCDSW0sA1cGHkbW4q
# ooi+ryyMp6S4ZngwgZgwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
# cnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAx
# MAITMwAAAeqaJHLVWT9hYwABAAAB6jAiBCDjS1pWQBtpggyeIgCMv6946D8fJSfU
# b1otM7qnXGulKjANBgkqhkiG9w0BAQsFAASCAgBzkUk15OOUDGZJCjAqrQNqi3cs
# W5hGbmRJ5T0HzmcvKP0jZaoD5lXmXofcip4OeHRGsWCNsHq7FHf8vgCyfIqnyUFB
# wZ3hjWOlaaXxuyMA+LF+HfKe7nmKedNd70YbxDTWUCs9KwEk8GhjD6t2CFL6XEYB
# l1YzsqriE/TVoKwIAsgjZmLo1AeIgJC4X5lQJrp8h21+vRekRDsF6M9Y5trSvhB/
# q7GsjZtp1UUD8DfCNBvYtn6nNGDrwokrqIwD9dkREOz5j5YWf7nGSAT4Ox/c/w70
# 2XY6mZMTLRs8xqG/oaQkVsYnezvptGCYN9xWIYWpTD+ki4yJUKvEZ124p1jZPzaj
# 9z95cbfA8bVaNidopFIgivYeszwHT7J5yOzzqlL/NzNOe93ndYcmlytAe+yWraJI
# nUiH+4vDlkjgnLh4xp8bcj8ARvTEGGWebSvQuMKaZR4nPVramzizezWz921TccvF
# ifMZ9ywWClDEPlIUms/zQvFprIHZUhU4EhcSTOS0oLAg6Avq7OfD11GWvqQVF2I5
# 8PMyX5fux/HrcTShIA6hEJqT1NkZLY0dJQGzieqEDYlpvVJNPGb23KRAcXa6pJvX
# /BJempmob7xeryR383qudVTYk2PwZjemRL4p3wM3Dl0ppmS42rbuBewTJsK1q9Gt
# 1dIS1FAFOUuSMPnrbA==
# SIG # End signature block