AzStackHciExternalActiveDirectory/AzStackHci.ExternalActiveDirectory.Helpers.psm1

Import-LocalizedData -BindingVariable lcAdTxt -FileName AzStackHci.ExternalActiveDirectory.Strings.psd1

function Get-ParamFromCommandLineOrConfigFile {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$false)]
        [string]
        $ConfigurationJsonPath,

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

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

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

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

    # If CommandLineParameterValue is set, then use it and the config file doesn't matter
    if ([string]::IsNullOrEmpty($CommandLineParameterValue))
    {
        # If the configuration file is present, check to see if the value exists under the DeploymentData object
        # If we can find it, we'll overwrite $CommandLineParameterValue (so we can check for that later to see if it worked)
        if (-not [string]::IsNullOrEmpty($ConfigurationJsonPath))
        {
            $configData = Get-Content -Path $ConfigurationJsonPath -ErrorAction SilentlyContinue | ConvertFrom-Json

            $deployData = $configData.ScaleUnits.DeploymentData | Select-Object -First 1

            if ($deployData)
            {
                if ($deployData.PSobject.Properties.name -eq $ParameterName)
                {
                    $CommandLineParameterValue = $deployData.PSobject.Properties.Item($ParameterName).Value
                }
            }
        }

        if ([string]::IsNullOrEmpty($ConfigurationJsonPath))
        {
            throw ($lcAdTxt.MissingRequiredParameter -f $ParameterName,$ParameterDescription)
        }
    }

    if (-not ($CommandLineParameterValue -match $ValidationRegex))
    {
        throw ($lcAdTxt.MalformedRequiredParameter -f $ParameterName,$CommandLineParameterValue,$ValidationRegex)
    }

    return $CommandLineParameterValue
}

function Get-ClusterNameFromCommandLineOrConfigFile {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$false)]
        [string]
        $ConfigurationJsonPath,

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

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

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

    # If CommandLineParameterValue is set, then use it and the config file doesn't matter
    if ([string]::IsNullOrEmpty($CommandLineParameterValue))
    {
        # If the configuration file is present, check to see if the value exists under the DeploymentData object
        # If we can find it, we'll overwrite $CommandLineParameterValue (so we can check for that later to see if it worked)
        if (-not [string]::IsNullOrEmpty($ConfigurationJsonPath))
        {
            $configData = Get-Content -Path $ConfigurationJsonPath -ErrorAction SilentlyContinue | ConvertFrom-Json

            $deployData = $configData.ScaleUnits.DeploymentData | Select-Object -First 1

            if ($deployData)
            {
                $clusterEntry = $deployData.Cluster
                $CommandLineParameterValue = if ($clusterEntry) { $clusterEntry.Name } else { "" }
            }
        }

        if ([string]::IsNullOrEmpty($ConfigurationJsonPath))
        {
            throw ($lcAdTxt.MissingRequiredParameter -f "ClusterName",$ParameterDescription)
        }
    }

    if (-not ($CommandLineParameterValue -match $ValidationRegex))
    {
        throw ($lcAdTxt.MalformedRequiredParameter -f "ClusterName",$CommandLineParameterValue,$ValidationRegex)
    }

    return $CommandLineParameterValue
}

function Install-GroupPolicyModule
{
    $modulePresent = $false

    try
    {
        $result = Get-Module -All | Where-Object { $_.Name -eq 'GroupPolicy' }
        if (-not $result)
        {
            # Module is not already imported. See if it's available.
            $result = Get-Module -Refresh -ListAvailable | Where-Object { $_.Name -eq 'GroupPolicy' }

            if ($result)
            {
                $result | Import-Module -WarningAction Ignore
                $modulePresent = $true
            }
        }
        else
        {
            $modulePresent = $true
        }
    }
    catch
    {
        # Module not present and not importable.
    }

    if (-not $modulePresent)
    {
        if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
        {
            throw $lcAdTxt.NotRunningElevated
        }

        try
        {
            $capability = $null

            try
            {
                # See if we're on Windows 10 Oct 2018 update or later where it's a windows capability
                $capability = Get-WindowsCapability -Online | Where-Object {$_.Name -like 'Rsat.GroupPolicy*'}
            }
            catch {}

            if ($capability)
            {
                # Yes, Windows 10 Oct 2018 or later. Check if it's present and add it if not
                if ($capability.State -ne 'Installed')
                {
                    ($capability | Add-WindowsCapability -Online)
                }
                else
                {
                    # The capability is present and installed, but the Get-Module above failed, so we should just bail
                    throw ($lcAdTxt.RsatCapabilityPresentButCantImport -f $capability.Name)
                }
            }
            else
            {
                # We're not on Windows 10 Oct 2018 or later. If we're on a server sku (or a client with RSAT installed), we
                # may be able to find the optional feature to install
                $optionalFeature = Get-WindowsOptionalFeature -Online | Where-Object {$_.FeatureName -eq 'Microsoft-Windows-GroupPolicy-ServerAdminTools-Update'}

                if ($optionalFeature)
                {
                    # Feature is known, so see if it's enabled or enable-able
                    if ($optionalFeature.State -eq 'Enabled')
                    {
                        # Feature is known, and enabled, but still the previous efforts didn't find the module. We should just bail.
                        throw ($lcAdTxt.RsatOptionalFeaturePresentButCantImport -f 'Microsoft-Windows-GroupPolicy-ServerAdminTools-Update')
                    }
                    else
                    {
                        # Feature is known, but not enabled
                        $result = ($optionalFeature | Enable-WindowsOptionalFeature -Online)
                    }
                }
                else
                {
                    # We cannot find the module from WindowsCapability or from WindowsOptionalFeature, and it wasn't in the available modules
                    # Not much we can do here except prompt the user to install RSAT
                    throw $lcAdTxt.MissingModuleAndRsat
                }
            }
        }
        catch
        {
            throw ($lcAdTxt.FailedToInstallRsat -f $_)
        }
    }

    if (-not $modulePresent)
    {
        # If we're here, it's because the module wasn't originally present, and our attempt at installing it seemed to be successful. Try to find it again
        try
        {
            $result = Get-Module -Refresh -ListAvailable | Where-Object { $_.Name -eq 'GroupPolicy' }
            if ($result)
            {
                # AD module warns about finding a default server, but we'll specify everything later
                $result | Import-Module -WarningAction Ignore
            }
            else
            {
                throw $lcAdTxt.ModuleStillMissingAfterRsatInstall
            }
        }
        catch
        {
            throw ($lcAdTxt.FailToLoadModuleAfterRsatInstall -f $_)
        }
    }
}

function Install-ActiveDirectoryModule
{
    $modulePresent = $false

    try
    {
        $result = Get-Module -All | Where-Object { $_.Name -eq 'ActiveDirectory' }
        if (-not $result)
        {
            # Module is not already imported. See if it's available.
            $result = Get-Module -Refresh -ListAvailable | Where-Object { $_.Name -eq 'ActiveDirectory' }

            if ($result)
            {
                # AD module warns about finding a default server, but we'll specify everything later
                $result | Import-Module -WarningAction Ignore
                $modulePresent = $true
            }
        }
        else
        {
            $modulePresent = $true
        }
    }
    catch
    {
        # Module not present and not importable.
    }

    if (-not $modulePresent)
    {
        if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
        {
            throw $lcAdTxt.NotRunningElevated
        }

        $windowsFeature = $null

        # See if we're on a server version that installs AD Powershell with Add-WindowsFeature
        try {
            $windowsFeature = Get-WindowsFeature -Name "RSAT-AD-PowerShell"
        }
        catch {
        }

        if ($windowsFeature -and $windowsFeature.InstallState -ne [Microsoft.Windows.ServerManager.Commands.InstallState]::Installed)
        {
            Add-WindowsFeature -Name "RSAT-AD-PowerShell" -IncludeAllSubFeature
        }

        $result = Get-Module -Refresh -ListAvailable | Where-Object { $_.Name -eq 'ActiveDirectory' }

        $modulePresent = ($null -ne $result)
    }

    if (-not $modulePresent)
    {
        try
        {
            $capability = $null

            try
            {
                # See if we're on Windows 10 Oct 2018 update or later where it's a windows capability
                $capability = Get-WindowsCapability -Online | Where-Object {$_.Name -like 'Rsat.ActiveDirectory*'}
            }
            catch {}

            if ($capability)
            {
                # Yes, Windows 10 Oct 2018 or later. Check if it's present and add it if not
                if ($capability.State -ne 'Installed')
                {
                    ($capability | Add-WindowsCapability -Online)
                }
                else
                {
                    # The capability is present and installed, but the Get-Module above failed, so we should just bail
                    throw ($lcAdTxt.RsatCapabilityPresentButCantImport -f $capability.Name)
                }
            }
            else
            {
                # We're not on Windows 10 Oct 2018 or later. If we're on a server sku (or a client with RSAT installed), we
                # may be able to find the optional feature to install
                $optionalFeature = Get-WindowsOptionalFeature -Online | Where-Object {$_.FeatureName -eq 'RSAT-ADDS-Tools-Feature'}

                if ($optionalFeature)
                {
                    # Feature is known, so see if it's enabled or enable-able
                    if ($optionalFeature.State -eq 'Enabled')
                    {
                        # Feature is known, and enabled, but still the previous efforts didn't find the module. We should just bail.
                        throw ($lcAdTxt.RsatOptionalFeaturePresentButCantImport -f 'RSAT-ADDS-Tools-Feature')
                    }
                    else
                    {
                        # Feature is known, but not enabled
                        $result = ($optionalFeature | Enable-WindowsOptionalFeature -Online)
                    }
                }
                else
                {
                    # We cannot find the module from WindowsCapability or from WindowsOptionalFeature, and it wasn't in the available modules
                    # Not much we can do here except prompt the user to install RSAT
                    throw $lcAdTxt.MissingModuleAndRsat
                }
            }
        }
        catch
        {
            throw ($lcAdTxt.FailedToInstallRsat -f $_)
        }
    }

    if (-not $modulePresent)
    {
        # If we're here, it's because the module wasn't originally present, and our attempt at installing it seemed to be successful. Try to find it again
        try
        {
            $result = Get-Module -Refresh -ListAvailable | Where-Object { $_.Name -eq 'ActiveDirectory' }
            if ($result)
            {
                # AD module warns about finding a default server, but we'll specify everything later
                $result | Import-Module -WarningAction Ignore
            }
            else
            {
                throw $lcAdTxt.ModuleStillMissingAfterRsatInstall
            }
        }
        catch
        {
            throw ($lcAdTxt.FailToLoadModuleAfterRsatInstall -f $_)
        }
    }

    # Sometimes we seem to import the module, but don't get the PS provider as well. Should be safe to just import again here to see
    if (-not (Get-PSProvider -PSProvider ActiveDirectory -ErrorAction SilentlyContinue))
    {
        Import-Module 'ActiveDirectory' -Force
    }
    if (-not (Get-PSProvider -PSProvider ActiveDirectory -ErrorAction SilentlyContinue))
    {
        throw ("Can't find ActiveDirectory PSProvider!")
    }
}
# SIG # Begin signature block
# MIInvwYJKoZIhvcNAQcCoIInsDCCJ6wCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCk1lB13SuzPNoo
# P6ibFNgVq4gdAErR/enuPYqf/s9KraCCDXYwggX0MIID3KADAgECAhMzAAACy7d1
# OfsCcUI2AAAAAALLMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjIwNTEyMjA0NTU5WhcNMjMwNTExMjA0NTU5WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQC3sN0WcdGpGXPZIb5iNfFB0xZ8rnJvYnxD6Uf2BHXglpbTEfoe+mO//oLWkRxA
# wppditsSVOD0oglKbtnh9Wp2DARLcxbGaW4YanOWSB1LyLRpHnnQ5POlh2U5trg4
# 3gQjvlNZlQB3lL+zrPtbNvMA7E0Wkmo+Z6YFnsf7aek+KGzaGboAeFO4uKZjQXY5
# RmMzE70Bwaz7hvA05jDURdRKH0i/1yK96TDuP7JyRFLOvA3UXNWz00R9w7ppMDcN
# lXtrmbPigv3xE9FfpfmJRtiOZQKd73K72Wujmj6/Su3+DBTpOq7NgdntW2lJfX3X
# a6oe4F9Pk9xRhkwHsk7Ju9E/AgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUrg/nt/gj+BBLd1jZWYhok7v5/w4w
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKzQ3MDUyODAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAJL5t6pVjIRlQ8j4dAFJ
# ZnMke3rRHeQDOPFxswM47HRvgQa2E1jea2aYiMk1WmdqWnYw1bal4IzRlSVf4czf
# zx2vjOIOiaGllW2ByHkfKApngOzJmAQ8F15xSHPRvNMmvpC3PFLvKMf3y5SyPJxh
# 922TTq0q5epJv1SgZDWlUlHL/Ex1nX8kzBRhHvc6D6F5la+oAO4A3o/ZC05OOgm4
# EJxZP9MqUi5iid2dw4Jg/HvtDpCcLj1GLIhCDaebKegajCJlMhhxnDXrGFLJfX8j
# 7k7LUvrZDsQniJZ3D66K+3SZTLhvwK7dMGVFuUUJUfDifrlCTjKG9mxsPDllfyck
# 4zGnRZv8Jw9RgE1zAghnU14L0vVUNOzi/4bE7wIsiRyIcCcVoXRneBA3n/frLXvd
# jDsbb2lpGu78+s1zbO5N0bhHWq4j5WMutrspBxEhqG2PSBjC5Ypi+jhtfu3+x76N
# mBvsyKuxx9+Hm/ALnlzKxr4KyMR3/z4IRMzA1QyppNk65Ui+jB14g+w4vole33M1
# pVqVckrmSebUkmjnCshCiH12IFgHZF7gRwE4YZrJ7QjxZeoZqHaKsQLRMp653beB
# fHfeva9zJPhBSdVcCW7x9q0c2HVPLJHX9YCUU714I+qtLpDGrdbZxD9mikPqL/To
# /1lDZ0ch8FtePhME7houuoPcMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# 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
# /Xmfwb1tbWrJUnMTDXpQzTGCGZ8wghmbAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAALLt3U5+wJxQjYAAAAAAsswDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIB5Gid20v3aNTAfownuKWDfg
# VRGXR1GpcsD/9FWIh/SDMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEADv6wyUlqs+3tt2DYW07E8qY3AKVcDxutOrq8xpjdXWXP8NmpSnlMcVQj
# CX2hapHFdJAzc8N0aJt5ZzdKu5VBoSPiqfMCX9w6RIzgD6ARMdnGHYx54oj4J9cl
# CWSQg6B1a1vI1YQKDpzrrAGXFwXVrxnM/10rK8to/WoKtFfGadt19edkD2x0hNJU
# 1rMiZnK4ukBUeLdSGsPpHMjd2UD8MyovvqabsvbA3XwZvXBOrW8cAfhMDdR+hZBf
# ORtFF4e5+ME9Z/lma0GRuI+P6sNFB+aKmzCGydRtuwt1SZv7mE5tZN3aCYSi8Y33
# Xz+7p231MV1Do/B19GMLuGw2pOQpXKGCFykwghclBgorBgEEAYI3AwMBMYIXFTCC
# FxEGCSqGSIb3DQEHAqCCFwIwghb+AgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFZBgsq
# hkiG9w0BCRABBKCCAUgEggFEMIIBQAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCBxDKqdqEV2XnG34axII6Ep4wnZNGum2NgSec8nyXPYCQIGY/daFWnT
# GBMyMDIzMDMyMTEwMDcwNi44NDNaMASAAgH0oIHYpIHVMIHSMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl
# bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNO
# OjNCRDQtNEI4MC02OUMzMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT
# ZXJ2aWNloIIReDCCBycwggUPoAMCAQICEzMAAAG0+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
# tB1VM1izoXBm8qGCAtQwggI9AgEBMIIBAKGB2KSB1TCB0jELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxh
# bmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjoz
# QkQ0LTRCODAtNjlDMzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy
# dmljZaIjCgEBMAcGBSsOAwIaAxUAZZzYkPObl/ZzeCkSbf4B5CceCQiggYMwgYCk
# fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF
# AOfDdSYwIhgPMjAyMzAzMjEwODEzMjZaGA8yMDIzMDMyMjA4MTMyNlowdDA6Bgor
# BgEEAYRZCgQBMSwwKjAKAgUA58N1JgIBADAHAgEAAgJOgzAHAgEAAgIRozAKAgUA
# 58TGpgIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAID
# B6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAKKq+QGKBdMMFQSf0Xzd
# V3KQ1HN945vY7iEgGgTKxwkUJujHiv9mRIB7fNkGKu5qxeNdOH24viDEgKGDeEBg
# B4pcX4Zfx1kowUZIEpVw95yHYPGpTgBnj4/aKN9znPE7Q8hRFaIopKUHDKttUrWr
# 0ySlhLKfJPomMCKme/mB9IeEMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgUENBIDIwMTACEzMAAAG0+4AIRAXSLfoAAQAAAbQwDQYJYIZIAWUDBAIB
# BQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQx
# IgQguPwg88SkPeV0jGtVLuuhm9TavDEUJ2bDhZTmDO0eK5UwgfoGCyqGSIb3DQEJ
# EAIvMYHqMIHnMIHkMIG9BCDTyPd75qMwcAZRcb36/6xJa3hT0eLse71ysdp4twH3
# BjCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABtPuA
# CEQF0i36AAEAAAG0MCIEIHMV18aiNka+09vhWkPFjiiBWxJZu2K8e3V+rXylPK/g
# MA0GCSqGSIb3DQEBCwUABIICAJxmJUqSqkVDp4ghGLePgwRkZAllFBF5TgOu/HkQ
# IgsiYjeBYdSikY+I6D52bUreJL3IjFxvlHsw2ufbuZCXaDNBkECFXnpH/uKCtG1x
# OrHuLlO2A7yT3BYEm5r1adZrLdxtf/Xu8QFMyi5WMfP1wqgFmp/XYnRoN7nrBlyY
# 11RKqJQeun7eT6LB/Gom6k+/p82dP9jryc2zm1tqtWyulvM4y9Rz6WZNWUer1W6r
# Bvrzvf6JfKKNm36FGoIqAmnQoiTh4T5orIblqQO7kRf1N4X+veI3SfZ+iRGyPzs6
# 5QlLOWoWSYnkw0QJrhmaCR3G/a8r805JDM7aSOjfexOwGfzAV/3tXBx/6SqauZ0S
# qqlGBq0DUcD++WtT6iTiij4JAI2Se829OQKiheivCQzJ39Bw7v/l354/STntLGZD
# fDVEexrpVXOqYd9XPnO/Unmafx6pQ0KmwPb6A5LlzI0LoqbjNRfxJu+EZ73zgJCC
# ULD/WCHOQwvysN7dU5UX8X3QLLF4o35VUF2shnu62i6CwxKb9tmAaFSRKh6IWRh/
# mHWSnNLAtaMfKtWNXCKGOYJDdvNUSOI4ywkwWv69wpZ5lFFR6R87ALSgAZpf2JuB
# dguttvDwplhpTzkjogAH8s84HhRkIFqd1kayoSbCAA+FX2/B9UXIML5X8GMPwnKu
# I2S4
# SIG # End signature block