AzStackHciStandaloneObservability/package/scripts/ExtensionHelper.psm1

##------------------------------------------------------------------
## <copyright file="ExtensionHelper.psm1" company="Microsoft">
## Copyright (C) Microsoft. All rights reserved.
## </copyright>
##------------------------------------------------------------------

#region Functions
function Get-ExtensionRootLocation { Split-Path -Parent $PSScriptRoot }

function Get-GmaPackageContentPath { Join-Path -Path $(Get-ExtensionRootLocation) -ChildPath 'bin\GMA' }

##---------------------------------------------------------------------------------------------------------------------
## NOTE: Purposefully importing this module after the above function definitions.
##---------------------------------------------------------------------------------------------------------------------
Import-Module (Join-Path -Path (Get-GmaPackageContentPath) -ChildPath 'GMATenantJsonHelper.psm1') `
    -DisableNameChecking `
    -Verbose:$false

#region Pre-installation validation functions

function Assert-SufficientMemoryAvailableForGMACache {
    $driveLetter = Get-SystemDriveLetter
    $availableMemoryOnStamp = ((Get-Volume -DriveLetter $driveLetter).SizeRemaining) / 1GB

    if ($availableMemoryOnStamp -lt $MiscConstants.AvailableMemoryLimitInGB) {
        return $ErrorConstants.InsufficientMemoryForGMACache.Name
    }
    
    return $true
}

function Invoke-PreInstallationValidation {
    param (
        [Parameter(Mandatory)]
        [System.String] $LogFile
    )

    $functionName = $MyInvocation.MyCommand.Name
    
    $validationFunctionNames = (
        $MiscConstants.ValidationFunctionNames.AssertSufficientMemoryAvailableForGMACache
    )
    
    Write-Log `
        -Message "$functionName : Performing pre-installation validation." `
        -LogFile $logFile
    
    foreach($validationFunction in $validationFunctionNames) {
        $validationResult = (Invoke-Expression $validationFunction)
        if ($validationResult -ne $true) {
            Write-Log `
                -Message "$validationFunction : $($ErrorConstants.$validationResult.Message)" `
                -LogFile $LogFile `
                -Level $MiscConstants.Level.Error
                
            throw $validationResult
        }

        Write-Log `
            -Message "$validationFunction : $validationResult" `
            -LogFile $LogFile `
    }
    
    Write-Log `
        -Message "$functionName : Pre-installation validation completed successfully." `
        -LogFile $logFile
}
#endregion Pre-installation validation functions

#region GCS functions
function Get-CloudName {
    Param (
        [Parameter(Mandatory)]
        [System.String] $LogFile
    )

    $functionName = $MyInvocation.MyCommand.Name
    Write-Log `
        -Message "$functionName : Entering..." `
        -LogFile $LogFile

    $publicSettings = Get-HandlerConfigSettings -LogFile $LogFile
    
    $cloudName = ""

    ## Check if any cloud value is passed through Config settings, if yes than use that
    if (-not ([System.String]::IsNullOrEmpty($publicSettings.cloudName)) -and `
        -not ([System.String]::IsNullOrWhiteSpace($publicSettings.cloudName))) {
        Write-Log `
            -Message "$functionName : CloudName from publicSetting = $($publicSettings.cloudName)" `
            -LogFile $LogFile

        $cloudName = $publicSettings.cloudName
    }

    Write-Log `
        -Message "$functionName : Exiting. CloudName: $cloudName" `
        -LogFile $LogFile

    return $cloudName
}

function Get-GcsEnvironmentName {
    Param (
        [Parameter(Mandatory)]
        [System.String] $CloudName,
        
        [Parameter(Mandatory)]
        [System.String] $LogFile
    )

    $functionName = $MyInvocation.MyCommand.Name
    
    Write-Log `
        -Message "$functionName : CloudName = $CloudName" `
        -LogFile $LogFile

    $gcsEnvironmentName = $MiscConstants.GCSEnvironment.Prod

    ## Check whether cloud name is Canary or TIP or reg key created for CI exists or not, if yes then change the GCSEnvironment to point to PPE instead.
    if ($CloudName -in $MiscConstants.CloudNames.AzureCanary -or `
        $CloudName -in $MiscConstants.CloudNames.AzurePPE -or `
        (Test-RegKeyExists -Path $MiscConstants.CIRegKey.Path -Name $MiscConstants.CIRegKey.Name) -eq $true) {
        $gcsEnvironmentName = $MiscConstants.GCSEnvironment.Ppe
    }
    elseif ($CloudName -in $MiscConstants.CloudNames.AzureUSGovernmentCloud) {
        $gcsEnvironmentName = $MiscConstants.GCSEnvironment.Fairfax
    }
    elseif ($CloudName -in $MiscConstants.CloudNames.AzureChinaCloud) {
        $gcsEnvironmentName = $MiscConstants.GCSEnvironment.Mooncake
    }

    Write-Log `
        -Message "$functionName : GcsEnvironmentName based on CloudName ($CloudName) = $gcsEnvironmentName" `
        -LogFile $LogFile

    return $gcsEnvironmentName
}

function Get-GcsRegionName {
    Param (
        [Parameter(Mandatory)]
        [System.String] $LogFile
    )

    $functionName = $MyInvocation.MyCommand.Name
    Write-Log `
        -Message "$functionName : Entering..." `
        -LogFile $LogFile

    ## Check if any region value is passed through Config settings, if yes than use that
    $publicSettings = Get-HandlerConfigSettings -LogFile $LogFile

    if (-not ([System.String]::IsNullOrEmpty($publicSettings.region)) -and `
        -not ([System.String]::IsNullOrWhiteSpace($publicSettings.region))) {
        Write-Log `
            -Message "$functionName : RegionName from publicSetting = $($publicSettings.region)" `
            -LogFile $LogFile

        $gcsRegionName = $publicSettings.region
    }

    Write-Log `
        -Message "$functionName : Exiting. GCSRegionName: $gcsRegionName" `
        -LogFile $LogFile

    return $gcsRegionName
}

function Wait-ForGcsConfigSync() {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [System.String] $LogFile,

        [Parameter(Mandatory = $false)]
        [int] $TimeInSeconds = 60
    )

    $functionName = $MyInvocation.MyCommand.Name
    Write-Log `
        -Message "$functionName : Entering. TimeOut: $TimeInSeconds" `
        -LogFile $LogFile

    Write-Host "Going to wait for GCSConfig sync $TimeInSeconds"
    Start-Sleep -Seconds $TimeInSeconds
    $cacheDir = Get-GmaCacheDirectories
    $gcsConfigFiles = Get-ChildItem -Path $cacheDir.GMACache -Filter GcsConfig -Recurse
    if($gcsConfigFiles.Count -eq 0)
    {
        Write-Log `
            -Message "$functionName : $($ErrorConstants.GcsConfigFilesNotFound.Message)" `
            -LogFile $LogFile `
            -Level $MiscConstants.Level.Error
    }

    Write-Log `
        -Message "$functionName : Exiting. GCSCongfile count: $($gcsConfigFiles.Count)" `
        -LogFile $LogFile
}

#endregion GCS functions

#region Handler/Extension functions
## Get sequence number from env variable provided by Agent
function Get-ConfigSequenceNumber {if ($null -eq $env:ConfigSequenceNumber) { 0 } else { $env:ConfigSequenceNumber }}

function Get-HandlerConfigSettings {
    param (
        [Parameter(Mandatory)]
        [System.String] $LogFile
    )

    $functionName = $MyInvocation.MyCommand.Name
    $handlerEnvInfo = Get-HandlerEnvInfo -LogFile $LogFile

    if (-not (Test-Path $handlerEnvInfo.configFolder -PathType Container)) {
        Write-Log `
            -Message "$functionName : $($ErrorConstants.ConfigFolderDoesNotExist.Message)" `
            -LogFile $LogFile `
            -Level $MiscConstants.Level.Error

        throw $ErrorConstants.ConfigFolderDoesNotExist.Name
    }

    $configFile = Get-ChildItem -Path $handlerEnvInfo.configFolder | Sort-Object CreationTime -Descending | Select-Object -First 1
    # Parse config file to read parameters
    $configJson = Get-Content -Path $configFile.FullName -Raw | ConvertFrom-Json
    return $configJson.runtimeSettings[0].handlerSettings.publicSettings
}

function Get-HandlerEnvInfo {
    <#
    Sample HandlerEnvironment.json content:
    [
        {
            "handlerEnvironment": {
                "configFolder": "C:\\Packages\\Plugins\\Microsoft.AzureStack.Observability.Observability\\0.0.0.4\\RuntimeSettings",
                "deploymentid": "",
                "heartbeatFile": "C:\\Packages\\Plugins\\Microsoft.AzureStack.Observability.Observability\\0.0.0.4\\status\\HeartBeat.Json",
                "hostResolverAddress": "",
                "instance": "",
                "logFolder": "C:\\ProgramData\\GuestConfig\\extension_logs\\Microsoft.AzureStack.Observability.Observability",
                "rolename": "",
                "statusFolder": "C:\\Packages\\Plugins\\Microsoft.AzureStack.Observability.Observability\\0.0.0.4\\status"
            },
            "name": "Microsoft.RecoveryServices.Test.AzureSiteRecovery",
            "version": "1"
        }
    ]
    #>


    $envFile = "$(Get-ExtensionRootLocation)\HandlerEnvironment.json"

    if (-not (Test-Path $envFile -PathType Leaf)) {
        throw $ErrorConstants.HandlerEnvJsonDoesNotExist.Name
    }


    # Read handler config
    $envJson = Get-Content -Path $envFile -Raw | ConvertFrom-Json
    if ($envJson -is [System.Array]) {
        $envJson = $envJson[0]
    }

    return $envJson.handlerEnvironment
}

function Get-HandlerHeartBeatFile {
    $handlerEnvInfo = Get-HandlerEnvInfo -LogFile $LogFile
    return $handlerEnvInfo.heartbeatFile
}

function Get-HandlerLogFile { Join-Path $(Get-LogFolderPath) -ChildPath $MiscConstants.HandlerLogFileName }

function Get-LogFolderPath {
    $handlerEnvInfo = Get-HandlerEnvInfo -LogFile $LogFile

    if (-not (Test-Path $handlerEnvInfo.logFolder -PathType Container)) {
        throw $ErrorConstants.LogFolderDoesNotExist.Name
    }

    return $handlerEnvInfo.logFolder
}

function Get-StatusFolderPath {
    $handlerEnvInfo = Get-HandlerEnvInfo -LogFile $LogFile

    if (-not (Test-Path $handlerEnvInfo.statusFolder -PathType Container)) {
        throw $ErrorConstants.StatusFolderDoesNotExist.Name
    }

    return $handlerEnvInfo.statusFolder
}
#endregion Handler/Extension functions

#region Misc functions
function Get-GmaCacheDirectories {
    $logFolderPath = Get-LogFolderPath

    return [ordered] @{
        GMACache = "$logFolderPath\GMACache"
        DiagnosticsCache = "$logFolderPath\GMACache\DiagnosticsCache"
        HealthCache = "$logFolderPath\GMACache\HealthCache"
        JsonDropLocation = "$logFolderPath\GMACache\JsonDropLocation"
        MonAgentHostCache = "$logFolderPath\GMACache\MonAgentHostCache"
        TelemetryCache = "$logFolderPath\GMACache\TelemetryCache"
        MetricsCache = "$logFolderPath\GMACache\MetricsCache"
    }
}

function Set-GmaCacheDirectories {
    param (
        [Parameter(Mandatory)]
        [System.String] $LogFile
    )

    $functionName = $MyInvocation.MyCommand.Name

    $gmaCacheDirectories = Get-GmaCacheDirectories

    foreach ($directory in $gmaCacheDirectories.Values) {
        if (Test-Path $directory -PathType Container) {
            Write-Log `
                -Message "$functionName : $directory exists already." `
                -LogFile $LogFile
        }
        else {            
            New-Item `
                -Path $directory `
                -ItemType Directory `
                -Force `
                | Out-Null
        }
    }

    return $gmaCacheDirectories
}

function Set-CacheDirectories {
    ## NOTE: Remove the above function when adding Diagnostics cache locations as this is supposed to be generic function to create cache dirs.
    param (
        [Parameter(Mandatory = $true)]
        [System.String] $LogFile
    )

    $gmaCacheDirectories = Get-GmaCacheDirectories

    foreach ($directory in $gmaCacheDirectories.Values) {
        Set-Directory `
            -Path $directory `
            -LogFile $logFile
    }

    return $gmaCacheDirectories
}

function Set-Directory {
    param (
        [Parameter(Mandatory = $true)]
        [System.String] $Path,

        [Parameter(Mandatory = $true)]
        [System.String] $LogFile
    )

    $functionName = $MyInvocation.MyCommand.Name
    
    if (-not (Test-Path $Path -PathType Container)) {
        New-Item `
            -Path $Path `
            -ItemType Directory `
            -Force `
            | Out-Null
        
        Write-Log `
            -Message "$functionName : Directory path ($Path) created." `
            -LogFile $LogFile
    }
    else {
        Write-Log `
            -Message "$functionName : Directory path ($Path) exists already." `
            -LogFile $LogFile
    }
}

function Get-UtcExporterPackageContentPath { Join-Path -Path $(Get-ExtensionRootLocation) -ChildPath "\bin\UtcExporter" }

function Get-WatchdogPackageContentPath { Join-Path -Path $(Get-ExtensionRootLocation) -ChildPath "\bin\MAWatchdog" }

function Get-SystemDriveLetter { ($env:SystemDrive).split(':')[0] }

function Set-Status {
    Param (
        
        [Parameter(Mandatory)]
        [System.String] $Name,

        [Parameter(Mandatory)]
        [System.String] $Operation,

        [Parameter(Mandatory)]
        [System.String] $Message,

        [Parameter(Mandatory)]
        [ValidateSet("transitioning", "error", "success", "warning")]
        [System.String] $Status,

        [Parameter(Mandatory)]
        [System.Int16] $Code
    )
    
    & "$PSScriptRoot\ReportStatus.ps1" `
        -Name $Name `
        -Operation $Operation `
        -Status $Status `
        -Code $Code `
        -Message $Message
}
#endregion Misc functions

#region UTC setup functions
Function Initialize-UTCSetup {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $False)]
        [System.String] $LogFile
    )

    $functionName = $MyInvocation.MyCommand.Name

    try {
        Write-Log `
            -Message "$functionName : Initializing UTC setup." `
            -LogFile $logFile

        #region Stop diagtrack service
        Stop-ServiceForObservability `
            -ServiceName $MiscConstants.ObsServiceDetails.DiagTrack.Name `
            -LogFile $logFile
        #endregion Stop diagtrack service
    
        ## Create the UTC exporter destination folder (if not present) and copy the UtcGenevaExporter dll in it.
        Write-Log `
            -Message "$functionName : Create folder for UTCExporterdll and place the respective binary in it." `
            -LogFile $logFile

        $utcExporterDllName = $MiscConstants.UtcxporterDllName
        $utcExporterSourcePath = Join-Path -Path (Get-UtcExporterPackageContentPath) -ChildPath $utcExporterDllName
    
        $utcExporterDestinationDirectory = $MiscConstants.UtcExporterDestinationDirectory
    
        Set-Directory `
            -Path $utcExporterDestinationDirectory `
            -LogFile $logFile
    
        Copy-Item `
            -Path $utcExporterSourcePath `
            -Destination $utcExporterDestinationDirectory `
            -Force `
            | Out-Null
    
        $utcExporterDestinationPath = Join-Path -Path $utcExporterDestinationDirectory -ChildPath $utcExporterDllName
    
        if (Test-Path $utcExporterDestinationPath) {
            Write-Log `
                -Message "$functionName : Successfully copied '$utcExporterDllName' to '$utcExporterDestinationPath'." `
                -LogFile $logFile
        }
        else {
            Write-Log `
                -Message "$functionName : Failed to copy '$utcExporterDllName' to '$utcExporterDestinationPath'." `
                -LogFile $logFile `
                -Level $MiscConstans.Status.Error
    
            throw $ErrorConstants.CannotCopyUtcExporterDll.Name
        }
    
        #region Create reg keys
        Set-RegKey `
            -Path $MiscConstants.DiagTrackExportersRegKeyPath `
            -LogFile $logFile `
            -CreatePathOnly
    
        Set-RegKey `
            -Path $MiscConstants.GenevaExporterRegKey.Path `
            -LogFile $logFile `
            -CreatePathOnly
    
        Set-RegKey `
            -Path $MiscConstants.DiagTrackRegKey.Path `
            -Name $MiscConstants.DiagTrackRegKey.Name `
            -PropertyType $MiscConstants.DiagTrackRegKey.PropertyType `
            -Value $MiscConstants.DiagTrackRegKey.Value `
            -LogFile $logFile
    
        Set-RegKey `
            -Path $MiscConstants.GenevaExporterRegKey.Path `
            -Name $MiscConstants.GenevaExporterRegKey.Name `
            -PropertyType $MiscConstants.GenevaExporterRegKey.PropertyType `
            -Value $utcExporterDestinationPath `
            -LogFile $logFile
    
        Set-RegKey `
            -Path $MiscConstants.TestHooksRegKey.Path `
            -Name $MiscConstants.TestHooksRegKey.Name `
            -PropertyType $MiscConstants.TestHooksRegKey.PropertyType `
            -Value $MiscConstants.TestHooksRegKey.Value `
            -LogFile $logFile
    
        Set-RegKey `
            -Path $MiscConstants.GenevaExporterRegKey.Path `
            -Name $MiscConstants.GenevaNamespaceRegKey.Name `
            -PropertyType $MiscConstants.GenevaNamespaceRegKey.PropertyType `
            -Value $MiscConstants.GenevaNamespaceRegKey.Value `
            -LogFile $logFile
        #endregion Create reg keys
    
        #region Start diagtrack service
        Start-ServiceForObservability `
            -ServiceName $MiscConstants.ObsServiceDetails.DiagTrack.Name `
            -LogFile $logFile
        #endregion Start diagtrack service
    
        Write-Log `
            -Message "$functionName : Successfully initialized UTC setup." `
            -LogFile $logFile

    }
    finally {
        if ((Get-Service $MiscConstants.ObsServiceDetails.DiagTrack.Name).Status -eq "Stopped") {
            Write-Log `
                -Message "$functionName : Starting $($MiscConstants.ObsServiceDetails.DiagTrack.Name) service after it was stopped." `
                -LogFile $LogFile
            
            Start-Service `
                -Name $MiscConstants.ObsServiceDetails.DiagTrack.Name `
                -ErrorAction SilentlyContinue `
                -Verbose:$false `
                | Out-Null
        }
    }
}

Function Clear-UTCSetup {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $False)]
        [System.String] $LogFile
    )

    $functionName = $MyInvocation.MyCommand.Name

    try {
        Write-Log `
            -Message "$functionName : Cleaning up UTC related artifacts." `
            -LogFile $logFile

        #region Stop diagtrack service
        Stop-ServiceForObservability `
            -ServiceName $MiscConstants.ObsServiceDetails.DiagTrack.Name `
            -LogFile $LogFile
        #endregion Stop diagtrack service

        #region Remove UtcExporter dll
        Write-Log `
            -Message "$functionName : Removing UTCExporterdll file and its folder." `
            -LogFile $logFile
        
        $utcExporterDestinationPath = Join-Path -Path $MiscConstants.UtcExporterDestinationDirectory -ChildPath $MiscConstants.UtcxporterDllName

        if (Test-Path -Path $utcExporterDestinationPath) {

            Remove-Item `
                -Path $utcExporterDestinationPath `
                -Force `
                -Verbose:$false `
                | Out-Null

            Write-Log `
                -Message "$functionName : Removed file '$utcExporterDestinationPath'." `
                -LogFile $logFile


            if ((Get-ChildItem -Path $MiscConstants.UtcExporterDestinationDirectory | Measure-Object).Count -eq 0) {
                Remove-Item `
                    -Path $MiscConstants.UtcExporterDestinationDirectory `
                    -Force `
                    -Verbose:$false `
                    | Out-Null

                Write-Log `
                    -Message "$functionName : Removed directory '$($MiscConstants.UtcExporterDestinationDirectory)'." `
                    -LogFile $logFile
            }

            Write-Log `
                -Message "$functionName : Removed UTCExporterdll file '$utcExporterDestinationPath' and its folder path '$($MiscConstants.UtcExporterDestinationDirectory)'." `
                -LogFile $logFile
        }
        else {
            Write-Log `
                -Message "$functionName : UTCExporter dll does not exists at path '$utcExporterDestinationPath'. Nothing to remove." `
                -LogFile $logFile
        }
        #endregion Remove UtcExporter dll

        #region Remove reg keys
        Remove-RegKey `
            -Path $MiscConstants.DiagTrackRegKey.Path `
            -Name $MiscConstants.DiagTrackRegKey.Name `
            -LogFile $logFile

        Remove-RegKey `
            -Path $MiscConstants.TestHooksRegKey.Path `
            -Name $MiscConstants.TestHooksRegKey.Name `
            -LogFile $logFile

        Remove-RegKey `
            -Path $MiscConstants.GenevaExporterRegKey.Path `
            -Name $MiscConstants.GenevaExporterRegKey.Name `
            -LogFile $logFile

        Remove-RegKey `
            -Path $MiscConstants.GenevaExporterRegKey.Path `
            -Name $MiscConstants.GenevaNamespaceRegKey.Name `
            -LogFile $logFile

        Remove-RegKey `
            -Path $MiscConstants.GenevaExporterRegKey.Path `
            -LogFile $logFile `
            -RemovePathOnly
        #endregion Remove reg keys

        #region Start diagtrack service
        Start-ServiceForObservability `
            -ServiceName $MiscConstants.ObsServiceDetails.DiagTrack.Name `
            -LogFile $LogFile
        #endregion Start diagtrack service

        Write-Log `
            -Message "$functionName : Cleaned up artifacts related to UTC setup." `
            -Logfile $logFile
    }
    finally {
        if ((Get-Service $MiscConstants.ObsServiceDetails.DiagTrack.Name).Status -eq "Stopped") {
            Write-Log `
                -Message "$functionName : Starting $($MiscConstants.ObsServiceDetails.DiagTrack.Name) service after it was stopped." `
                -LogFile $LogFile
            
            Start-Service `
                -Name $MiscConstants.ObsServiceDetails.DiagTrack.Name `
                -ErrorAction SilentlyContinue `
                -Verbose:$false `
                | Out-Null
        }
    }
}
#endregion UTC setup functions

#region Registry functions
function  Set-RegKey {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [System.String] $Path,

        [Parameter(Mandatory = $false)]
        [System.String] $Name,
        
        [Parameter(Mandatory = $false)]
        [System.String] $PropertyType,
        
        [Parameter(Mandatory = $false)]
        [System.String] $Value,

        [Parameter(Mandatory = $true)]
        [System.String] $LogFile,

        [Parameter(Mandatory=$false)]
        [System.Management.Automation.SwitchParameter] $CreatePathOnly
    )

    $functionName = $MyInvocation.MyCommand.Name

    if ($CreatePathOnly) {
        if (-not (Test-Path -Path $Path)) {
            New-Item `
                -Path $Path `
                -Force `
                -Verbose:$false `
                | Out-Null
            
            Write-Log `
                -Message "$functionName : Created RegKey path ($Path)." `
                -LogFile $LogFile
        }
        else {
            Write-Log `
                -Message "$functionName : RegKey path ($Path) exists already." `
                -LogFile $LogFile
        }
    }
    else {
        if (-not (Test-RegKeyExists -Path $Path -Name $Name)) {
            New-ItemProperty `
                -Path $Path `
                -Name $Name `
                -PropertyType $PropertyType `
                -Value $Value `
                -Force `
                | Out-Null
            
            Write-Log `
                -Message "$functionName : Created registry key with path ($Path), name ($Name) and value ($Value)." `
                -LogFile $LogFile
        }
        else {
            Write-Log `
                -Message "$functionName : RegKey path ($Path) and name ($Name) exists already." `
                -LogFile $LogFile
        }
    }
}

Function  Remove-RegKey {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $True)]
        [System.String] $Path,

        [Parameter(Mandatory = $False)]
        [System.String] $Name,

        [Parameter(Mandatory = $False)]
        [System.String] $LogFile,

        [Parameter(Mandatory=$False)]
        [System.Management.Automation.SwitchParameter] $RemovePathOnly
    )

    $functionName = $MyInvocation.MyCommand.Name

    if ($RemovePathOnly) {
        if (Test-Path -Path $Path) {
            Remove-Item `
                -Path $Path `
                -Force `
                -Verbose:$false `
                | Out-Null

            Write-Log `
                -Message "$functionName : Path ($Path) removed successfully." `
                -LogFile $LogFile
        }
        else {
            Write-Log `
                -Message "$functionName : Path ($Path) does not exists. Nothing to remove" `
                -LogFile $LogFile
        }
    }
    else {
        if (Test-RegKeyExists -Path $Path -Name $Name) {
            
            Remove-ItemProperty `
                -Path $Path `
                -Name $Name `
                -Force `
                -Verbose:$false `
                | Out-Null
            
            Write-Log `
                -Message "$functionName : RegKey path ($Path) and name ($Name) removed successfully." `
                -LogFile $LogFile
        }
        else {
            Write-Log `
                -Message "$functionName : RegKey path ($Path) and name ($Name) does not exists. Nothing to remove" `
                -LogFile $LogFile
        }
    }
}
#endregion Registry functions

#region Scheduled task functions
function Enable-ObsScheduledTask {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [System.String] $LogFile
    )

    $functionName = $MyInvocation.MyCommand.Name

    Write-Log `
        -Message "$functionName : Enabling ObsScheduledTask ($($MiscConstants.ObsScheduledTaskDetails.TaskName))." `
        -LogFile $LogFile

    Write-Log `
        -Message "$functionName : $(Enable-ScheduledTask `
            -TaskPath $MiscConstants.ObsScheduledTaskDetails.TaskPath `
            -TaskName $MiscConstants.ObsScheduledTaskDetails.TaskName `
            -ErrorAction Stop)"
 `
        -LogFile $LogFile

    Write-Log `
        -Message "$functionName : Successfully enabled obs scheduled task with name $($MiscConstants.ObsScheduledTaskDetails.TaskName)." `
        -LogFile $LogFile
}

function Disable-ObsScheduledTask {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [System.String] $LogFile
    )

    $functionName = $MyInvocation.MyCommand.Name
    
    Write-Log `
        -Message "$functionName : Disabling ObsScheduledTask ($($MiscConstants.ObsScheduledTaskDetails.TaskName))." `
        -LogFile $LogFile
    

    Write-Log `
        -Message "$functionName : $(Disable-ScheduledTask `
            -TaskPath $MiscConstants.ObsScheduledTaskDetails.TaskPath `
            -TaskName $MiscConstants.ObsScheduledTaskDetails.TaskName `
            -ErrorAction Stop)"
 `
        -LogFile $LogFile


    Write-Log `
        -Message "$functionName : Successfully disabled obs scheduled task with name $($MiscConstants.ObsScheduledTaskDetails.TaskName)." `
        -LogFile $LogFile
}

function Remove-ObsScheduledTaskAndPath {
    param (
        [Parameter(Mandatory)]
        [System.String] $LogFile
    )

    $functionName = $MyInvocation.MyCommand.Name

    $trimmedTaskPath = $MiscConstants.ObsScheduledTaskDetails.TaskPath.TrimEnd('\')
    $tasks = Get-ScheduledTask -TaskPath "$trimmedTaskPath\*"
    if ($tasks)
    {
        foreach($task in $tasks) {
            if($task.TaskName -eq $MiscConstants.ObsScheduledTaskDetails.TaskName) {
                Unregister-ScheduledTask -TaskName $task.TaskName -TaskPath $task.TaskPath -Confirm:$false | Out-Null

                Write-Log `
                    -Message "$functionName : Successfully removed scheduled task $($task.TaskName) from path $($task.TaskPath)." `
                    -LogFile $LogFile
            }
        }
    }
    else
    {
        Write-Log `
            -Message "$functionName : No scheduled tasks at task path $trimmedTaskPath found to delete." `
            -LogFile $LogFile
    }
}
#endregion Scheduled task functions

#region Windows service functions
function Register-ServiceForObservability {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [System.String] $ServiceName,

        [Parameter(Mandatory = $true)]
        [System.String] $ServiceDisplayName,

        [Parameter(Mandatory = $true)]
        [System.String] $ServiceBinaryFilePath,

        [Parameter(Mandatory = $false)]
        [System.String] $ServiceStartupType = 'Manual',

        [Parameter(Mandatory = $false)]
        [System.String] $LogFile
    )

    $functionName = $MyInvocation.MyCommand.Name

    try {
        Write-Log `
            -Message "$functionName : Starting registration of service '$ServiceName'." `
            -LogFile $logFile
        
        Write-Log `
            -Message "$functionName : Configuring service '$ServiceName' from path '$ServiceBinaryFilePath'" `
            -LogFile $LogFile
        
        if (Get-Service $ServiceName -ErrorAction SilentlyContinue)
        {
            Write-Log `
                -Message "$functionName : Service '$ServiceName' already registered." `
                -LogFile $LogFile
        }
        else
        {
            New-Service `
                -Name $ServiceName `
                -BinaryPathName $ServiceBinaryFilePath `
                -DisplayName $ServiceDisplayName `
                -StartupType $ServiceStartupType `
                -ErrorAction Stop `
                -Verbose:$false `
                | Out-Null
        }
    
        Write-Log `
            -Message "$functionName : Registration of service '$ServiceName' with display name '$ServiceDisplayName' completed." `
            -LogFile $logFile
    }
    catch {
        Write-Log `
            -Message "$functionName : $($ErrorConstants.CannotRegisterService.Message) Service Name: '$ServiceName. Exception: $_" `
            -LogFile $LogFile `
            -Level $MiscConstants.Level.Error
    
        throw $ErrorConstants.CannotRegisterService.Name
    }
}

function Start-ServiceForObservability {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [System.String] $ServiceName,

        [Parameter(Mandatory = $false)]
        [System.String] $LogFile,

        [Parameter(Mandatory = $false)]
        [int] $Retries = $MiscConstants.Retries
    )

    $functionName = $MyInvocation.MyCommand.Name

    Write-Log `
        -Message "$functionName : Starting service '$ServiceName'." `
        -LogFile $logFile

    # Start MA Watchdog Agent Service
    $retryCount = $Retries
    $serviceStatus = (Get-Service $ServiceName).Status

    if ($serviceStatus -eq "Running") {
        Write-Log `
            -Message "$functionName : Service '$ServiceName' running already." `
            -LogFile $LogFile
        
        return
    }

    while(($serviceStatus -ne "Running") -and ($retryCount -gt 0)) {     
        Start-Service $ServiceName `
            -WarningAction SilentlyContinue `
            -WarningVariable $startSvcWarn
        
        if ($null -ne $startSvcWarn) {
            Write-Log `
                -Message "$functionName : $startSvcWarn" `
                -Level $MiscConstants.Level.Warning `
                -LogFile $LogFile
        }

        Write-Log `
            -Message "$functionName : Waiting for service '$ServiceName' to start..." `
            -LogFile $LogFile

        Start-Sleep -Seconds 5

        $serviceStatus = (Get-Service $ServiceName).Status
        $retryCount--
    }

    if ($serviceStatus -ne "Running") {
        Write-Log `
            -Message "$functionName : $($ErrorConstants.CannotStartService.Message) Service Name: '$ServiceName'" `
            -LogFile $LogFile `
            -Level $MiscConstants.Level.Error

        throw $ErrorConstants.CannotStartService.Name
    }

    Write-Log `
        -Message "$functionName : Successfully started service '$ServiceName'." `
        -LogFile $LogFile
}

function Stop-ServiceForObservability {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [System.String] $ServiceName,

        [Parameter(Mandatory = $false)]
        [System.String] $LogFile,

        [Parameter(Mandatory = $false)]
        [int] $Retries = $MiscConstants.Retries
    )

    $functionName = $MyInvocation.MyCommand.Name

    Write-Log `
        -Message "$functionName : Stopping service '$ServiceName'." `
        -LogFile $logFile

    # Stop MA Watchdog Agent Service
    $retryCount = $Retries
    $serviceStatus = (Get-Service $ServiceName).Status

    if ($serviceStatus -eq "Stopped") {
        Write-Log `
            -Message "$functionName : Service '$ServiceName' stopped already." `
            -LogFile $LogFile
        
        return
    }

    while (($serviceStatus -ne "Stopped") -and ($retryCount -gt 0)) {
        Stop-Service $ServiceName `
            -WarningAction SilentlyContinue `
            -WarningVariable $stopSvcWarn
        
        if ($null -ne $stopSvcWarn) {
            Write-Log `
                -Message "$functionName : $stopSvcWarn" `
                -Level $MiscConstants.Level.Warning `
                -LogFile $LogFile
        }

        Write-Log `
            -Message "$functionName : Waiting for service '$ServiceName' to stop..." `
            -LogFile $LogFile

        Start-Sleep -Seconds 5

        $serviceStatus = (Get-Service $ServiceName).Status
        $retryCount--
    }

    if ($serviceStatus -ne "Stopped") {
        Write-Log `
            -Message "$functionName : $($ErrorConstants.CannotStopService.Message) Service Name: '$ServiceName'" `
            -LogFile $LogFile `
            -Level $MiscConstants.Level.Error

        throw $ErrorConstants.CannotStopService.Name
    }

    Write-Log `
        -Message "$functionName : Successfully stopped service '$ServiceName'." `
        -LogFile $LogFile
}

Function Unregister-ServiceForObservability {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true)]
        [System.String] $ServiceName,

        [Parameter(Mandatory = $true)]
        [System.String] $LogFile
    )

    $functionName = $MyInvocation.MyCommand.Name

    Write-Log `
        -Message "$functionName : Unregistering service '$ServiceName'." `
        -LogFile $LogFile
    
    if (Get-Service $ServiceName -ErrorAction SilentlyContinue) {
        Stop-ServiceForObservability -ServiceName $ServiceName -LogFile $LogFile
    }

    Write-Log `
        -Message "$functionName : $(sc.exe delete $ServiceName -Verbose)" `
        -LogFile $LogFile

    Write-Log `
        -Message "$functionName : Successfully unregisteried service '$ServiceName'." `
        -LogFile $LogFile
}

function Set-StandaloneScenarioRegistry {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true)]
        [System.String] $LogFile
    )

    Set-RegKey `
    -Path $MiscConstants.GMAScenarioRegKey.Path `
    -Name $MiscConstants.GMAScenarioRegKey.Name `
    -PropertyType $MiscConstants.GMAScenarioRegKey.PropertyType `
    -Value $MiscConstants.GMAScenarioRegKey.OneP `
    -CreatePathOnly `
    -LogFile $LogFile

    Set-RegKey `
    -Path $MiscConstants.GMAScenarioRegKey.Path `
    -Name $MiscConstants.GMAScenarioRegKey.Name `
    -PropertyType $MiscConstants.GMAScenarioRegKey.PropertyType `
    -Value $MiscConstants.GMAScenarioRegKey.OneP `
    -LogFile $LogFile
}

#endregion

#region GMA Utility functions

#endregion

#endregion Functions

#region Exports
Export-ModuleMember -Function Get-ExtensionRootLocation
Export-ModuleMember -Function Get-GmaPackageContentPath

# Pre-installation validation functions
Export-ModuleMember -Function Invoke-PreInstallationValidation

# GCS functions
Export-ModuleMember -Function Get-CloudName
Export-ModuleMember -Function Get-GcsEnvironmentName
Export-ModuleMember -Function Get-GcsRegionName
Export-ModuleMember -Function Wait-ForGcsConfigSync

# Handler/Extension functions
Export-ModuleMember -Function Get-ConfigSequenceNumber
Export-ModuleMember -Function Get-HandlerConfigSettings
Export-ModuleMember -Function Get-HandlerEnvInfo
Export-ModuleMember -Function Get-HandlerHeartBeatFile
Export-ModuleMember -Function Get-HandlerLogFile
Export-ModuleMember -Function Get-LogFolderPath
Export-ModuleMember -Function Get-StatusFolderPath

# Misc functions
Export-ModuleMember -Function Get-GmaCacheDirectories
Export-ModuleMember -Function Set-GmaCacheDirectories
Export-ModuleMember -Function Set-Directory
Export-ModuleMember -Function Get-UtcExporterPackageContentPath
Export-ModuleMember -Function Get-WatchdogPackageContentPath
Export-ModuleMember -Function Get-SystemDriveLetter
Export-ModuleMember -Function Set-Status
Export-ModuleMember -Function Set-StandaloneScenarioRegistry

# UTC setup functions
Export-ModuleMember -Function Initialize-UTCSetup
Export-ModuleMember -Function Clear-UTCSetup

# Registry functions
Export-ModuleMember -Function Set-RegKey
Export-ModuleMember -Function Remove-RegKey

# Scheduled task functions
Export-ModuleMember -Function Enable-ObsScheduledTask
Export-ModuleMember -Function Disable-ObsScheduledTask
Export-ModuleMember -Function Remove-ObsScheduledTaskAndPath

# Windows service functions
Export-ModuleMember -Function Register-ServiceForObservability
Export-ModuleMember -Function Start-ServiceForObservability
Export-ModuleMember -Function Stop-ServiceForObservability
Export-ModuleMember -Function Unregister-ServiceForObservability
#endregion Exports
# SIG # Begin signature block
# MIInwgYJKoZIhvcNAQcCoIInszCCJ68CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBdkXq+guNBXJd9
# J8gwIwzJfbUFe2A4mVTRLKP1jQzbqKCCDXYwggX0MIID3KADAgECAhMzAAADTrU8
# 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
# /Xmfwb1tbWrJUnMTDXpQzTGCGaIwghmeAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAANOtTx6wYRv6ysAAAAAA04wDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIEjws4OIo+nso8vP8M0bdkxv
# bpddyxBaxNSPaXxgMSr5MEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEAQSHcTQEj7KnJvZJ5XdAX9yX3c84XNRWhpbPz0GEjZEdrD4Hsf5Lz/B1y
# fWX6x57DeZyPiDAGXClQGv8Y1gqxqKt8oypELcLyGAekvoPbEfOkBPh0IUqLv/XA
# rXmS0vea5G5cgDwbO5pqtMsyi2+ajNMGMrf/NWxDO0RHsir+5oU65vTGd2VyY9Aq
# kAgz+YjYLVLJKhyyEimurh2bTLMABFEc4lPVS5v+wY4/B2upLOWpSkgHPpvmWR7L
# XqKPod5ySgVQww//GdeVvgee3OJPjTJbt8FnyhgVPGRuL59RTRmi9TCRhF00cFFe
# 63KX46IdPsRsbjyyUm90fqaoxuPCQaGCFywwghcoBgorBgEEAYI3AwMBMYIXGDCC
# FxQGCSqGSIb3DQEHAqCCFwUwghcBAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFZBgsq
# hkiG9w0BCRABBKCCAUgEggFEMIIBQAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCDDL+HWc+Je7jEYtkj02QsGLUeY6R7D8krcfAzH8uUSLgIGZD/Uapni
# GBMyMDIzMDUxMDE3MDExNS45NzNaMASAAgH0oIHYpIHVMIHSMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl
# bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNO
# OjNCRDQtNEI4MC02OUMzMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT
# ZXJ2aWNloIIRezCCBycwggUPoAMCAQICEzMAAAG0+4AIRAXSLfoAAQAAAbQwDQYJ
# KoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMjIw
# OTIwMjAyMjA5WhcNMjMxMjE0MjAyMjA5WjCB0jELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3Bl
# cmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjozQkQ0LTRC
# ODAtNjlDMzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCC
# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALRHpp5lBzJCH7zortuyvOmW
# 8FoZLBsFe9g5dbhnaq9qSpvpn86E/mJ4JKvWixH/lw7QA8gPtiiGVNIjvFhu/XiY
# 889vX5WaQSmyoPMZdj9zvXa5XrkMN05zXzTePkCIIzF6RN7cTxezOyESymTIjrdx
# X5BVlZolyQAOxNziMCYKYYNPbYd0786fDE/PhzrRt23a0Xf8trvFa0LEEy2YlcE2
# eqg2CjU/D0GZe8Ra0kjt0M12vdS4qWZ2Dpd7IhiQwnntQWu19Ytd3UBR8SpeRX+C
# cw3bjgWfOXtla6chctWt2shlMwayMOfY4TG4yMPWFXELfZFFp7cgpjZNeVsmwkvo
# V6RAwy1Y9V+VvbJ5qFtartN/rp6a0I1kGlbjuwX3L0HTVXcikqgHistXk9h3HOZ9
# WgFXlxZurG1SZmcz0BEEdya+1vGHE45KguYU9qq2LiHGBjn9z4+DqnV5tUKobsLb
# JMb4r+8st2fj8SacSsftnusxkWqEJiJS34P2uNlzVR03+ls6+ZO0NcO79LgP7BbI
# MipiOx8yh19PMQw0piaKFwOW7Q+gdJcfy6rOkG+CrYZwOzdiBHSebIzCIch2cAa+
# 38w7JFP/koKdlJ36qzdVXWv4G/qZpWycIvDKYbxJWM40+z2Stg5uHqK3I8e09kFX
# txCHpS7hm8c8m25WaEU5AgMBAAGjggFJMIIBRTAdBgNVHQ4EFgQUy0SF5fGUuDqc
# uxIot07eOMwy2X4wHwYDVR0jBBgwFoAUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYD
# VR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j
# cmwvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwG
# CCsGAQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIw
# MjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcD
# CDAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggIBABLRDwWMKbeCYqEq
# tI6Bs8KmF+kqDR+2G6qYAK3ZZ63bert7pCkRJbihFaktl2o18cdFJFxnOF4vXadm
# 0sabskJ05KviEMJIO6dXSq8AGtr3Zmjc895q0mnlBLuNMgk4R8KrkJMHqBuHqkUW
# XtfTrVUpgwzQt2UOiINKs+/b4r14MuXRVpOJ6cQOS8UhkeMAWl2iLlYaBGtOr3f/
# f9mLEPfWwoke0sSUbdV60OZCRh1ItBYYM9efKr14H5qu6jan6n00prEEa7W3uGb/
# 1/qj6P5emnvkqy5HI0X69DjVdLxVbjSsegm/dA+S4DaXPcfFf6iBxK/iV21l1upg
# EVVajUApl5VR40wY4XF8EpmnUdTqLXDf7CqdhDjPST2K/OjvWPyQGQvc7oPapYyk
# 66GU32AOyyHXJj6+vbtRUg/+ory+h0R2Xf5NhC+xbWcMzXEUXRRf1YKZDsRyH6r4
# 12pm8KDKE/r7Rk7aoKK7oYUpNGzNRf6QaYv5z2bVTSxkzWivFrepLHGwvRun9PYM
# /8AQSTgZr0yzzjk/97WghkqCaAwAVpyvg3uaYnuCl/AccSkGyb8c+70bFSeUephs
# fgb2r+QI7Mb2WcOnkJpCNLz0XJMS/UwlQn1ktLsiCpsqOk3aLJ2wTv6LK3u69I0v
# QB/LKRKlZYRXKUDXzoPwr3UtsTVTMIIHcTCCBVmgAwIBAgITMwAAABXF52ueAptJ
# mQAAAAAAFTANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNh
# dGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEwOTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1
# WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEB
# BQADggIPADCCAgoCggIBAOThpkzntHIhC3miy9ckeb0O1YLT/e6cBwfSqWxOdcjK
# NVf2AX9sSuDivbk+F2Az/1xPx2b3lVNxWuJ+Slr+uDZnhUYjDLWNE893MsAQGOhg
# fWpSg0S3po5GawcU88V29YZQ3MFEyHFcUTE3oAo4bo3t1w/YJlN8OWECesSq/XJp
# rx2rrPY2vjUmZNqYO7oaezOtgFt+jBAcnVL+tuhiJdxqD89d9P6OU8/W7IVWTe/d
# vI2k45GPsjksUZzpcGkNyjYtcI4xyDUoveO0hyTD4MmPfrVUj9z6BVWYbWg7mka9
# 7aSueik3rMvrg0XnRm7KMtXAhjBcTyziYrLNueKNiOSWrAFKu75xqRdbZ2De+JKR
# Hh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9fvzZnkXftnIv231fgLrbqn427DZM9itu
# qBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdHGO2n6Jl8P0zbr17C89XYcz1DTsEzOUyO
# ArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7XKHYC4jMYctenIPDC+hIK12NvDMk2ZItb
# oKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiER9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6
# bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/eKtFtvUeh17aj54WcmnGrnu3tz5q4i6t
# AgMBAAGjggHdMIIB2TASBgkrBgEEAYI3FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQW
# BBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacb
# UzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYz
# aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnku
# aHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIA
# QwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2
# VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwu
# bWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEw
# LTA2LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93
# d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYt
# MjMuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCdVX38Kq3hLB9nATEkW+Geckv8qW/q
# XBS2Pk5HZHixBpOXPTEztTnXwnE2P9pkbHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6
# U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gngugnue99qb74py27YP0h1AdkY3m2CDPVt
# I1TkeFN1JFe53Z/zjj3G82jfZfakVqr3lbYoVSfQJL1AoL8ZthISEV09J+BAljis
# 9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHCgRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTp
# kbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0
# sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEUBHG/ZPkkvnNtyo4JvbMBV0lUZNlz138e
# W0QBjloZkWsNn6Qo3GcZKCS6OEuabvshVGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJ
# sWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7
# Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrpNPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0
# dFtq0Z4+7X6gMTN9vMvpe784cETRkPHIqzqKOghif9lwY1NNje6CbaUFEMFxBmoQ
# tB1VM1izoXBm8qGCAtcwggJAAgEBMIIBAKGB2KSB1TCB0jELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxh
# bmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjoz
# QkQ0LTRCODAtNjlDMzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy
# dmljZaIjCgEBMAcGBSsOAwIaAxUAZZzYkPObl/ZzeCkSbf4B5CceCQiggYMwgYCk
# fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF
# AOgGAIAwIhgPMjAyMzA1MTAxOTM3MzZaGA8yMDIzMDUxMTE5MzczNlowdzA9Bgor
# BgEEAYRZCgQBMS8wLTAKAgUA6AYAgAIBADAKAgEAAgIPMgIB/zAHAgEAAgIVPDAK
# AgUA6AdSAAIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB
# AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAFZq4SXruRd45Yt+
# ofSN7Ol0nzequTa3Jd0g1YYMb7bqLlMeXeC52Ex6OXYPCE5u2JzpsUQ+Ux6BqaZZ
# z2MPUaybQP75hnOWobgSBb9VHJ+RCVGzdiZ1PdOEMEdmOMItiz6yZe+tNMECZ3AQ
# axhNIVqA5bgu30+WyXBzrAy0MQqLMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
# bWUtU3RhbXAgUENBIDIwMTACEzMAAAG0+4AIRAXSLfoAAQAAAbQwDQYJYIZIAWUD
# BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B
# CQQxIgQg0iCJtCJmHE0W5Uk1TyghXnuWNzoi9kdaBBhu5ayM54IwgfoGCyqGSIb3
# DQEJEAIvMYHqMIHnMIHkMIG9BCDTyPd75qMwcAZRcb36/6xJa3hT0eLse71ysdp4
# twH3BjCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB
# tPuACEQF0i36AAEAAAG0MCIEIHSA/VKCqpY+ZWe8BgcfhCO8stlh60b/CiexrRlN
# d/isMA0GCSqGSIb3DQEBCwUABIICADs1Zg4Zz097TDQw4w7nc+Aunz4IPR3V5Ra+
# ZopJvcfyKcsujn6GvXP0JlP6g4XRjDBuyAXh7SBHKO8MY++OH6jlsgA7g+4JSh36
# +goTmEGyX++7mk5K68PFvZZxHEIfJsKEKmTgyqf3UatECqQ+xs2m4+KQ1hEwhCXC
# kCj6gtrt91w9AyA3Np1stf/muAHTv1DTOS0ikFl4mrGRzYVxj6fMgox9LgYBobdS
# KzR4DyLe/qhqJTaivS26icxGebK8jj8jYLMFVDkvotiVqldy6SSqIasPrqt9rr+u
# ppTccV9y0dIP34KLJAEKRzkg6FQgGykKJ2S0RgpNiFWpJsMTz/w05msQpXb1g/Of
# OwCq2mIjGkqBTpToSbqwe2kJ0WNh33s8+NpFtvEuf7pOhWPQ4gW43+gaWOYFQDSe
# Bd0TEJ63bFRivAaSomUtMxMKArmSfZssUKdjHZcUlsFzYJF2QMLJq09sCMPW5NFD
# 4gRfmuQ2+nei3ckAzzX7T0Lr9HX/TuXXiHwyLL0UQjpg48pFTf65/Xz6zpibiadd
# lIyx9VjfhcBB3Wo9ajAJw4wmC5XZlkl/uMGGPXYSpDOBBEwoocV1cgwBlfTtCYLW
# 7izX4pBwfmjHENmr0oqbYQT4b9AfQJ/mUjiuNvvr6vxiSpkTJckduDHxdBBQgMBi
# 34/FoB2W
# SIG # End signature block