AzSK.ADO.psm1

Set-StrictMode -Version Latest

. $PSScriptRoot\Framework\Framework.ps1


#These are the topmost folder PS1 files that load
@("$PSScriptRoot\SVT", "$PSScriptRoot\AlertMonitoring", "$PSScriptRoot\ContinuousAssurance","$PSScriptRoot\AzSKADOInfo", "$PSScriptRoot\STMapping") |
    ForEach-Object {
    (Get-ChildItem -Path $_ -Recurse -File -Include "*.ps1") |
        ForEach-Object {
        . $_.FullName
    }
}


function Set-AzSKADOPrivacyNoticeResponse {
    <#
    .SYNOPSIS
    This command would help to set user preferences for EULA and Privacy.
    .DESCRIPTION
    This command would help to set user preferences for EULA and Privacy.
 
    .PARAMETER AcceptPrivacyNotice
        Provide the flag to suppress the Privacy notice prompt and submit the acceptance. (Yes/No)
 
    .LINK
    https://aka.ms/azskossdocs
 
    #>

    [Alias("Set-AzSKPrivacyNoticeResponse")]
    Param
    (
        [Parameter(Mandatory = $true, HelpMessage = "Provide the flag to suppress the Privacy notice prompt and submit the acceptance. (Yes/No)")]
        [string]
        [ValidateSet("Yes", "No")]
        [Alias("apn")]
        $AcceptPrivacyNotice
    )
    Begin {
        [CommandHelper]::BeginCommand($PSCmdlet.MyInvocation);
        [ListenerHelper]::RegisterListeners();
    }
    Process {
        try {
            $azskSettings = [ConfigurationManager]::GetLocalAzSKSettings();

            if ($AcceptPrivacyNotice -eq "yes") {
                $azskSettings.PrivacyNoticeAccepted = $true
                $azskSettings.UsageTelemetryLevel = "Anonymous"
            }

            if ($AcceptPrivacyNotice -eq "no") {
                $azskSettings.PrivacyNoticeAccepted = $false
                $azskSettings.UsageTelemetryLevel = "None"
            }
            [ConfigurationManager]::UpdateAzSKSettings($azskSettings)
            [EventBase]::PublishGenericCustomMessage("Successfully updated privacy settings.");
        }
        catch {
            [EventBase]::PublishGenericException($_);
        }

    }
    End {
        [ListenerHelper]::UnregisterListeners();
    }
}

function Clear-AzSKADOSessionState {

    Write-Host "Clearing $([Constants]::AzSKModuleName) session state..." -ForegroundColor Yellow
    [ConfigOverride]::ClearConfigInstance()
    Write-Host "Session state cleared." -ForegroundColor Yellow

}


function Set-AzSKADOPolicySettings {
    <#
    .SYNOPSIS
    This command would help to set online policy store URL.
    .DESCRIPTION
    This command would help to set online policy store URL.
 
    .PARAMETER AutoUpdateCommand
            Provide org install URL
    .PARAMETER AutoUpdate
            Toggle the auto-update feature
    #>

    [Alias("Set-AzSKPolicySettings")]
    Param(

        [Parameter(Mandatory = $false, HelpMessage = "Project that hosts ADO organization-specific policy")]
        [Alias("pp")]
        $PolicyProject,

        [Parameter(Mandatory = $false, HelpMessage = "Provide org install URL")]
        [string]
        [Alias("auc")]
        $AutoUpdateCommand,

        [Parameter(Mandatory = $false, ParameterSetName = "AutoUpdatePolicy", HelpMessage = "Toggle the auto-update feature")]
        [ValidateSet("On", "Off", "NotSet")]
        [Alias("au")]
        $AutoUpdate,

        [Parameter(Mandatory = $false, HelpMessage = "Turn org/admin control attestation on/off")]
        [bool]
        [Alias("oca")]
        $EnableOrgControlAttestation,

        [Parameter(Mandatory = $false, HelpMessage = "Provide scanner tool path")]
        [string]
        [Alias("stp")]
        $SecretsScanToolFolder,

        [Parameter(Mandatory = $false, HelpMessage = "Provide scanner tool name")]
        [string]
        [Alias("stn")]
        $SecretsScanToolName,

        [Parameter(Mandatory = $false, HelpMessage = "Branch that hosts ADO organization-specific policy")]
        [string]
        [Alias("bid")]
        $BranchId,

        [Parameter(Mandatory = $false, HelpMessage = "Attestation repository that stores attestation details.")]
        [string]
        [Alias("atr")]
        $AttestationRepo,

        [Parameter(Mandatory = $false, HelpMessage = "Attestation branch that stores attestation details.")]
        [string]
        [Alias("atb")]
        $AttestationBranch,

        [Parameter(Mandatory = $false, HelpMessage = "Provide the local policy folder path")]
        [string]
        [Alias("lopf")]
        $LocalOrgPolicyFolderPath,

        [Parameter(Mandatory = $false, HelpMessage = "Restore default org policy settings")]
        [switch]
        [Alias("rdops")]
        $RestoreDefaultOrgPolicySettings
    )
    Begin {
        [CommandHelper]::BeginCommand($PSCmdlet.MyInvocation);
        [ListenerHelper]::RegisterListeners();
    }
    Process {
        try {

            $azskSettings = [ConfigurationManager]::GetLocalAzSKSettings();

            if (-not [string]::IsNullOrWhiteSpace($AutoUpdateCommand))
            {
                $azskSettings.AutoUpdateCommand = $AutoUpdateCommand;
            }

            if ($AutoUpdate)
            {
                $azskSettings.AutoUpdateSwitch = $AutoUpdate
            }

            if (-not [string]::IsNullOrWhiteSpace($PolicyProject))
            {
                $azskSettings.PolicyProject = $PolicyProject;
            }

            if ($EnableOrgControlAttestation)
            {
                $azskSettings.EnableOrgControlAttestation = $true
            }
            else
            {
                $azskSettings.EnableOrgControlAttestation = $false
            }

            if (-not [string]::IsNullOrWhiteSpace($BranchId))
            {
                $azskSettings.BranchId = $BranchId;
            }

            if($SecretsScanToolFolder -and $SecretsScanToolName)
            {
                $azskSettings.SecretsScanToolFolder = $SecretsScanToolFolder
                $azskSettings.SecretsScanToolName = $SecretsScanToolName
            }

            #Set attestation repository for dev/test
            if (-not [string]::IsNullOrWhiteSpace($AttestationRepo)) {
                $azskSettings.AttestationRepo = $AttestationRepo;
            }

            #Set attestation branch for dev/test
            if (-not [string]::IsNullOrWhiteSpace($AttestationBranch)) {
                $azskSettings.AttestationBranch = $AttestationBranch;
            }

            #Set local policy folder path to OnlinePolicyStoreUrl. At runtime it will detect its folder path and starting running cmdlets with local policy.
            if ($LocalOrgPolicyFolderPath) {
                if ((-not[string]::IsNullOrWhiteSpace($LocalOrgPolicyFolderPath)) -and (Test-Path $LocalOrgPolicyFolderPath)) {
                    $azskSettings.OnlinePolicyStoreUrl = $LocalOrgPolicyFolderPath
                }
                else {

                    [EventBase]::PublishGenericCustomMessage("Policy folder does not exists. Enter valid policy folder path: $LocalOrgPolicyFolderPath", [MessageType]::Error);
                    return
                }
            }

            if ($RestoreDefaultOrgPolicySettings) {
                $defaultOrgPolicyLocation = "https://dev.azure.com/{0}/{1}/_apis/git/repositories/{2}/Items?path=%2F`$FileName&recursionLevel=0&includeContentMetadata=true&versionDescriptor.version={3}&versionDescriptor.versionOptions=0&versionDescriptor.versionType=0&includeContent=true&resolveLfs=true?api-version=4.1-preview.1"
                [EventBase]::PublishGenericCustomMessage("Updating the org policy to default location.", [MessageType]::Info);
                $azskSettings.OnlinePolicyStoreUrl = $defaultOrgPolicyLocation
            }

            [ConfigurationManager]::UpdateAzSKSettings($azskSettings);
            [ConfigOverride]::ClearConfigInstance();
            [EventBase]::PublishGenericCustomMessage("Successfully configured settings.", [MessageType]::Warning);
        }
        catch {
            [EventBase]::PublishGenericException($_);
        }
    }
    End {
        [ListenerHelper]::UnregisterListeners();
    }
}

function Set-AzSKADOUsageTelemetryLevel {
    <#
    .SYNOPSIS
    This command would help to set telemetry level.
    .DESCRIPTION
    This command would help to set telemetry level.
 
    .PARAMETER Level
        Provide the telemetry level
 
    #>

    Param(
        [Parameter(Mandatory = $true, HelpMessage = "Provide the telemetry level")]
        [ValidateSet("None", "Anonymous")]
        [string]
        [Alias("lvl")]
        $Level
    )
    Begin {
        [CommandHelper]::BeginCommand($PSCmdlet.MyInvocation);
        [ListenerHelper]::RegisterListeners();
    }
    Process {
        try {
            $azskSettings = [ConfigurationManager]::GetLocalAzSKSettings();
            $azskSettings.UsageTelemetryLevel = $Level
            [ConfigurationManager]::UpdateAzSKSettings($azskSettings);
            [EventBase]::PublishGenericCustomMessage("Successfully set usage telemetry level");
            # clearing session state so that telemetry setting will be immediately effective
            [ConfigOverride]::ClearConfigInstance()
        }
        catch {
            [EventBase]::PublishGenericException($_);
        }
    }
    End {
        [ListenerHelper]::UnregisterListeners();
    }
}

function Set-AzSKADOUserPreference {
    <#
    .SYNOPSIS
    This command would help to set user preferences for ADO Scanner.
    .DESCRIPTION
    This command would help to set user preferences for ADO Scanner.
    .PARAMETER OutputFolderPath
    Provide the custom folder path for output files generated from ADO Scanner.
    .PARAMETER ResetOutputFolderPath
    Reset the output folder path to default value.
    .LINK
    https://aka.ms/adoscanner
    #>


    Param
    (
        [Parameter(Mandatory = $false, HelpMessage = "Provide the custom folder path for output files generated from ADO Scanner")]
        [string]
        [Alias("ofp")]
        $OutputFolderPath,

        [Parameter(Mandatory = $false, HelpMessage = "Reset the output folder path to default value")]
        [switch]
        [Alias("rofp")]
        $ResetOutputFolderPath

    )
    Begin {
        [CommandHelper]::BeginCommand($PSCmdlet.MyInvocation);
        [ListenerHelper]::RegisterListeners();
    }
    Process {
        try {
            $azskSettings = [ConfigurationManager]::GetLocalAzSKSettings();
            $flag = $false
            if ($ResetOutputFolderPath) {

                $azskSettings.OutputFolderPath = "";
                [EventBase]::PublishGenericCustomMessage("Output folder path has been reset successfully");
                $flag = $true
            }
            elseif (-not [string]::IsNullOrWhiteSpace($OutputFolderPath)) {
                if (Test-Path -Path $OutputFolderPath) {
                    $azskSettings.OutputFolderPath = $OutputFolderPath;
                    [EventBase]::PublishGenericCustomMessage("Output folder path has been changed successfully");
                    $flag = $true
                }
                else {
                    [EventBase]::PublishGenericCustomMessage("The specified path does not exist", [MessageType]::Error);
                }
            }

            if($flag)
            {
                [ConfigurationManager]::UpdateAzSKSettings($azskSettings);
                [EventBase]::PublishGenericCustomMessage("Successfully set user preference");
            }
            else {
                [EventBase]::PublishGenericCustomMessage("User preference not updated",[MessageType]::Error);
            }
        }
        catch {
            [EventBase]::PublishGenericException($_);
        }
    }
    End {
        [ListenerHelper]::UnregisterListeners();
    }
}


#$FrameworkPath = $PSScriptRoot

. $FrameworkPath\Helpers\AliasHelper.ps1

# SIG # Begin signature block
# MIIjngYJKoZIhvcNAQcCoIIjjzCCI4sCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC2dWAiHjl5Bqx/
# r5TTjuztVSbBhzTJl6bejQBsnaAgiKCCDYEwggX/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/BvW1taslScxMNelDNMYIVczCCFW8CAQEwgZUwfjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAd9r8C6Sp0q00AAAAAAB3zAN
# BglghkgBZQMEAgEFAKCBsDAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgS3PYxpF1
# 2Hnao+yVOztI0VbO8Wk0H2TI15Z+XDKlRHgwRAYKKwYBBAGCNwIBDDE2MDSgFIAS
# AE0AaQBjAHIAbwBzAG8AZgB0oRyAGmh0dHBzOi8vd3d3Lm1pY3Jvc29mdC5jb20g
# MA0GCSqGSIb3DQEBAQUABIIBAJk19ONARGiw+mPqCDFyx/L1nUBMNnT5N+i9UKkQ
# KlGKFoYtijeXrEFqr0Vg22KkCRv7Un7CtI/0oAtc7BvZ+IMiLq+gB5p8bMjK0nrv
# 5lP56cK17XD/xye0oi5Yi7pNy9Hj/mGxyrubP3lGDlYdpKjhSiget1FylIWbCl8l
# YvSDY3cWFVAk4SJTAMGK15slaydaZOrkMcAqjG35APGTQyCxM6uN6q9/kwZ+k9Dz
# B/EByGLI3YmmK8zPJz8csGQzbuvhOY05vVCMKHBpqCq4+pPMhqQ386UA6X91oIzV
# a4sWyn+kkrGa1PP6nAZo5t+KCG1RY2OXAoQhYzPloYtUk16hghL7MIIS9wYKKwYB
# BAGCNwMDATGCEucwghLjBgkqhkiG9w0BBwKgghLUMIIS0AIBAzEPMA0GCWCGSAFl
# AwQCAQUAMIIBWQYLKoZIhvcNAQkQAQSgggFIBIIBRDCCAUACAQEGCisGAQQBhFkK
# AwEwMTANBglghkgBZQMEAgEFAAQgEFISYv67jJ+MI1rRrWTBwc8YEwc1Yzah1ecp
# L0JUpkwCBmEe10rMMxgTMjAyMTA5MTUxMTA5NDEuNzMzWjAEgAIB9KCB2KSB1TCB
# 0jELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
# ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMk
# TWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1U
# aGFsZXMgVFNTIEVTTjoyQUQ0LTRCOTItRkEwMTElMCMGA1UEAxMcTWljcm9zb2Z0
# IFRpbWUtU3RhbXAgU2VydmljZaCCDkowggT5MIID4aADAgECAhMzAAABOPOUIdZh
# vvApAAAAAAE4MA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
# EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
# ZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBD
# QSAyMDEwMB4XDTIwMTAxNTE3MjgyMFoXDTIyMDExMjE3MjgyMFowgdIxCzAJBgNV
# BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w
# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29m
# dCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhhbGVzIFRT
# UyBFU046MkFENC00QjkyLUZBMDExJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0
# YW1wIFNlcnZpY2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDFufin
# qV5wgzICAqNsrv4D+92pj7LWmaBciM7Bca8MCPLtoo+yn3vwUf5U8eH7diT21zQR
# PfnnhttLtOs5t6NsAfxtDdQypPuxTv2eQAvEqrKSnaVh8j9m+sNUF5yzBvPW//0J
# lxN2tJqKVs0MiDWVN9IDQsOF5tO3TYn5Hl4JmuF9d50JYk/WS3WPLQTnAlSvKpg6
# FeyuB7AZ1Cx0fZu+nkl9GKn6+DyvBUAnvxEdttPnFOh/6gUs4ICCYkbR7bILs8Ai
# 0Sso8xaMzzqmRUpHpq2hUq4Dmgbh3g1aS2JTMpPZvKfCVWTHvi67qsXtT7nq50LD
# CTaac2MP45cOHI8ZAgMBAAGjggEbMIIBFzAdBgNVHQ4EFgQU3+qt3OjvrFOxMvs5
# iMVgwZ5xwLUwHwYDVR0jBBgwFoAU1WM6XIoxkPNDe3xGG8UzaFqFbVUwVgYDVR0f
# BE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJv
# ZHVjdHMvTWljVGltU3RhUENBXzIwMTAtMDctMDEuY3JsMFoGCCsGAQUFBwEBBE4w
# TDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0
# cy9NaWNUaW1TdGFQQ0FfMjAxMC0wNy0wMS5jcnQwDAYDVR0TAQH/BAIwADATBgNV
# HSUEDDAKBggrBgEFBQcDCDANBgkqhkiG9w0BAQsFAAOCAQEAo3iwSFwLsb8NEpDL
# 0si2jk4jlVlSBLdIEVms0eyv9nNHkyxqJjd5XGb3NmZuD6bi610KLwVxR09JwPqv
# wWi18c7d0X8jquNd6P+wZs2/uHwewE9aYvdJ76Zn7/8hCsWLmuS2rGzotKQI+KuY
# yFhnqFR8mA3g+C2Qb+bCpKoXTh/vgbwDYkfCEPOwsGLgxgFkNPgosn0oA6BZkCZL
# g7cwBjHOx9uw3RayMyAMyrwHZepA3MgsRLCQA9EkpZd9fvcLoglLkwdZqsYiP4HK
# AOzmnozbzDnexc9YAo0Cq3tkYFqykb6T+3fF9YCbx4PgKe8QpJUzxV9+vYfAb4JS
# AAKmmjCCBnEwggRZoAMCAQICCmEJgSoAAAAAAAIwDQYJKoZIhvcNAQELBQAwgYgx
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1p
# Y3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDEwMB4XDTEwMDcw
# MTIxMzY1NVoXDTI1MDcwMTIxNDY1NVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB
# IDIwMTAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpHQ28dxGKOiDs
# /BOX9fp/aZRrdFQQ1aUKAIKF++18aEssX8XD5WHCdrc+Zitb8BVTJwQxH0EbGpUd
# zgkTjnxhMFmxMEQP8WCIhFRDDNdNuDgIs0Ldk6zWczBXJoKjRQ3Q6vVHgc2/JGAy
# WGBG8lhHhjKEHnRhZ5FfgVSxz5NMksHEpl3RYRNuKMYa+YaAu99h/EbBJx0kZxJy
# GiGKr0tkiVBisV39dx898Fd1rL2KQk1AUdEPnAY+Z3/1ZsADlkR+79BL/W7lmsqx
# qPJ6Kgox8NpOBpG2iAg16HgcsOmZzTznL0S6p/TcZL2kAcEgCZN4zfy8wMlEXV4W
# nAEFTyJNAgMBAAGjggHmMIIB4jAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU
# 1WM6XIoxkPNDe3xGG8UzaFqFbVUwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEw
# CwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/o
# olxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNy
# b3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYt
# MjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5t
# aWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5j
# cnQwgaAGA1UdIAEB/wSBlTCBkjCBjwYJKwYBBAGCNy4DMIGBMD0GCCsGAQUFBwIB
# FjFodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vUEtJL2RvY3MvQ1BTL2RlZmF1bHQu
# aHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAFAAbwBsAGkAYwB5AF8A
# UwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQAH5ohRDeLG
# 4Jg/gXEDPZ2joSFvs+umzPUxvs8F4qn++ldtGTCzwsVmyWrf9efweL3HqJ4l4/m8
# 7WtUVwgrUYJEEvu5U4zM9GASinbMQEBBm9xcF/9c+V4XNZgkVkt070IQyK+/f8Z/
# 8jd9Wj8c8pl5SpFSAK84Dxf1L3mBZdmptWvkx872ynoAb0swRCQiPM/tA6WWj1kp
# vLb9BOFwnzJKJ/1Vry/+tuWOM7tiX5rbV0Dp8c6ZZpCM/2pif93FSguRJuI57BlK
# cWOdeyFtw5yjojz6f32WapB4pm3S4Zz5Hfw42JT0xqUKloakvZ4argRCg7i1gJsi
# OCC1JeVk7Pf0v35jWSUPei45V3aicaoGig+JFrphpxHLmtgOR5qAxdDNp9DvfYPw
# 4TtxCd9ddJgiCGHasFAeb73x4QDf5zEHpJM692VHeOj4qEir995yfmFrb3epgcun
# Caw5u+zGy9iCtHLNHfS4hQEegPsbiSpUObJb2sgNVZl6h3M7COaYLeqN4DMuEin1
# wC9UJyH3yKxO2ii4sanblrKnQqLJzxlBTeCG+SqaoxFmMNO7dDJL32N79ZmKLxvH
# Ia9Zta7cRDyXUHHXodLFVeNp3lfB0d4wwP3M5k37Db9dT+mdHhk4L7zPWAUu7w2g
# UDXa7wknHNWzfjUeCLraNtvTX4/edIhJEqGCAtQwggI9AgEBMIIBAKGB2KSB1TCB
# 0jELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
# ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMk
# TWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1U
# aGFsZXMgVFNTIEVTTjoyQUQ0LTRCOTItRkEwMTElMCMGA1UEAxMcTWljcm9zb2Z0
# IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIaAxUAQLzrOzbQAqfNqS4h
# IUsmylnmgfmggYMwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAN
# BgkqhkiG9w0BAQUFAAIFAOTsRKUwIhgPMjAyMTA5MTUxODA4MzdaGA8yMDIxMDkx
# NjE4MDgzN1owdDA6BgorBgEEAYRZCgQBMSwwKjAKAgUA5OxEpQIBADAHAgEAAgIJ
# wDAHAgEAAgIROzAKAgUA5O2WJQIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEE
# AYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GB
# AICtocXzEtZJy+HyjcRpxpUnvt+UjA8JG1msCE56F3llh1PjbrBwqqpDECvHd1ol
# D9m5a+5rcH9spoWeQDvmHS/lKBJwIb8/AxEL1GbloXol326+/bO2Csk1KXMjM7Tj
# hwv5rAVS7i2JkDKwOr4LI8OyXniEPhayxH07mER+AiIaMYIDDTCCAwkCAQEwgZMw
# fDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
# ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMd
# TWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAE485Qh1mG+8CkAAAAA
# ATgwDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRAB
# BDAvBgkqhkiG9w0BCQQxIgQgE+oM7wx7jf3/eJihtT9t1pRRszrMGOCZkEmhF3n1
# YoUwgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCBDQJNK+X9EKpJIYuIspu7u
# xtLeaOYI6k76K2HtFo+HKDCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
# EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
# ZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBD
# QSAyMDEwAhMzAAABOPOUIdZhvvApAAAAAAE4MCIEIBv+YDKQ0bnccbjiBXYTWhAs
# VKsBkvHUhkW7k9iyETS7MA0GCSqGSIb3DQEBCwUABIIBAFC/8uyjBMg9RIuBucq3
# rni37BCZ+bPL1+EYK6KPg4ok1dO54YlKOn2w3aKwM1uK2HVKJasCjEsrkh3nqIdD
# 9+NFwSky5aZSfrovuPYqj30RPJVyeWB1m6rbBqpQLPw40Xbr+e8gOsGXqjGj1VZn
# gsXeHoQaDd0Mbkqyo/YhVVgeaU4H+1haJzJjBmO8ihglzJj7t8hXdSijYtawnZGU
# 3ztu7G74+lfU/yoOjGdtwrVg58N2qkKDMlcXJcS/8y1vFNUJ/f3EHa41USZLD7BS
# DS1bITBN0Lfy7GW5K90ue8e0SEV3qPQ2K5itzwSmmnge36HhrdH5LiAeug0pLr6h
# 8yc=
# SIG # End signature block