classes/OSConfig.ps1

# Copyright (c) Microsoft Corporation. All rights reserved.

function Get-ServerType() {
    try {
        $Value = Get-ItemPropertyValue -LiteralPath "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters" -Name "SysvolReady"
        if (-not [String]::IsNullOrWhiteSpace($Value)) {
            return "Domain Controller"
        }
    } catch {
        # Ignored.
    }

    try {
        $Value = Get-ItemPropertyValue -LiteralPath "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" -Name "Domain"
        if (-not [String]::IsNullOrWhiteSpace($Value)) {
            return "Member Server"
        }
    } catch {
        # Ignored.
    }

    return "Workgroup Member"
}

function Get-EnvironmentType {
    $Properties = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion"

    if ($Properties.InstallationType -match "Server") {
        if ($Properties.EditionId -eq "ServerAzureStackHCICor") {
            $InstallationType = "AzureLocal"

            if ($Properties.DisplayVersion -eq "24H2") {
                $Version = "24H2"
            } elseif ($Properties.DisplayVersion -eq "23H2") {
                $Version = "23H2"
            } else {
                return
            }
        } else {
            $InstallationType = "WindowsServer"

            # We need to have the necessary update for Windows Server 2022 and Windows Server 23H2
            # but not for Windows Server 2025. We can use the UBR (Update Build Revision) to
            # determine the version.

            if ($Properties.DisplayVersion -eq "24H2") {
                $Version = "2025"
            } elseif (($Properties.DisplayVersion -eq "23H2") -and ($Properties.UBR -ge 1369)) {
                $Version = "2022"
            } elseif (($Properties.DisplayVersion -eq "21H2") -and ($Properties.UBR -ge 3091)) {
                $Version = "2022"
            } else {
                return
            }
        }
    } else {
        return
    }

    $Role = (Get-ServerType) -replace " ", ""

    "$InstallationType\$Version\$Role"
}

function ConvertFrom-Expression($Expression) {
    if ($Expression -isnot [String] -or [String]::IsNullOrWhiteSpace($Expression)) {
        throw [InvalidValueException]::new($Strings.ErrorInvalidExpression -f $Expression)
    }

    $InQuotes = $False
    $ProcessedExpression = $Expression.Clone()

    for ($i = 0; $i -lt $Expression.Length; $i++) {
        if ($Expression[$i] -eq '"') {
            $InQuotes = -not $InQuotes
            continue
        }

        if ($InQuotes) {
            continue
        }

        if ($Expression[$i] -eq ";") {
            $ProcessedExpression = $ProcessedExpression.Remove($i, 1).Insert($i, "`n")
        }
    }

    try {
        $Result = @($ProcessedExpression | ConvertFrom-Csv -Header @("Name", "Value") -Delimiter ":")

        if (($Result.Count -eq 1) -and (-not $Result.Value)) {
            $Result[0].Value = $Result[0].Name
            $Result[0].Name = "*"
        }

        , $Result
    } catch {
        throw [InvalidValueException]::new($Strings.ErrorInvalidExpression -f $Expression)
    }
}

function Find-Rule($Name, $Rules) {
    foreach ($Rule in $Rules) {
        $InclusionRule = -not $Rule.Name.StartsWith("!")

        if ($InclusionRule -and ($Name -like $Rule.Name)) {
            return $Rule
        }

        if (-not $InclusionRule -and ($Name -like $Rule.Name.Substring(1))) {
            return $null
        }
    }
}

# Temporary workaround until `$Parameters` is implemented
function ConvertTo-ResourceValue($Type, $Properties, $Value) {
    if ($null -eq $Value) {
        return $Value
    }

    switch ($Type) {
        'Microsoft.Windows/CSP' {
            switch ($Properties.Type) {
                'string' {
                    return $Value
                }
                'integer' {
                    return [Int64] $Value
                }
                'boolean' {
                    return [Bool]::Parse($Value)
                }
            }
        }
        'Microsoft.Windows/Registry' {
            switch ($Properties.Type) {
                'REG_SZ' {
                    return $Value
                }
                'REG_MULTI_SZ' {
                    return $Value -split ',' | ForEach-Object { $_.Trim() }
                }
                'REG_DWORD' {
                    return [Int32] $Value
                }
            }
        }
    }
}

class OSConfigReason {
    [DscProperty()]
    [String] $Code

    [DscProperty()]
    [String] $Phrase

    OSConfigReason() { }

    OSConfigReason([String] $RuleId, [Bool] $IsCompliant, [String] $Reason) {
        $Status = if ($IsCompliant) { 'BaselineSettingCompliant' } else { 'BaselineSettingNotCompliant' }

        if ($RuleId) {
            $Status = "$Status`:$RuleId"
        }

        $this.Code = $Status
        $this.Phrase = $Reason
    }
}

[DscResource()]
class OSConfig {
    [DscProperty()]
    [String] $RuleId

    [DscProperty()]
    [String] $Severity

    [DscProperty(Key)]
    [String] $Name

    [DscProperty(Key)]
    [String] $Type

    [DscProperty(Key)]
    [String] $Properties

    # [DscProperty()]
    # [String] $Parameters

    [DscProperty()]
    [String] $Value

    [DscProperty()]
    [String] $Compliance

    [DscProperty()]
    [String] $RoleFilter

    [DscProperty()]
    [String] $VersionFilter

    [DscProperty(NotConfigurable)]
    [OSConfigReason[]] $Reasons

    hidden [Bool] $IsCompliant = $True

    [OSConfig] Get() {
        $CurrentState = [OSConfig]::new()

        $CurrentState.RuleId = $this.RuleId
        $CurrentState.Severity = $this.Severity
        $CurrentState.Name = $this.Name
        $CurrentState.Type = $this.Type
        $CurrentState.Properties = $this.Properties
        # $CurrentState.Parameters = $this.Parameters
        $CurrentState.Value = $this.Value
        $CurrentState.Compliance = $this.Compliance
        $CurrentState.RoleFilter = $this.RoleFilter
        $CurrentState.VersionFilter = $this.VersionFilter
        $CurrentState.Reasons = @()

        try {
            $ErrorActionPreference = 'Stop'

            $Schema = $this.GetSchema()

            if ((-not $this.IsApplicable()) -or ($null -eq $Schema)) {
                $CurrentState.IsCompliant = $True
                $CurrentState.Reasons += [OSConfigReason]::new($this.RuleId, $CurrentState.IsCompliant, 'Not applicable')
                return $CurrentState
            }

            $Resource = @{
                'name'       = $this.Name
                'type'       = 'Microsoft.OSConfig/Test'
                'properties' = @{
                    'resource' = @{
                        'name'       = $this.Name
                        'type'       = $this.Type
                        'properties' = $this.Properties | ConvertFrom-Json
                    }
                    'schema'   = $Schema
                }
            }

            $Output = Invoke-Native resource status show --name $Resource.Name --type $Resource.Type --properties ($Resource.Properties | ConvertTo-Json -Depth $Script:Constants.MaxJsonDepth -Compress)

            $ComplianceResult = $Output | ConvertFrom-Json | Select-Object -ExpandProperty resources | Select-Object -First 1 | Select-Object -ExpandProperty properties | Select-Object -ExpandProperty compliance

            $CurrentState.IsCompliant = $ComplianceResult.status -eq 'compliant'
            $CurrentState.Reasons += [OSConfigReason]::new($this.RuleId, $CurrentState.IsCompliant, $ComplianceResult.reason)
        } catch {
            $CurrentState.IsCompliant = $False
            $CurrentState.Reasons += [OSConfigReason]::new($this.RuleId, $CurrentState.IsCompliant, "Failed to get reported configuration: $_")
            Write-Verbose "Error: $_"
        }

        return $CurrentState
    }

    [Bool] Test() {
        try {
            return $this.Get().IsCompliant
        } catch {
            Write-Verbose "Error: $_"
        }

        return $False
    }

    [Void] Set() {
        try {
            if ($this.Test()) {
                return
            }

            throw 'Not implemented!'
        } catch {
            Write-Verbose "Error: $_"
        }
    }

    [Bool] IsApplicable() {
        if (-not [String]::IsNullOrWhiteSpace($this.RoleFilter)) {
            $CurrentRole = Get-ServerType
            $AllowedRoles = $this.RoleFilter -split ',' | ForEach-Object { $_.Trim() }

            if ($AllowedRoles -notcontains $CurrentRole) {
                return $False
            }
        }

        if (-not [String]::IsNullOrWhiteSpace($this.VersionFilter)) {
            $CurrentVersion = Get-WindowsServerVersion

            if (-not $CurrentVersion) {
                return $False
            }

            $AllowedVersions = $this.VersionFilter -split ',' | ForEach-Object { $_.Trim() }

            if ($AllowedVersions -notcontains $CurrentVersion) {
                return $False
            }
        }

        return $True
    }

    [PSCustomObject] GetSchema() {
        if ($this.Compliance) {
            return ConvertFrom-Json -InputObject $this.Compliance
        }

        $Rules = if (-not [String]::IsNullOrWhiteSpace($this.Value)) {
            ConvertFrom-Expression -Expression $this.Value
        }

        $EnvironmentType = Get-EnvironmentType
        $Rule = Find-Rule -Name $EnvironmentType -Rules $Rules

        if ($Rules -and (-not $Rule)) {
            return $null # Not applicable
        }

        if (-not $Rules -and [String]::IsNullOrWhiteSpace($this.Value)) {
            throw 'No rules or value specified.'
        }

        $StringValue = if ($null -ne $Rule) { $Rule.Value } else { $this.Value }
        return @{ 'const' = ConvertTo-ResourceValue -Type $this.Type -Properties $this.Properties -Value $StringValue }
    }
}

# SIG # Begin signature block
# MIIoKgYJKoZIhvcNAQcCoIIoGzCCKBcCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA1iiukD4tbzyII
# D5XBGjy26fJxogK3L3bn3sVtEp+I+qCCDXYwggX0MIID3KADAgECAhMzAAAEhV6Z
# 7A5ZL83XAAAAAASFMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjUwNjE5MTgyMTM3WhcNMjYwNjE3MTgyMTM3WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDASkh1cpvuUqfbqxele7LCSHEamVNBfFE4uY1FkGsAdUF/vnjpE1dnAD9vMOqy
# 5ZO49ILhP4jiP/P2Pn9ao+5TDtKmcQ+pZdzbG7t43yRXJC3nXvTGQroodPi9USQi
# 9rI+0gwuXRKBII7L+k3kMkKLmFrsWUjzgXVCLYa6ZH7BCALAcJWZTwWPoiT4HpqQ
# hJcYLB7pfetAVCeBEVZD8itKQ6QA5/LQR+9X6dlSj4Vxta4JnpxvgSrkjXCz+tlJ
# 67ABZ551lw23RWU1uyfgCfEFhBfiyPR2WSjskPl9ap6qrf8fNQ1sGYun2p4JdXxe
# UAKf1hVa/3TQXjvPTiRXCnJPAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUuCZyGiCuLYE0aU7j5TFqY05kko0w
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKzUwNTM1OTAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBACjmqAp2Ci4sTHZci+qk
# tEAKsFk5HNVGKyWR2rFGXsd7cggZ04H5U4SV0fAL6fOE9dLvt4I7HBHLhpGdE5Uj
# Ly4NxLTG2bDAkeAVmxmd2uKWVGKym1aarDxXfv3GCN4mRX+Pn4c+py3S/6Kkt5eS
# DAIIsrzKw3Kh2SW1hCwXX/k1v4b+NH1Fjl+i/xPJspXCFuZB4aC5FLT5fgbRKqns
# WeAdn8DsrYQhT3QXLt6Nv3/dMzv7G/Cdpbdcoul8FYl+t3dmXM+SIClC3l2ae0wO
# lNrQ42yQEycuPU5OoqLT85jsZ7+4CaScfFINlO7l7Y7r/xauqHbSPQ1r3oIC+e71
# 5s2G3ClZa3y99aYx2lnXYe1srcrIx8NAXTViiypXVn9ZGmEkfNcfDiqGQwkml5z9
# nm3pWiBZ69adaBBbAFEjyJG4y0a76bel/4sDCVvaZzLM3TFbxVO9BQrjZRtbJZbk
# C3XArpLqZSfx53SuYdddxPX8pvcqFuEu8wcUeD05t9xNbJ4TtdAECJlEi0vvBxlm
# M5tzFXy2qZeqPMXHSQYqPgZ9jvScZ6NwznFD0+33kbzyhOSz/WuGbAu4cHZG8gKn
# lQVT4uA2Diex9DMs2WHiokNknYlLoUeWXW1QrJLpqO82TLyKTbBM/oZHAdIc0kzo
# STro9b3+vjn2809D0+SOOCVZMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
# IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg
# Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
# CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03
# a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr
# rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg
# OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy
# 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9
# sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh
# dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k
# A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB
# w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn
# Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90
# lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w
# ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o
# ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD
# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa
# BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny
# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG
# AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV
# HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG
# AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl
# AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb
# C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l
# hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6
# I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0
# wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560
# STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam
# ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa
# J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah
# XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA
# 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt
# Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr
# /Xmfwb1tbWrJUnMTDXpQzTGCGgowghoGAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAASFXpnsDlkvzdcAAAAABIUwDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIGH1abI8SJ8HAVTaAOvE+NZ0
# xyA7YtkVbYVKw0AquLTDMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEAe4cZ0PMKtrKnSJGbnPH4JPTIxvaz6lXs1LsRoKahitHMB+hGH/vjINmd
# PTyNGdQVHdv6K1WSMjyT6fjblc2t/Q3MDhKKE4thPPWlrsC1jUn7e+OZe68BkDwU
# vfjRCz/GSqdoxKxe3w39CUNnkGAXUk3tcjsCG2HDtJuA6ftKs64joPOdXn98r27F
# k37Tc3zHOU8H3iriyYfzj5AaLPc4jHioHtjwqp8XiX2//xj7HX688uhxvHl1fPqP
# y+zI4tWJd0/0dNkKrVVqKlUxdNJiDwkK35iE0iKcZOaGY1rPcuiJuaZpfmZdO8nk
# NM7YVSdGsmPm/1L3LiTH/d+i2cDq2aGCF5QwgheQBgorBgEEAYI3AwMBMYIXgDCC
# F3wGCSqGSIb3DQEHAqCCF20wghdpAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq
# hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCAoBa6HYwmJ/JL1mk0FeIyrG4wRcbaBbjJZ09/JPai92QIGaO/YsYQN
# GBMyMDI1MTExMzAyNTAyOC41MThaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l
# cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046QTQwMC0w
# NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg
# ghHqMIIHIDCCBQigAwIBAgITMwAAAgJ5UHQhFH24oQABAAACAjANBgkqhkiG9w0B
# AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yNTAxMzAxOTQy
# NDRaFw0yNjA0MjIxOTQyNDRaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
# cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z
# MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046QTQwMC0wNUUwLUQ5NDcxJTAjBgNV
# BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQC3eSp6cucUGOkcPg4vKWKJfEQeshK2ZBsYU1tDWvQu
# 6L9lp+dnqrajIdNeH1HN3oz3iiGoJWuN2HVNZkcOt38aWGebM0gUUOtPjuLhuO5d
# 67YpQsHBJAWhcve/MVdoQPj1njiAjSiOrL8xFarFLI46RH8NeDhAPXcJpWn7AIzC
# yIjZOaJ2DWA+6QwNzwqjBgIpf1hWFwqHvPEedy0notXbtWfT9vCSL9sdDK6K/HH9
# HsaY5wLmUUB7SfuLGo1OWEm6MJyG2jixqi9NyRoypdF8dRyjWxKRl2JxwvbetlDT
# io66XliTOckq2RgM+ZocZEb6EoOdtd0XKh3Lzx29AhHxlk+6eIwavlHYuOLZDKod
# POVN6j1IJ9brolY6mZboQ51Oqe5nEM5h/WJX28GLZioEkJN8qOe5P5P2Yx9HoOqL
# ugX00qCzxq4BDm8xH85HKxvKCO5KikopaRGGtQlXjDyusMWlrHcySt56DhL4dcVn
# n7dFvL50zvQlFZMhVoehWSQkkWuUlCCqIOrTe7RbmnbdJosH+7lC+n53gnKy4OoZ
# zuUeqzCnSB1JNXPKnJojP3De5xwspi5tUvQFNflfGTsjZgQAgDBdg/DO0TGgLRDK
# vZQCZ5qIuXpQRyg37yc51e95z8U2mysU0XnSpWeigHqkyOAtDfcIpq5Gv7HV+da2
# RwIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFNoGubUPjP2f8ifkIKvwy1rlSHTZMB8G
# A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG
# Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy
# MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w
# XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy
# dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG
# A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD
# AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQCD83aFQUxN37HkOoJDM1maHFZVUGcqTQcP
# nOD6UoYRMmDKv0GabHlE82AYgLPuVlukn7HtJPF2z0jnTgAfRMn26JFLPG7O/XbK
# K25hrBPJ30lBuwjATVt58UA1BWo7lsmnyrur/6h8AFzrXyrXtlvzQYqaRYY9k0UF
# Y5GM+n9YaEEK2D268e+a+HDmWe+tYL2H+9O4Q1MQLag+ciNwLkj/+QlxpXiWou9K
# vAP0tIk+fH8F3ww5VOTi9aZ9+qPjszw31H4ndtivBZaH5s5boJmH2JbtMuf2y7hS
# dJdE0UW2B0FEZPLImemlKhslJNVqEO7RPgl7c81QuVSO58ffpmbwtSxhYrES3VsP
# glXn9ODF7DqmPMG/GysB4o/QkpNUq+wS7bORTNzqHMtH+ord2YSma+1byWBr/izI
# KggOCdEzaZDfym12GM6a4S+Iy6AUIp7/KIpAmfWfXrcMK7V7EBzxoezkLREEWI4X
# tPwpEBntOa1oDH3Z/+dRxsxL0vgya7jNfrO7oizTAln/2ZBYB9ioUeobj5AGL45m
# 2mcKSk7HE5zUReVkILpYKBQ5+X/8jFO1/pZyqzQeI1/oJ/RLoic1SieLXfET9EWZ
# IBjZMZ846mDbp1ynK9UbNiCjSwmTF509Yn9M47VQsxsv1olQu51rVVHkSNm+rTrL
# wK1tvhv0mTCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI
# hvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# MjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAy
# MDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
# bWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
# AQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25Phdg
# M/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPF
# dvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6
# GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBp
# Dco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50Zu
# yjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3E
# XzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0
# lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1q
# GFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ
# +QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PA
# PBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkw
# EgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxG
# NSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARV
# MFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj
# cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAK
# BggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC
# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX
# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v
# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI
# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG
# 9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0x
# M7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmC
# VgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449
# xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wM
# nosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDS
# PeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2d
# Y3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxn
# GSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+Crvs
# QWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokL
# jzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL
# 6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNN
# MIICNQIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEn
# MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOkE0MDAtMDVFMC1EOTQ3MSUwIwYDVQQD
# ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQBJ
# iUhpCWA/3X/jZyIy0ye6RJwLzqCBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA7L89fDAiGA8yMDI1MTExMjE3MDgx
# MloYDzIwMjUxMTEzMTcwODEyWjB0MDoGCisGAQQBhFkKBAExLDAqMAoCBQDsvz18
# AgEAMAcCAQACAhKmMAcCAQACAhIXMAoCBQDswI78AgEAMDYGCisGAQQBhFkKBAIx
# KDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZI
# hvcNAQELBQADggEBAI2Gmf/CRH3Aamp2tFWVYg1x2ojbZMWGFcDtDZ6ZbZS18oVM
# P8zdOs1fEIE/glgrACAQigqgYaIMKcxEhgksSusfVbcaDkf5jfPiJ7/nvowJ0osl
# fcRCZFUZoPvEOMTrXYT9XCHwrv91YytWEIYaDFJ5TexU80U7xfuPEJWQu3jjt3zK
# 2w51RhwzujVHXvuoqxOtrQfSJtYd5HlIOnpo2FO7hfCW3hikg+B6G3i3pCXOGsiW
# 7rLnOI3sfn2eivFtOkGGumU2yKl8/vTZgaKL52+pOI8+J36OJSJodCs/WQDYTC3p
# hoDLA7l3YMue/tB01vV2TY66IXClGH6AT+lFX9MxggQNMIIECQIBATCBkzB8MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNy
# b3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAgJ5UHQhFH24oQABAAACAjAN
# BglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8G
# CSqGSIb3DQEJBDEiBCCdrbq1wuclklgcQLbUas94qr0rJ41/oKPjEHZZgDQarjCB
# +gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIPON6gEYB5bLzXLWuUmL8Zd8xXAs
# qXksedFyolfMlF/sMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh
# c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD
# b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw
# MTACEzMAAAICeVB0IRR9uKEAAQAAAgIwIgQgPnCfB3kBz5FshMvxsJPGQCpRb+TZ
# dT0sKDPm7rw6KtAwDQYJKoZIhvcNAQELBQAEggIAjceI9OART4wbXrnKhMRysm+6
# xl3T6H7CDh8tCM2x1guMcvlUXUnOd9++JeDc0C2/1TdYBHMkBKEPsQlV9UGdFwIk
# ByE+7rKuL8oYrxsPUa7tt0Y8KTXWzk/yXzpPbyNt8RHtAuUfz1gzWP1gCTpAWKXt
# r9A+iMVJ6EnnuzGkf/ltqxyI6w/YRzm3w7pynOb+/e+bQGMdqhFgRH5pKy66sEHA
# Fi3NVZutYZDz2JC6YfcPjKaRCnX3ZhB78GCFt1eIVTVnXiJck/UnfZeQZRs6G52B
# fkLKRfKyacCfKpr5EZcqmEDmi+nUcEt6tiktKxZF1F94iksiw7EsQFpnHUvWJ2B0
# SwvsTc6K/VWSZj7eMjL4W3LeEHT80QMO5uYJAN9xmeDkp8DAwgcwMquK4INarRNl
# 7CcPO77ZalzkpqkPjw3PJeyQ8Tp7DteQObGPUOlx+dfZqtBMLXt/dwipr++Y4vKZ
# ViuiztXtT/iJeSlmD6pcl/wQXVALgRjjbnWHXZ28X1v0rjTBIvYhaqUCM6upXorW
# zwsb0QNkFhPU72M7dkn3d465IOIaE8vLkBb2EpEXE48zOnIsAXMgi1QiGFv2dPv2
# rj1+cBdwrRd9Aw2PwEWGUmyTkUV8qdysQGDbztaCqEuTWQqaMbUyWNntvCseHn+F
# hY50ew7A+/NCAjIaSC4=
# SIG # End signature block