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='LogCollection')] 
    Param(
        [Parameter(Mandatory=$false, ParameterSetName='ToShare')]
        [Parameter(Mandatory=$false, ParameterSetName='LogCollection')]
        [string[]]
        $FilterByRole,

        [Parameter(Mandatory=$false, ParameterSetName='ToShare')]
        [Parameter(Mandatory=$false, ParameterSetName='LogCollection')]
        [nullable[DateTime]]
        $FromDate,

        [Parameter(Mandatory=$false, ParameterSetName='ToShare')]
        [Parameter(Mandatory=$false, ParameterSetName='LogCollection')]
        [nullable[DateTime]]
        $ToDate,

        [Parameter(Mandatory=$false, ParameterSetName='ToShare')]
        [Parameter(Mandatory=$false, ParameterSetName='LogCollection')]
        [bool]
        $CollectSddc = $true,

        [Parameter(Mandatory=$true, ParameterSetName='ToShare')]
        [switch]
        $ToSMBShare,

        [Parameter(Mandatory=$true, ParameterSetName='FromShare')]
        [switch]
        $FromSMBShare,

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

        [Parameter(Mandatory=$true, ParameterSetName='FromShare')]
        [Parameter(Mandatory=$true, ParameterSetName='ToShare')]
        [ValidateNotNullOrEmpty()]
        [string]
        $SharePath,

        [Parameter(Mandatory=$false, ParameterSetName='FromShare')]
        [Parameter(Mandatory=$false, ParameterSetName='ToShare')]
        [ValidateNotNullOrEmpty()]
        [PSCredential]
        $ShareCredential,

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

    if ($FromDate -eq $null)
    {
        $FromDate = (Get-Date).AddHours(-1)
        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"
    }

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

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

    if ($ToDate -le $FromDate)
    {
        Write-Error "ToDate cannot be less than or equal to FromDate. 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 (!$ToSMBShare)
    {
        try
        {
            $pathToDiagnosticJson = "$env:SystemDrive\Gmacache\JsonDropLocation\AEODiagnostics.json"
            $diagnosticsJson = Get-Content -Raw $pathToDiagnosticJson | ConvertFrom-json
            $region =  $diagnosticsJson.ConstantVariables.MONITORING_AEO_REGION
            $deviceArmResourceUri = $diagnosticsJson.ConstantVariables.MONITORING_AEO_DEVICE_ARM_RESOURCE_URI
            Write-Host "The DeviceARM Uri is $deviceArmResourceUri"
            Write-Host "The region is $region"
        }
        catch
        {
            Write-Host "Could not get DeviceARM Uri or region. Error: $_"
        }
    }
    
    # For now, FromSMBShare and ToSMBShare can only be used if we bypass obs agent.
    # Path through observability agent has not been implemented yet.
    if ($FromSMBShare -or $ToSMBShare)
    {
        if ($FromSMBShare)
        {
            $shareParam = "FromSMBShare"
        }
        else
        {
            $shareParam = "ToSMBShare"
        }
        Write-Host "Bypassing the observability agent, as parameter $shareParam was passed in."
        $BypassObsAgent = $true
    }
    
    if (-not $BypassObsAgent -and (Is-ObservabilityAgentRunning)) {
        Write-Host "Observability Agent is running."
        $logCollectionModel = Create-LogCollectionModel -FilterByRole $FilterByRole -FromDate $FromDate -ToDate $ToDate `
            -CollectSddc $CollectSddc -CorrelationId $correlationId
        $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 ended with status: $currentState" 
    }
    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
         }

         # Don't pass in parameters used in collecting logs if we are using FromShare parameter set, because
         # in that case, we skip log collection.
         if ($PSCmdlet.ParameterSetName -ne 'FromShare') 
         {
            $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 ($FromSMBShare)
         {
            $getDiagLogParams.Add("FromSMBShare", $FromSMBShare)
         }
         if ($ToSMBShare)
         {
            $getDiagLogParams.Add("ToSMBShare", $ToSMBShare)
         }
         if ($SharePath)
         {
              $getDiagLogParams.Add("SharePath", $SharePath)
         }
         if ($ShareCredential)
         {
              $getDiagLogParams.Add("ShareCredential", $ShareCredential)
         }
         if ($LogFolderToParse)
         {
            $getDiagLogParams.Add("LogFolderToParse", $LogFolderToParse)
         }
         Write-Host "Calling Get-DiagnosticLog."
         Get-DiagnosticLog @getDiagLogParams
         Write-Host "Get-DiagnosticLog is complete." 
    }
}

<#
.SYNOPSIS
    Gets the log colllection history.
 
.DESCRIPTION
    Gets history of logs collected on the stamp.
 
.EXAMPLE
    PS C:\> Get-LogCollectionHistory
 
.NOTES
#>

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

    $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 ($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.
 
.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)
    )

    try
    {
       $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 ($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)]
        [Guid]
        $CorrelationId = [System.Guid]::Empty
    )

    $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.CorrelationId = $CorrelationId
    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
{
    try
    {
        $value = Get-ItemPropertyValue -Path "HKLM:\SOFTWARE\Microsoft\AzureStack" -Name "DeviceType" -ErrorAction SilentlyContinue

        if ($value -eq "AzureEdge" -or $value -eq "HCI")
        {
            return $true
        }
        else
        {
            return $false
        }
    }
    catch
    {
        # could fail 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
    } 
}

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
    }
}

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
# MIIoKgYJKoZIhvcNAQcCoIIoGzCCKBcCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCMovjPcVsyRdC1
# Qal4qux/bM524LjyXKtw+cfrIpy77aCCDXYwggX0MIID3KADAgECAhMzAAADTrU8
# esGEb+srAAAAAANOMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjMwMzE2MTg0MzI5WhcNMjQwMzE0MTg0MzI5WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDdCKiNI6IBFWuvJUmf6WdOJqZmIwYs5G7AJD5UbcL6tsC+EBPDbr36pFGo1bsU
# p53nRyFYnncoMg8FK0d8jLlw0lgexDDr7gicf2zOBFWqfv/nSLwzJFNP5W03DF/1
# 1oZ12rSFqGlm+O46cRjTDFBpMRCZZGddZlRBjivby0eI1VgTD1TvAdfBYQe82fhm
# WQkYR/lWmAK+vW/1+bO7jHaxXTNCxLIBW07F8PBjUcwFxxyfbe2mHB4h1L4U0Ofa
# +HX/aREQ7SqYZz59sXM2ySOfvYyIjnqSO80NGBaz5DvzIG88J0+BNhOu2jl6Dfcq
# jYQs1H/PMSQIK6E7lXDXSpXzAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUnMc7Zn/ukKBsBiWkwdNfsN5pdwAw
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKzUwMDUxNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAD21v9pHoLdBSNlFAjmk
# mx4XxOZAPsVxxXbDyQv1+kGDe9XpgBnT1lXnx7JDpFMKBwAyIwdInmvhK9pGBa31
# TyeL3p7R2s0L8SABPPRJHAEk4NHpBXxHjm4TKjezAbSqqbgsy10Y7KApy+9UrKa2
# kGmsuASsk95PVm5vem7OmTs42vm0BJUU+JPQLg8Y/sdj3TtSfLYYZAaJwTAIgi7d
# hzn5hatLo7Dhz+4T+MrFd+6LUa2U3zr97QwzDthx+RP9/RZnur4inzSQsG5DCVIM
# pA1l2NWEA3KAca0tI2l6hQNYsaKL1kefdfHCrPxEry8onJjyGGv9YKoLv6AOO7Oh
# JEmbQlz/xksYG2N/JSOJ+QqYpGTEuYFYVWain7He6jgb41JbpOGKDdE/b+V2q/gX
# UgFe2gdwTpCDsvh8SMRoq1/BNXcr7iTAU38Vgr83iVtPYmFhZOVM0ULp/kKTVoir
# IpP2KCxT4OekOctt8grYnhJ16QMjmMv5o53hjNFXOxigkQWYzUO+6w50g0FAeFa8
# 5ugCCB6lXEk21FFB1FdIHpjSQf+LP/W2OV/HfhC3uTPgKbRtXo83TZYEudooyZ/A
# Vu08sibZ3MkGOJORLERNwKm2G7oqdOv4Qj8Z0JrGgMzj46NFKAxkLSpE5oHQYP1H
# tPx1lPfD7iNSbJsP6LiUHXH1MIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# 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
# /Xmfwb1tbWrJUnMTDXpQzTGCGgowghoGAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAANOtTx6wYRv6ysAAAAAA04wDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIHW6Zo+tQRAKvNP7h5GNeCRX
# fL86D1ecvnskQoHzJHjDMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEAwdr6laGXvITiqs3qih2Lbz6EVY1YNR/V2esEwLQlyb2JlYifqdNQXhg9
# wuOUmpcmK6tIATjGIBxcTk0sNAlbRZSnYNGAew8xHpMRpiogbJ5YEWrdUca91LCG
# 3PdP4jlH0FHakkX2acASMDnLYfxnNEwUzOAZiiLlUGRNzEnC089LLVCi4jOKZHSb
# s60718qwKvnGTg0R9V8tviYwWHEksVWcQSfI3JGLwXW1u+AdH0Ahe6Pd07RhY9AD
# YNg5XLseYjh+UZ0lsdCaXcLMrM8Bp1JgfOukuGW+bPOsPOYFz2+KNmZx4ITPfAWp
# 6K8/fJvBCLRlge8FAAgAiFvEZKucXKGCF5QwgheQBgorBgEEAYI3AwMBMYIXgDCC
# F3wGCSqGSIb3DQEHAqCCF20wghdpAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq
# hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCAXHiTeqQSKPtUOokshICgy8RVkdMcfOv6H6C/nadouTQIGZMvM4HkZ
# GBMyMDIzMDgwNzIxMzMyOC43MTJaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l
# cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046QTAwMC0w
# NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg
# ghHqMIIHIDCCBQigAwIBAgITMwAAAdB3CKrvoxfG3QABAAAB0DANBgkqhkiG9w0B
# AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzA1MjUxOTEy
# MTRaFw0yNDAyMDExOTEyMTRaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
# cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z
# MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046QTAwMC0wNUUwLUQ5NDcxJTAjBgNV
# BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQDfMlfn35fvM0XAUSmI5qiG0UxPi25HkSyBgzk3zpYO
# 311d1OEEFz0QpAK23s1dJFrjB5gD+SMw5z6EwxC4CrXU9KaQ4WNHqHrhWftpgo3M
# kJex9frmO9MldUfjUG56sIW6YVF6YjX+9rT1JDdCDHbo5nZiasMigGKawGb2HqD7
# /kjRR67RvVh7Q4natAVu46Zf5MLviR0xN5cNG20xwBwgttaYEk5XlULaBH5OnXz2
# eWoIx+SjDO7Bt5BuABWY8SvmRQfByT2cppEzTjt/fs0xp4B1cAHVDwlGwZuv9Rfc
# 3nddxgFrKA8MWHbJF0+aWUUYIBR8Fy2guFVHoHeOze7IsbyvRrax//83gYqo8c5Z
# /1/u7kjLcTgipiyZ8XERsLEECJ5ox1BBLY6AjmbgAzDdNl2Leej+qIbdBr/SUvKE
# C+Xw4xjFMOTUVWKWemt2khwndUfBNR7Nzu1z9L0Wv7TAY/v+v6pNhAeohPMCFJc+
# ak6uMD8TKSzWFjw5aADkmD9mGuC86yvSKkII4MayzoUdseT0nfk8Y0fPjtdw2Wne
# jl6zLHuYXwcDau2O1DMuoiedNVjTF37UEmYT+oxC/OFXUGPDEQt9tzgbR9g8HLtU
# fEeWOsOED5xgb5rwyfvIss7H/cdHFcIiIczzQgYnsLyEGepoZDkKhSMR5eCB6Kcv
# /QIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFDPhAYWS0oA+lOtITfjJtyl0knRRMB8G
# A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG
# Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy
# MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w
# XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy
# dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG
# A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD
# AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQCXh+ckCkZaA06SNW+qxtS9gHQp4x7G+gdi
# kngKItEr8otkXIrmWPYrarRWBlY91lqGiilHyIlZ3iNBUbaNEmaKAGMZ5YcS7IZU
# KPaq1jU0msyl+8og0t9C/Z26+atx3vshHrFQuSgwTHZVpzv7k8CYnBYoxdhI1uGh
# qH595mqLvtMsxEN/1so7U+b3U6LCry5uwwcz5+j8Oj0GUX3b+iZg+As0xTN6T0Qa
# 8BNec/LwcyqYNEaMkW2VAKrmhvWH8OCDTcXgONnnABQHBfXK/fLAbHFGS1XNOtr6
# 2/iaHBGAkrCGl6Bi8Pfws6fs+w+sE9r3hX9Vg0gsRMoHRuMaiXsrGmGsuYnLn3Aw
# TguMatw9R8U5vJtWSlu1CFO5P0LEvQQiMZ12sQSsQAkNDTs9rTjVNjjIUgoZ6XPM
# xlcPIDcjxw8bfeb4y4wAxM2RRoWcxpkx+6IIf2L+b7gLHtBxXCWJ5bMW7WwUC2Ll
# tburUwBv0SgjpDtbEqw/uDgWBerCT+Zty3Nc967iGaQjyYQH6H/h9Xc8smm2n6Vj
# ySRx2swnW3hr6Qx63U/xY9HL6FNhrGiFED7ZRKrnwvvXvMVQUIEkB7GUEeN6heY8
# gHLt0jLV3yzDiQA8R8p5YGgGAVt9MEwgAJNY1iHvH/8vzhJSZFNkH8svRztO/i3T
# vKrjb8ZxwjCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI
# hvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# MjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAy
# MDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
# bWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
# AQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25Phdg
# M/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPF
# dvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6
# GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBp
# Dco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50Zu
# yjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3E
# XzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0
# lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1q
# GFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ
# +QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PA
# PBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkw
# EgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxG
# NSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARV
# MFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj
# cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAK
# BggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC
# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX
# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v
# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI
# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG
# 9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0x
# M7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmC
# VgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449
# xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wM
# nosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDS
# PeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2d
# Y3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxn
# GSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+Crvs
# QWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokL
# jzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL
# 6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNN
# MIICNQIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEn
# MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOkEwMDAtMDVFMC1EOTQ3MSUwIwYDVQQD
# ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQC8
# t8hT8KKUX91lU5FqRP9Cfu9MiaCBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6HuRTTAiGA8yMDIzMDgwNzE1NTAz
# N1oYDzIwMjMwODA4MTU1MDM3WjB0MDoGCisGAQQBhFkKBAExLDAqMAoCBQDoe5FN
# AgEAMAcCAQACAgn7MAcCAQACAhNFMAoCBQDofOLNAgEAMDYGCisGAQQBhFkKBAIx
# KDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZI
# hvcNAQELBQADggEBACZlXGYildwi8A88nYqHN2HM8QyUZ5EXL5IPenhvckRdHdyV
# J/3DY3YgoI6CqwPJpipX1fuIzQkVYtLr/uoxJ6N3DNGTuYbJDaIRDq9IEHN0w5qG
# XGcCbtVZOlT87v6LpsZb2t7Ux0hmQi7tk163YL0ddGk+Fzc0PZPah3PLeH6Tzic4
# x2o7PXTjvOML4xfKsIsslRzZ3lBrCY65hu2M63z+gzOHFBa70UpDoWPqfeJXsD4m
# //BCuOrgEz6DX1gLxrDdkNBSfEKXDOJLhteWhVkENhnAd/NV4TsvR34vkTYngmQr
# 1mQjS42i1S5bIMBIA+YqchPVH+5rtnpAjjCkCkwxggQNMIIECQIBATCBkzB8MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNy
# b3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAdB3CKrvoxfG3QABAAAB0DAN
# BglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8G
# CSqGSIb3DQEJBDEiBCDqgzHsnaf2mNNt6VgLU86aXJhHQ32RqliN3law2qsuSTCB
# +gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIAiVQAZftNP/Md1E2Yw+fBXa9w6f
# jmTZ5WAerrTSPwnXMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh
# c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD
# b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw
# MTACEzMAAAHQdwiq76MXxt0AAQAAAdAwIgQgq7K70TUQtuVlHE2zaimieeN4nULR
# QcffxbgsiK4lvXcwDQYJKoZIhvcNAQELBQAEggIAaOLLrtCkl9bSxoQPVSjFAvKK
# QGpFccmtawrotc3GnaKedr0r+lX74V7+Tk5klkRvDD1Kl39qCi+dltIZ5OAWObBL
# twnCKyyMbrcpnnIMcJ95GG48ndTU5getbAlb+zh3BdRKyNIQU4Ukdzx/MBfHjJrE
# cWaX+zWi+c0i3qngFo3leJic0XNibV9SlZszF1bvvIIlnuAtPgbnfpWq7UeLsbL1
# JvvUvKmDfEvsnWui+GSzDWP+eZxgB8N3TiXP+zqEyrXt5qZ7QRwKbJrv2uYk4num
# EcPSCzT/YIzztlscryNYe7W3NpqqmLJ9CXbhTpow0rCgT7+T3Wy4tas/qxxzYoz8
# Bh+8h5FwzYpwI3snGIWJ4/KzMv4T5DsJDJQXxn1+65GgWFDjp19JR0YZKMjtXvHC
# BgmfYsNhQg9RvoQNS3xRkSniNVD67b6raBujhgUcYvdUKOYKfUk/QlGFG4F8lI05
# 2uncR2v3KRtookqffnKLwzNhu5ZSQ///h7CcWeFCqzxQw/ZmpoItqMAc7Xm88XB/
# 4XJIfIOPWfyeQzo5s9n9bFEl73gLjDTGXf23vCkNQsVfoOrpD1WVd/N/7t9pleDf
# 614U4TT2eMaQXtk4WGTLuKGiln2pAVCz2tGUz3dAUDzA5zwUy9fTEDjj520zWqNT
# /nJUAXH3LhIyGPJa7Sk=
# SIG # End signature block