Azs.TeamsIntegration.Utilities.psm1


#region PEP
function Invoke-PepCommand {
    <#
    .SYNOPSIS
        Try commands and reestablish PEP connection incase of breakage.
    #>

    param ($pepsession,$scriptblock, $retrySeconds = 30,[switch]$AsJob)
    $retries = 1
    while ($retries -le 3) {
        try {
            if ($pepsession.State -ne 'Opened') {
                throw ("Pep session state {0} until to invoke PEP command." -f $pepsession.State)
            }
            $result = Invoke-Command -session $pepsession -scriptblock $scriptblock -AsJob:$AsJob
            return $result
        }
        catch {
            Write-CustomLog ("PEP Command ($retries/3) retries failed with : {0}" -f $_.exception.message)
            $pepsession = Get-PepSessionWithRetries -stamp $stamp -retrySeconds $retrySeconds -PepCredential $PepCredential
            $retries++
            Start-Sleep -seconds $retrySeconds
        }
    }
}

function Get-PepSessionWithRetries {
    <#
    .SYNOPSIS
        Attempt to get PEPSession with retries
    #>

    param ($stamp, $retrySeconds = 30, $PepCredential)

    Clear-Variable retry, pepSession, accessDenied -ErrorAction SilentlyContinue
    while ($retry -lt 3 -and !$pepSession) {
        $retry++
        Write-CustomLog "[$Stamp] Opening Pepsession. Attempt $retry/3"
        $pepSession = Connect-AzsPepSession -Stamp $Stamp -PEPCredential $PepCredential
        if ($pepSession.State -eq 'Opened') {
            return $pepSession
        }
        else {
            Close-AzsPepSession -Stamp $stamp
            if ($Error[0] -like 'Access Denied - Skipping other IP addresses') {
                Write-CustomLog ("[$Stamp] Pep Attempt failed. Reason {0}. Will sleep for 15 minutes to avoid locking cloudadmin." -f $error[0])
                Write-Warning ("[$Stamp] Pep Attempt failed. Reason {0}. Will sleep for 15 minutes to avoid locking cloudadmin." -f $error[0])
                Start-Sleep -seconds 930
            }
            else {
                Write-CustomLog ("[$Stamp] Pep Attempt failed. Reason {0}" -f $error[0])
                Start-Sleep -seconds $retrySeconds
            }
        }
    }
}
#endregion

#region messages
function Format-OperatorMessage {
    <#
    .SYNOPSIS
        Add emphasis to certain messages to draw operator's attention
    #>

    param ($message)
    try {
        $strongmsgs = @('Collecting Azure Stack Logs',
            'We hit a problem sending data from the stamp to teams',
            'URP return unknown state',
            'PreparationFailed',
            'InstallationFailed',
            'Update is installing but PEP Session lost',
            'Update AzS (Update|Hotfix)',
            'Oem Package Update',
            'Update (.*) is')
        $message = $message | Foreach-Object {
            if (-not [string]::IsNullOrEmpty($PSITEM)){
                if ($PSITEM -match ($strongmsgs -join '|')) {
                    "<strong>$PSITEM</strong>"
                }
                else {
                    $PSITEM
                }
            }
        }
        return (New-Object PSObject -Property @{text = '<body>' + ($message -join '<BR>') } | ConvertTo-Json)
    }
    catch {
        throw ("Format-OperatorMessage failed with {0}" -f $_.exception.message)
    }
}

function Format-TimeSpan {
    <#
    .SYNOPSIS
        Convert timespan to Hours and Minutes, but don't break automation.
    #>

    param ($duration)
    try {
        # Try and get it into the right format first
        try {
            if ($duration -is [string]) {
                [TimeSpan]$TimeSpan = ([System.Xml.XmlConvert]::ToTimeSpan($duration))

            }
            else {
                [TimeSpan]$TimeSpan = $duration
            }
        }
        catch {
            Write-CustomLog ("Format-TimeSpan up front conversion failed for {0}" -f $duration)
        }

        # try anyway
        $h = $TimeSpan.Days * 24 + $TimeSpan.Hours
        $m = $TimeSpan.Minutes
        if ($h -gt 0) {
            "$h hours $m minutes"
        }
        else {
            "{0:d2} minutes" -f $m
        }
    }
    catch {
        Write-CustomLog ("Format-TimeSpan ran into an issue with formatting {0}" -f ($timeSpan -join ','))
    }
}

#endregion

#region posting content
function Send-TextMessage {
    <#
    .SYNOPSIS
        Remove illegal characters from exceptions, so they can be posted to operators
    #>

    param ($message, $uri)
    try {
        $text = $message -replace "[`r`n']+", ' '
        $TeamsMessageContent = New-Object PSObject -Property @{text = $text } | ConvertTo-Json
        Write-CustomLog $message
        foreach ($connector in $uri) {
            Invoke-RestMethod -Uri $connector -Method Post -Body $TeamsMessageContent -ContentType 'application/json'
        }
    }
    catch {
        throw ("Send-TextMessage failed with: {0}" -f $_.exception.message)
    }
}

function Send-MessageContent {
    <#
    .SYNOPSIS
        Send message content
    #>

    param ([system.uri[]]$uri, $messageContent)
    if ($uri) {
        Write-CustomLog ("[$Stamp] Posting message content to Uri(s): {0}" -f ($uri -join ','))
    }
    else {
        Write-CustomLog "[$Stamp] No message to post right now"
    }

    foreach ($u in $uri) {
        Invoke-RestMethod -Uri $u -Method Post -Body $messageContent -ContentType 'application/json' | Out-Null
    }
}

#endregion

#region adaptivecards

$AdaptiveCardContentWrapper = @"
{
    "type": "message",
    "attachments": [
        {
            "contentType": "application/vnd.microsoft.card.adaptive",
            "contentUrl": null,
            "content": {{MessageContent}}
        }
    ]
}
"@

function Format-TeamsMessage {
    <#
    .SYNOPSIS
        Transform data and adaptive card template and embed in teams message.
    #>

    param ($JsonTemplate, $AdaptiveCardData)
    Write-CustomLog "[$Stamp] Tranforming Adaptive Card"
    $UpdateProgressAdaptiveCard = Transform-AdaptiveCard -JsonTemplate $JsonTemplate -JsonData ($AdaptiveCardData | ConvertTo-Json -Depth 20) -AsString
    $TeamsMessageContent = $AdaptiveCardContentWrapper.replace('{{MessageContent}}', $UpdateProgressAdaptiveCard)
    return $TeamsMessageContent
}

function Transform-AdaptiveCard {
    <#
        .SYNOPSIS
        Builds an 'AdaptiveCard' from the given template and data
    #>


    [OutputType([AdaptiveCards.AdaptiveCard])]
    Param (
        [Parameter(
            Mandatory = $True,
            ValueFromPipeline = $True,
            ValueFromPipelineByPropertyName = $True)]
        [ValidateNotNullOrEmpty()]
        [String] $JsonTemplate,

        [Parameter(
            Mandatory = $True,
            ValueFromPipeline = $True,
            ValueFromPipelineByPropertyName = $True)]
        [ValidateNotNullOrEmpty()]
        [String] $JsonData,

        [Parameter()]
        [Switch] $AsString
    )

    $AdaptiveTransformer = [AdaptiveCards.Templating.AdaptiveCardTemplate]::new($JsonTemplate)
    $AdaptiveEvaluationContext = [AdaptiveCards.Templating.EvaluationContext]::new($JsonData)
    $CardJson = $AdaptiveTransformer.Expand($AdaptiveEvaluationContext)
    $cardjson | Out-File -FilePath $LogPath\MidTransform.json
    If ($PSBoundParameters.ContainsKey('AsString')) {
        Write-Output -InputObject $CardJson
    }
    Else {
        $AdaptiveCard = ([AdaptiveCards.AdaptiveCard]::FromJson($CardJson)).Card
        Write-Output -InputObject $AdaptiveCard
    }
}


#endregion

#region logging
function Initialize-LogPath {
    <#
    .SYNOPSIS
        Determine timestamped log path and return value
    #>

    param ($logPath)
    $newPath = New-Item $logPath -ItemType Directory -Force -ErrorAction SilentlyContinue
    return $newPath.FullName
}

function Write-CustomLog {
    <#
    .SYNOPSIS
        Write log messages to file and verbose stream.
    #>

    [CmdletBinding()]
    param ($message)
    $message = "[{0}] {1}" -f (Get-Date).ToUniversalTime().ToString('s'), $message

    # If console is selected in logaction, use write host, else use write-verbose for cmdletbinding support
    if ('Console' -in $GLOBAL:LogAction) {
        Write-Host -Message $message
    }
    else {
        Write-Verbose -Message $message
    }

    if ('File' -in $GLOBAL:LogAction) {
        $message | Out-File -FilePath (Join-Path $logPath AzsTeamsIntegration.log) -Append
    }
}

#end region

function Set-QuickEdit()
{
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$false, HelpMessage="This switch will disable Console QuickEdit option")]
        [switch]$DisableQuickEdit=$false
    )

$QuickEditCodeSnippet=@'
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
 
 
public static class DisableConsoleQuickEdit
{
 
const uint ENABLE_QUICK_EDIT = 0x0040;
 
// STD_INPUT_HANDLE (DWORD): -10 is the standard input device.
const int STD_INPUT_HANDLE = -10;
 
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
 
[DllImport("kernel32.dll")]
static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
 
[DllImport("kernel32.dll")]
static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
 
public static bool SetQuickEdit(bool SetEnabled)
{
 
    IntPtr consoleHandle = GetStdHandle(STD_INPUT_HANDLE);
 
    // get current console mode
    uint consoleMode;
    if (!GetConsoleMode(consoleHandle, out consoleMode))
    {
        // ERROR: Unable to get console mode.
        return false;
    }
 
    // Clear the quick edit bit in the mode flags
    if (SetEnabled)
    {
        consoleMode &= ~ENABLE_QUICK_EDIT;
    }
    else
    {
        consoleMode |= ENABLE_QUICK_EDIT;
    }
 
    // set the new mode
    if (!SetConsoleMode(consoleHandle, consoleMode))
    {
        // ERROR: Unable to set console mode
        return false;
    }
 
    return true;
}
}
'@


    $QuickEditMode=add-type -TypeDefinition $QuickEditCodeSnippet -Language CSharp
    if([DisableConsoleQuickEdit]::SetQuickEdit($DisableQuickEdit))
    {
        Write-CustomLog "QuickEdit settings has been updated."
    }
    else
    {
        Write-CustomLog "Issue happened with setting quickedit. Continuing."
    }
}
# SIG # Begin signature block
# MIIjkgYJKoZIhvcNAQcCoIIjgzCCI38CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCTRqAHHMMPwsEZ
# NARcx9HNdfH0zwzc93IoJjFjChiJlaCCDYEwggX/MIID56ADAgECAhMzAAAB32vw
# LpKnSrTQAAAAAAHfMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjAxMjE1MjEzMTQ1WhcNMjExMjAyMjEzMTQ1WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQC2uxlZEACjqfHkuFyoCwfL25ofI9DZWKt4wEj3JBQ48GPt1UsDv834CcoUUPMn
# s/6CtPoaQ4Thy/kbOOg/zJAnrJeiMQqRe2Lsdb/NSI2gXXX9lad1/yPUDOXo4GNw
# PjXq1JZi+HZV91bUr6ZjzePj1g+bepsqd/HC1XScj0fT3aAxLRykJSzExEBmU9eS
# yuOwUuq+CriudQtWGMdJU650v/KmzfM46Y6lo/MCnnpvz3zEL7PMdUdwqj/nYhGG
# 3UVILxX7tAdMbz7LN+6WOIpT1A41rwaoOVnv+8Ua94HwhjZmu1S73yeV7RZZNxoh
# EegJi9YYssXa7UZUUkCCA+KnAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUOPbML8IdkNGtCfMmVPtvI6VZ8+Mw
# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1
# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDYzMDA5MB8GA1UdIwQYMBaAFEhu
# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu
# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w
# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3
# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx
# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAnnqH
# tDyYUFaVAkvAK0eqq6nhoL95SZQu3RnpZ7tdQ89QR3++7A+4hrr7V4xxmkB5BObS
# 0YK+MALE02atjwWgPdpYQ68WdLGroJZHkbZdgERG+7tETFl3aKF4KpoSaGOskZXp
# TPnCaMo2PXoAMVMGpsQEQswimZq3IQ3nRQfBlJ0PoMMcN/+Pks8ZTL1BoPYsJpok
# t6cql59q6CypZYIwgyJ892HpttybHKg1ZtQLUlSXccRMlugPgEcNZJagPEgPYni4
# b11snjRAgf0dyQ0zI9aLXqTxWUU5pCIFiPT0b2wsxzRqCtyGqpkGM8P9GazO8eao
# mVItCYBcJSByBx/pS0cSYwBBHAZxJODUqxSXoSGDvmTfqUJXntnWkL4okok1FiCD
# Z4jpyXOQunb6egIXvkgQ7jb2uO26Ow0m8RwleDvhOMrnHsupiOPbozKroSa6paFt
# VSh89abUSooR8QdZciemmoFhcWkEwFg4spzvYNP4nIs193261WyTaRMZoceGun7G
# CT2Rl653uUj+F+g94c63AhzSq4khdL4HlFIP2ePv29smfUnHtGq6yYFDLnT0q/Y+
# Di3jwloF8EWkkHRtSuXlFUbTmwr/lDDgbpZiKhLS7CBTDj32I0L5i532+uHczw82
# oZDmYmYmIUSMbZOgS65h797rj5JJ6OkeEUJoAVwwggd6MIIFYqADAgECAgphDpDS
# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0
# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla
# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT
# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG
# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S
# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz
# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7
# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u
# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33
# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl
# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP
# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB
# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF
# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM
# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ
# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud
# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO
# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0
# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p
# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw
# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA
# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY
# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj
# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd
# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ
# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf
# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ
# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j
# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B
# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96
# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7
# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I
# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZzCCFWMCAQEwgZUwfjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAd9r8C6Sp0q00AAAAAAB3zAN
# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgDwVpQHV3
# /gpGAJaw6UfCBDyscu/6R0SXexJ3tq4GtJIwQgYKKwYBBAGCNwIBDDE0MDKgFIAS
# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN
# BgkqhkiG9w0BAQEFAASCAQApte6RaZgLzsW6Kmjnto6EXkpnMDT+SeDGvxvd+dJc
# wWKZodznEugesOlXBNXSFGfrV5irKCxGsewNMDgyljQZiNfQmdQVrM4CL2i6KkJr
# F3ySbuPon12vykVuM0YpksqJ0gFxRq1D/TILS/GjR6j5dum7aUMJ3eh5E1k6bPUL
# oNlNp3DFX9BoSCvPBIWD8KVqAc2aibef2XvjRh1yc4EMf6kemNhVoXP5/P/QdxBG
# AnpJ0ZHXSV2jKSL8Xb4ot7Z8u+e+fTQTNGmS/IaIpc9ShnWmHX8SZ26lF+z6DSXU
# hCIDTGfOP3eWE+9+re3VvT1BJgpb5cyeWAWcAyeyEuEOoYIS8TCCEu0GCisGAQQB
# gjcDAwExghLdMIIS2QYJKoZIhvcNAQcCoIISyjCCEsYCAQMxDzANBglghkgBZQME
# AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB
# MDEwDQYJYIZIAWUDBAIBBQAEIOrtOMeftpDw3FYtA6mfEOMNHOiPpzsTvmJg5CZ5
# qJlsAgZgWeUQzYcYEzIwMjEwMzI2MTE0MjU3LjAxMlowBIACAfSggdSkgdEwgc4x
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p
# Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg
# VFNTIEVTTjpGNzdGLUUzNTYtNUJBRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgU2VydmljZaCCDkQwggT1MIID3aADAgECAhMzAAABXp0px1+HBaHqAAAA
# AAFeMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
# MB4XDTIxMDExNDE5MDIxOVoXDTIyMDQxMTE5MDIxOVowgc4xCzAJBgNVBAYTAlVT
# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK
# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy
# YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpGNzdG
# LUUzNTYtNUJBRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj
# ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJrTI4OQehn3oKp2zuh6
# WP2Zib/Dxw/srLeeyTb9ed7PX+fLg7zBA0yl6ivF2n6lauGH8W7EBwRPEv7ZCSXw
# XgYZ6GGaH8aU+OrDXAbc4BXTO5XnLGwSbaye9R2+uQHdCJmaMtz/lEBWUK5xvHoj
# 0TUrXOZdZ/vv7TqMWA4h1AT1w/JBR4kHtV1i8KWdlQ+dZX/gNHpA72IoLoOmpImb
# GRzcGQ4Z2Kzq4eMB9wjaFRV1JF/wz1hLFIjGtlU3eGjRBiBEEVI7UEMMSvI4rK+C
# fLAIZnULu7SzlIfqSU3R0pSNUahwpWdCiB6fKzIq94Z+9888moQuo95RAPmzHQW1
# MI0CAwEAAaOCARswggEXMB0GA1UdDgQWBBSqcny6Dd1L5VTCEACezlR41fgfKzAf
# BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH
# hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU
# aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF
# BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0
# YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG
# AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQB/IfxZhMYkBMqRmXnh/Vit4bfxyioA
# lr7HJ1XDSHTIvRwDD1PGr0upZE/vrrI/QN/1Wi6vDcKmnJ2r7Xj6pWZOZqc9Bp+u
# BvpPaulue4stu3TqKTc9Fu2K5ibctpF4oHPfZ+IKeChop+Mk9g7N5llHzv0aCDia
# M0w2aAT3rj3QHQS8ijnQ5/qhtzwo1AoUnV1y2urWwX5aHdUzaoeAJrvnf2ee89Kf
# 4ycjjyafNJSUp/qaXBlbjMu90vNubJstdSxOtvwcxeeHP6ZaYbTl2cOla4cokiPU
# +BUjIZA/t/IZfYoazMGmBtLWFJZdC9LYWWmLLsNJ2W21qkeSSpEAw4pmMIIGcTCC
# BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv
# b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN
# MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
# bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
# aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw
# DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0
# VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw
# RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe
# dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx
# Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G
# kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA
# AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7
# fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC
# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX
# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v
# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI
# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g
# AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93
# d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB
# BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA
# bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh
# IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS
# +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK
# kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon
# /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi
# PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/
# fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII
# YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0
# cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a
# KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ
# cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+
# NR4Iuto229Nfj950iEkSoYIC0jCCAjsCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT
# AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP
# cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpG
# NzdGLUUzNTYtNUJBRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy
# dmljZaIjCgEBMAcGBSsOAwIaAxUAVkmPV/8hZVS9FzbtoX2x3Z2xYyqggYMwgYCk
# fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF
# AOQHr0IwIhgPMjAyMTAzMjYwNDU0MjZaGA8yMDIxMDMyNzA0NTQyNlowdzA9Bgor
# BgEEAYRZCgQBMS8wLTAKAgUA5AevQgIBADAKAgEAAgILkgIB/zAHAgEAAgIRNTAK
# AgUA5AkAwgIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB
# AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAA7Jo/kPAZtti24m
# S+7eKDwg9nA5WwkRbiXlEOq/NY/9aNIk+5XMqAQ3VK5t54dOyS9MLTBK00bgjQbO
# kxs5DaoEbvvuFYdzB+9IU5ccfqiTtF2LuJRFnI8vvGnHue7RcDo9ZtDhy7U3Nru/
# dSdXTwgE8i5peT3PN02Iql/Zi+jKMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
# bWUtU3RhbXAgUENBIDIwMTACEzMAAAFenSnHX4cFoeoAAAAAAV4wDQYJYIZIAWUD
# BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B
# CQQxIgQg7CzdWqpqYlBdMcYEXDnOqFAvGe15md83EGhbZzbhibMwgfoGCyqGSIb3
# DQEJEAIvMYHqMIHnMIHkMIG9BCB+5YTslNSXjuB+5mQDTKRkM7prkewhXnevXpLv
# 8RLT4jCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB
# Xp0px1+HBaHqAAAAAAFeMCIEIOeNp6ti7WMPfc0m8dltury+W8LMlRB1VxIfxvmP
# gaa2MA0GCSqGSIb3DQEBCwUABIIBAIph4i7IZNZM32P7asum+ck1ip408rSP0S1x
# 1TrO73hZSoRvQglcNckWYg1WgiuLrbUFGtPvUk1iBQsYYHXQMBqo0q43cgocmVyP
# cTGocnb0wk/KMEI+frg5jcFiuffxYp3jC1YMOz4lscP1oVFFtu9l9AIhZWVJxhr6
# HKGL33xGUcDr1YG03uP9TJvhui+I26Erh2mNFhKpXHYwl84fI/ClOerePctC3KX3
# MB9cLCTLz8/nfD3/0f2QjdHdAA2igfgGFrCBVfx71Ws3ikMs88XJAVZ9WEgN4LF4
# O/D0HIOYIml0cuOUcBOi5Ry1CJSjoOV8rKxahw+z9zFWmSyaQB0=
# SIG # End signature block