Public/Get-ADFSTkHealth.ps1
function Get-ADFSTkHealth { [CmdletBinding()] param ( #The path to the institution configuration file that should be handled. If not provided all institution config files present in ADFSTk config will be used. $ConfigFile, #The HealtheCheckMode states how rigorous tests should be done. Defalut is done every time Sync-ADFSTkAggregates are run. [ValidateSet("CriticalOnly", "Default", "Full")] $HealthCheckMode = "Default", #Silent only reports true/false without output. It also tries to fix errors that can be fixed automatically. [switch]$Silent ) $healtChecks = @{ CheckSignature = ($HealthCheckMode -ne "CriticalOnly") #Don't run i CriticalOnly CheckConfigVersion = $true MFAAccesControlPolicy = $true RemovedSPsStillInSPHash = ($HealthCheckMode -eq "Full") #Only run in Full mode MissingSPsInADFS = ($HealthCheckMode -eq "Full") #Only run in Full mode ScheduledTaskPresent = ($HealthCheckMode -eq "Full") #Checks if there are a Scheduled Task with the name 'Import Federated Metadata with ADFSToolkit' } enum Result { None Pass Warning Fail } $healthResults = @() #Get All paths if ([string]::IsNullOrEmpty($Global:ADFSTkPaths)) { $Global:ADFSTkPaths = Get-ADFSTKPaths } #region get config file(s) $configFiles = @() if ($PSBoundParameters.ContainsKey('configFile')) { $configFiles += $configFile } else { $configFiles = Get-ADFSTkConfiguration -ConfigFilesOnly | ? Enabled -eq $true | select -ExpandProperty ConfigFile } #endregion #region check script signatures if ($healtChecks.CheckSignature) { Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckSignatureStartMessage) $Signatures = Get-ChildItem -Path $Global:ADFSTkPaths.modulePath -Filter *.ps1 -Recurse | Get-AuthenticodeSignature $validSignatures = $Signatures | ? Status -eq Valid | Select -ExpandProperty Path $invalidSignatures = $Signatures | ? Status -eq HashMismatch | Select -ExpandProperty Path $missingSignatures = $Signatures | ? Status -eq NotSigned | Select -ExpandProperty Path Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckSignatureValidSignaturesResult -f $validSignatures.Count) Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckSignatureInvalidSignaturesResult -f $invalidSignatures.Count) Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckSignatureMissingSignaturesResult -f $missingSignatures.Count) #Signature(s) missing... if ($missingSignatures.Count -gt 0) { if ($Global:ADFSTkSkipNotSignedHealthCheck -eq $true) { $resultObject = [PSCustomObject]@{ CheckID = "CheckSignature" CheckName = "Signature check" ResultValue = [Result]::Pass ResultText = Get-ADFSTkLanguageText healthCheckSignatureSkipNotSignedMessage ResultData = @() ReferenceFile = "" FixID = "" } Write-ADFSTkVerboseLog $resultObject.ResultText $healthResults += $resultObject } else { $resultObject = [PSCustomObject]@{ CheckID = "CheckSignature" CheckName = "Signature check" ResultValue = [Result]::Fail ResultText = Get-ADFSTkLanguageText healthCheckSignatureMissingSignaturesResult -f $missingSignatures.Count ResultData = $missingSignatures ReferenceFile = "" FixID = "" } Write-ADFSTkLog (Get-ADFSTkLanguageText healthCheckSignatureMissingSignaturesMessage -f ($missingSignatures | Out-String)) -EntryType Warning $healthResults += $resultObject } } #Invalid signature(s)... if ($invalidSignatures.Count -gt 0) { $resultObject = [PSCustomObject]@{ CheckID = "CheckSignature" CheckName = Get-ADFSTkLanguageText healthCheckSignatureName ResultValue = [Result]::Fail ResultText = Get-ADFSTkLanguageText healthCheckSignatureInvalidSignaturesMessage -f ($invalidSignatures | Out-String) ResultData = $invalidSignatures ReferenceFile = "" FixID = "" } Write-ADFSTkVerboseLog $resultObject.ResultText -EntryType Warning $healthResults += $resultObject } if ($resultObject.ResultValue -eq [Result]::Pass) { Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckSignaturePass) } else { Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckSignatureFail) } } #endregion #region check config version if ($healtChecks.CheckConfigVersion) { Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckConfigVersionStartMessage) foreach ($cf in $configFiles) { $resultObject = [PSCustomObject]@{ CheckID = "CheckConfigVersion" CheckName = "Version control" ResultValue = [Result]::None ResultText = "" ResultData = @() ReferenceFile = $cf FixID = "" } Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healhCheckConfigVersionVerifyingPath -f $cf) if (Test-Path $cf) { Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healhCheckConfigVersionVerifyingPathSucceeded) Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healhCheckConfigVersionVerifyingXMLParse) try { [xml]$xmlCf = Get-Content $cf Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healhCheckConfigVersionVerifyingXMLParseSucceeded) #Check against compatible version Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckConfigVersionVerifyingVersionStart) Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckConfigVersionVerifyingVersionCompareVersions -f $xmlCf.configuration.ConfigVersion, $Global:ADFSTkCompatibleInstitutionConfigVersion) if ([float]$xmlCf.configuration.ConfigVersion -ge [float]$Global:ADFSTkCompatibleInstitutionConfigVersion) { $resultObject.ResultValue = [Result]::Pass $resultObject.ResultText = Get-ADFSTkLanguageText healthCheckConfigVersionVerifyingVersionSucceeded Write-ADFSTkVerboseLog $resultObject.ResultText } else { $resultObject.ResultValue = [Result]::Fail $resultObject.ResultText = Get-ADFSTkLanguageText healthIncompatibleInstitutionConfigVersion -f $xmlCf.configuration.ConfigVersion, $Global:ADFSTkCompatibleInstitutionConfigVersion $resultObject.ResultData = $xmlCf.configuration.ConfigVersion Write-ADFSTkLog $resultObject.ResultText -EntryType Warning } } catch { $resultObject.ResultValue = [Result]::Fail $resultObject.ResultText = Get-ADFSTkLanguageText healhCheckConfigVersionVerifyingXMLParseFailed -f $cf $resultObject.ResultData = $cf Write-ADFSTkLog $resultObject.ResultText -EntryType Warning } } else { $resultObject.ResultValue = [Result]::Fail $resultObject.ResultText = Get-ADFSTkLanguageText cFileDontExist -f $cf $resultObject.ResultData = $cf Write-ADFSTkLog $resultObject.ResultText -EntryType Warning } $healthResults += $resultObject if ($resultObject.ResultValue -eq [Result]::Pass) { Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckConfigVersionPass) } else { Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthCheckConfigVersionFail) } } } #endregion #region check Access Control Policy if MFA Adapter is installed if ($healtChecks.MFAAccesControlPolicy) { $resultObject = [PSCustomObject]@{ CheckID = "MFAAccesControlPolicy" CheckName = "MFA Access Control Policy" ResultValue = [Result]::None ResultText = "" ResultData = @() ReferenceFile = "" FixID = "" } #Only if MFA Adapter installed! # Check if the ADFSTK MFA Adapter is installed and add rules if so if ([string]::IsNullOrEmpty($Global:ADFSTKRefedsMFAUsernamePasswordAdapterInstalled)) { $Global:ADFSTKRefedsMFAUsernamePasswordAdapterInstalled = ![string]::IsNullOrEmpty((Get-AdfsAuthenticationProvider -Name RefedsMFAUsernamePasswordAdapter -WarningAction Ignore)) } if ($Global:ADFSTKRefedsMFAUsernamePasswordAdapterInstalled) { if ((Get-AdfsAccessControlPolicy -Identifier ADFSToolkitPermitEveryoneAndRequireMFA) -eq $null) { $resultObject.ResultValue = [Result]::Fail $resultObject.ResultText = Get-ADFSTkLanguageText healthMFAAccesControlPolicyInstalledACPMissing $resultObject.FixID = "CreateACP" } else { $resultObject.ResultValue = [Result]::Pass $resultObject.ResultText = Get-ADFSTkLanguageText healthMFAAccesControlPolicyInstalledACPPresent } } else { $resultObject.ResultValue = [Result]::Pass $resultObject.ResultText = Get-ADFSTkLanguageText healthMFAAccesControlPolicyNotInstalled } $healthResults += $resultObject if ($resultObject.ResultValue -eq [Result]::Pass) { Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthMFAAccesControlPolicyPass) } else { Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText healthMFAAccesControlPolicyFail) } } #endregion #region check removedSPsStillInSPHash if ($healtChecks.removedSPsStillInSPHash) { #Automatically remove SP's from SPHash File that's not in the Metadata foreach ($cf in $configFiles) { $resultObject = [PSCustomObject]@{ CheckID = "RemovedSPsStillInSPHash" CheckName = "SP's in SPHash File not in Metadata" ResultValue = [Result]::None ResultText = "" ResultData = @() ReferenceFile = $cf FixID = "" } try { $instConfig = Get-ADFSTkInstitutionConfig -ConfigFile $cf $spHashFile = Join-Path $Global:ADFSTkPaths.cacheDir $instConfig.configuration.SPHashFile $metadataCacheFile = Join-Path $Global:ADFSTkPaths.cacheDir $instConfig.configuration.MetadataCacheFile if (Test-Path $spHashFile) { try { $fromHash = [string[]](Import-Clixml $spHashFile).Keys } catch { #What to do? #Rename it? Delete it? $resultObject.Checkname = "SPHash File corrupt" $resultObject.ResultValue = [Result]::Fail $resultObject.ResultText = (Get-ADFSTkLanguageText healthCheckRemovedSPsStillInSPHashSPHashCorrupt -f $spHashFile) $resultObject.ReferenceFile = $spHashFile $resultObject.FixID = "DeleteSPHashFile" } if ($resultObject.ResultValue -ne [Result]::Fail) { $MetadataXML = Get-ADFSTkMetadata -metadataURL $instConfig.configuration.metadataURL -CachedMetadataFile $metadataCacheFile $RawAllSPs = $MetadataXML.EntitiesDescriptor.EntityDescriptor | ? { $_.SPSSODescriptor -ne $null } $MetadataSPs = $RawAllSPs.EntityID $compare = Compare-ADFSTkObject $MetadataSPs $fromHash -CompareType InSecondSetOnly if ($compare.MembersInCompareSet -gt 0) { $resultObject.ResultValue = [Result]::Warning $resultObject.ResultText = (Get-ADFSTkLanguageText healthCheckRemovedSPsStillInSPHashMissingInMetadata -f $compare.MembersInCompareSet) $resultObject.ReferenceFile = $spHashFile $resultObject.ResultData = $compare.CompareSet $resultObject.FixID = "RemoveSPsFromSPHashFile" } else { $resultObject.ResultValue = [Result]::Pass $resultObject.ResultText = Get-ADFSTkLanguageText healthCheckRemovedSPsStillInSPHashNoSPsMissingInMetadata } } } else { $resultObject.CheckID = "SPHashMissing" $resultObject.CheckName = "SP Hash File existance" $resultObject.ResultValue = [Result]::Warning $resultObject.ResultText = (Get-ADFSTkLanguageText healthCheckRemovedSPsStillInSPHashAllSPsWillBeImported -f $spHashFile) $resultObject.ReferenceFile = $spHashFile } } catch { $resultObject.ResultValue = [Result]::Fail $resultObject.ResultText = $_ } $healthResults += $resultObject } } #endregion #region remove/rerun missing SP's if ($healtChecks.MissingSPsInADFS) { #Automatically remove SP's from SPHash File that's not in the Metadata foreach ($cf in $configFiles) { $resultObject = [PSCustomObject]@{ CheckID = "MissingSPsInADFS" CheckName = "SP's in SPHash File missing in ADFS" ResultValue = [Result]::None ResultText = "" ResultData = @() ReferenceFile = $cf FixID = "" } try { $instConfig = Get-ADFSTkInstitutionConfig -ConfigFile $cf $spHashFile = Join-Path $Global:ADFSTkPaths.cacheDir $instConfig.configuration.SPHashFile $metadataCacheFile = Join-Path $Global:ADFSTkPaths.cacheDir $instConfig.configuration.MetadataCacheFile if (Test-Path $spHashFile) { try { $fromHash = [string[]](Import-Clixml $spHashFile).Keys } catch { #What to do? #Rename it? Delete it? $resultObject.Checkname = "SPHash File corrupt" $resultObject.ResultValue = [Result]::Fail $resultObject.ResultText = (Get-ADFSTkLanguageText healthCheckRemovedSPsStillInSPHashSPHashCorrupt -f $spHashFile) $resultObject.ReferenceFile = $spHashFile $resultObject.FixID = "DeleteSPHashFile" } if ($resultObject.ResultValue -ne [Result]::Fail) { $installed = [string[]](Get-ADFSTkToolEntityId -All | Select -ExpandProperty Identifier) $compare = Compare-ADFSTkObject $installed $fromHash -CompareType InSecondSetOnly if ($compare.MembersInCompareSet -gt 0) { $resultObject.ResultValue = [Result]::Warning $resultObject.ResultText = (Get-ADFSTkLanguageText healthMissingSPsInADFSSPsMissingInADFS -f $compare.MembersInCompareSet) $resultObject.ResultData = $compare.CompareSet $resultObject.ReferenceFile = $spHashFile $resultObject.FixID = "AddMissingSPsInADFS" } else { $resultObject.ResultValue = [Result]::Pass $resultObject.ResultText = Get-ADFSTkLanguageText healthMissingSPsInADFSNoSPsMissingInADFS } } } else { $resultObject.CheckID = "SPHashMissing" $resultObject.CheckName = "SP Hash File existance" $resultObject.ResultValue = [Result]::Warning $resultObject.ResultText = (Get-ADFSTkLanguageText healthCheckRemovedSPsStillInSPHashAllSPsWillBeImported -f $spHashFile) $resultObject.ReferenceFile = $spHashFile } } catch { $resultObject.ResultValue = [Result]::Fail $resultObject.ResultText = $_ } $healthResults += $resultObject } } #endregion #region Check if Scheduled Task is present if ($healtChecks.ScheduledTaskPresent) { #Automatically remove SP's from SPHash File that's not in the Metadata $resultObject = [PSCustomObject]@{ CheckID = "ScheduledTaskPresent" CheckName = "Scheduled Task present" ResultValue = [Result]::None ResultText = "" ResultData = @() ReferenceFile = "" FixID = "" } $schedTask = Get-ScheduledTask -TaskName 'Import Federated Metadata with ADFSToolkit' -TaskPath "\ADFSToolkit\" -ErrorAction SilentlyContinue if (![string]::IsNullOrEmpty($schedTask)) { $resultObject.ResultValue = [Result]::Pass $resultObject.ResultText = Get-ADFSTkLanguageText healthScheduledTaskPresentScheduledTaskPresent } else { $resultObject.ResultValue = [Result]::Warning $resultObject.ResultText = Get-ADFSTkLanguageText healthScheduledTaskPresentScheduledTaskNotPresent $resultObject.FixID = "RegisterScheduledTask" } $healthResults += $resultObject } #endregion #region Show result if (!$Silent) { $healthResults | Select CheckName, ResultValue, ResultText, ReferenceFile | sort ResultValue, CheckName, ReferenceFile | ft -AutoSize -Wrap } #endregion #region Correct fixable errors $FixedAnything = $false #region MFAAccesControlPolicy #createACP $MFAAccesControlPolicy = $healthResults | ? FixID -eq "CreateACP" if (![String]::IsNullOrEmpty($MFAAccesControlPolicy)) { if ($Silent -or (Get-ADFSTkAnswer (Get-ADFSTkLanguageText healthMFAAccesControlPolicyRegisterACP))) { New-ADFSTKAccessControlPolicy Write-ADFSTkLog "Access Control Policy 'ADFSTk:Permit everyone and force MFA' created." $MFAAccesControlPolicy.ResultValue = [Result]::Pass $MFAAccesControlPolicy.ResultText = (Get-ADFSTkLanguageText healthFixed) + $resultObject.ResultText $FixedAnything = $true } } #endregion #region RemovedSPsStillInSPHash #Do we have SP's in SP Hash file that are missing in the Metadata? They should be removed... $removedSPsStillInSPHash = $healthResults | ? FixID -eq "RemoveSPsFromSPHashFile" if (![String]::IsNullOrEmpty($removedSPsStillInSPHash)) { foreach ($resultObject in $removedSPsStillInSPHash) { if ($Silent -or (Get-ADFSTkAnswer (Get-ADFSTkLanguageText healthRemoveSPsFromSPHashFileRemoveSPsNotInMetadata -f $resultObject.ReferenceFile ))) { Remove-ADFSTkEntityHash -SPHashFile $resultObject.ReferenceFile -EntityIDs $resultObject.ResultData $resultObject.ResultText = (Get-ADFSTkLanguageText healthFixed) + $resultObject.ResultText $resultObject.ResultValue = [Result]::Pass $FixedAnything = $true } else { #No need to fail, carry on! } } } #Do we have corrupt SP Hash files? $removedSPsStillInSPHash = $healthResults | ? FixID -eq 'DeleteSPHashFile' if (![String]::IsNullOrEmpty($removedSPsStillInSPHash)) { foreach ($resultObject in $removedSPsStillInSPHash) { if ($Silent -or (Get-ADFSTkAnswer (Get-ADFSTkLanguageText healthDeleteSPHashFileDeleteCorruptSPHashFile -f $resultObject.ReferenceFile ))) { Remove-Item -Path $resultObject.ReferenceFile $resultObject.ResultText = (Get-ADFSTkLanguageText healthFixed) + $resultObject.ResultText $resultObject.ResultValue = [Result]::Pass $FixedAnything = $true } else { #The fail result will stand! } } } #endregion #region RemovedSPsStillInSPHash #Do we have SP's in SP Hash file that are missing in ADFS? They should be removed from SP Hash File... $addMissingSPs = $healthResults | ? FixID -eq "AddMissingSPsInADFS" if (![String]::IsNullOrEmpty($addMissingSPs)) { foreach ($resultObject in $addMissingSPs) { if ($Silent -or (Get-ADFSTkAnswer (Get-ADFSTkLanguageText healthAddMissingSPsInADFSRemoveMissingSPs -f $resultObject.ReferenceFile ))) { Remove-ADFSTkEntityHash -SPHashFile $resultObject.ReferenceFile -EntityIDs $resultObject.ResultData $resultObject.ResultText = (Get-ADFSTkLanguageText healthFixed) + $resultObject.ResultText $resultObject.ResultValue = [Result]::Pass $FixedAnything = $true } else { #No need to fail, carry on! } } } #endregion #region Add Scheduled Task #Only if run manually if (!$Silent) { $addMissingSPs = $healthResults | ? FixID -eq "RegisterScheduledTask" if (![String]::IsNullOrEmpty($addMissingSPs)) { if ((Get-ADFSTkAnswer (Get-ADFSTkLanguageText healthRegisterScheduledTaskCreateSchedTask))) { Register-ADFSTkScheduledTask $resultObject.ResultText = (Get-ADFSTkLanguageText healthFixed) + $resultObject.ResultText $resultObject.ResultValue = [Result]::Pass $FixedAnything = $true } } } #endregion if ($FixedAnything -and -not $Silent) { $healthResults | Select CheckName, ResultValue, ResultText, ReferenceFile | sort ResultValue, CheckName, ReferenceFile | ft -AutoSize -Wrap } if ($Silent) { return !($healthResults.ResultValue.Contains([Result]::Fail)) } else { if ($healthResults.ResultValue.Contains([Result]::Fail)) { Write-ADFSTkLog -Message (Get-ADFSTkLanguageText healthFailed) -EntryType Error } elseif ($healthResults.ResultValue.Contains([Result]::Warning)) { Write-ADFSTkLog -Message (Get-ADFSTkLanguageText healthPassedWithWarnings) -EntryType Warning } else { Write-ADFSTkLog -Message (Get-ADFSTkLanguageText healthPassed) -EntryType Information -ForegroundColor Green } } <# .SYNOPSIS Use this cmdlet to check the health of your installation of ADFS Toolkit .DESCRIPTION This cmdlet can check different things on the installation of ADFS Toolkit. Every time Sync-ADFSTkAggregates are run a default helath check is run. If an error is found that the health check can fix you will be asked if you want to do so. If the cmdlet is run with -Silent it will automatically fix errors. .EXAMPLE PS C:\> Get-ADFSTkHealth Does a default check which includes checking that all files in the module are signed and has not been altered, that the institution configuration files has the correct version for the installed version of ADFS Toolkit and that the MFA Access Control Policy exists (if the Refeds MFA adapter is installed). .EXAMPLE PS C:\> Get-ADFSTkHealth -HealthCheckMode CriticalOnly This excludes the check of signatures. Run this if you have changed any files in the module. .EXAMPLE PS C:\> Get-ADFSTkHealth -HealthCheckMode Full Run this after upgrade of the ADFS Toolkit. It does a full scan of the installation including tests of missing SP's. #> } # SIG # Begin signature block # MIId/gYJKoZIhvcNAQcCoIId7zCCHesCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCuYyXwGWkSjAOg # dRxwJCpK8YTi3YEQ35JA0/EMw8/0eaCCGKwwggR9MIIDZaADAgECAgMb5xUwDQYJ # KoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRk # eSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3MgMiBDZXJ0aWZp # Y2F0aW9uIEF1dGhvcml0eTAeFw0xNDAxMDEwNzAwMDBaFw0zMTA1MzAwNzAwMDBa # MIGDMQswCQYDVQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2Nv # dHRzZGFsZTEaMBgGA1UEChMRR29EYWRkeS5jb20sIEluYy4xMTAvBgNVBAMTKEdv # IERhZGR5IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqG # SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/cWII8fpZNPcbyRij94BJWOkigxOmxSBD # ATuE8eaFSZ8n6vaEG06gtNtwmMcyAbEFPgdO7vT6Ty9ZMCLnqxlWa+KAB/zzFnWA # OVF75fk1tnROqY2CE+S2P6kDg/qivooVan/eC8O2GRQFyurDqASUO0Z8Mg3zAGYi # yI1pbTaMERi307IcYLQ4+gKMztPdRgfeCj7rXXzIfPuwK1OkkmJpUSUFYRpEgYws # qUOWI9+sOoGaDinFHKnpXR62np4wCjnO8YiA+0tdzDLshWJDJTQCVicBkbQ7cCo/ # brHonIgBfZ/U+dtTbWCdvyznWKu4X0b8zsQbAzwJ60kxXGlGs+BHAgMBAAGjggEX # MIIBEzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU # OpqFBxBnKLbv9r0FQW4gwZTaD94wHwYDVR0jBBgwFoAU0sSw0pHUTBFxs2HLPaH+ # 3ahq1OMwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5n # b2RhZGR5LmNvbS8wMgYDVR0fBCswKTAnoCWgI4YhaHR0cDovL2NybC5nb2RhZGR5 # LmNvbS9nZHJvb3QuY3JsMEYGA1UdIAQ/MD0wOwYEVR0gADAzMDEGCCsGAQUFBwIB # FiVodHRwczovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMA0GCSqGSIb3 # DQEBCwUAA4IBAQBZC1O9koYRpyR77Vsxzx0fbHDFuG6+Trv2vpdQ4TB/uihcYpTC # 434z9/tCdoXblRyMIlh1CQyIZWc5ChYJxaA4l6TFI5M/tBimAQZEkeOnaSe0WiV/ # Orcyzd2E/yo4KTOk3Weyhf6hiCAcUInI3Cr2QgM3TOaI39WvJPKxw9/MtezgmV63 # SVQgPJQYDMccUhhJpG3hs1gLydjs2a4cMo4ocA3i/qYXnoQPvVdws1rpH6CGU7vv # fP9pC+BIw7eTC8gKVMSsXRRnN2zKpS8xCDeqbm+MvJviV10kga+Xl5yErWysN0xm # 82GRESDkvjCfeqQpCbDhNF9kdxhAUd+MMKavMIIE0DCCA7igAwIBAgIBBzANBgkq # hkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0FyaXpvbmExEzAR # BgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMuMTEw # LwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcy # MB4XDTExMDUwMzA3MDAwMFoXDTMxMDUwMzA3MDAwMFowgbQxCzAJBgNVBAYTAlVT # MRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQK # ExFHb0RhZGR5LmNvbSwgSW5jLjEtMCsGA1UECxMkaHR0cDovL2NlcnRzLmdvZGFk # ZHkuY29tL3JlcG9zaXRvcnkvMTMwMQYDVQQDEypHbyBEYWRkeSBTZWN1cmUgQ2Vy # dGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw # ggEKAoIBAQC54MsQ1K92vdSTYuswZLiBCGzDBNliF44v/z5lz4/OYuY8UhzaFkVL # Vat4a2ODYpDOD2lsmcgaFItMzEUz6ojcnqOvK/6AYZ15V8TPLvQ/MDxdR/yaFrzD # N5ZBUY4RS1T4KL7QjL7wMDge87Am+GZHY23ecSZHjzhHU9FGHbTj3ADqRay9vHHZ # qm8A29vNMDp5T19MR/gd71vCxJ1gO7GyQ5HYpDNO6rPWJ0+tJYqlxvTV0KaudAVk # V4i1RFXULSo6Pvi4vekyCgKUZMQWOlDxSq7neTOvDCAHf+jfBDnCaQJsY1L6d8Eb # yHSHyLmTGFBUNUtpTrw700kuH9zB0lL7AgMBAAGjggEaMIIBFjAPBgNVHRMBAf8E # BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUQMK9J47MNIMwojPX+2yz # 8LQsgM4wHwYDVR0jBBgwFoAUOpqFBxBnKLbv9r0FQW4gwZTaD94wNAYIKwYBBQUH # AQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5nb2RhZGR5LmNvbS8wNQYD # VR0fBC4wLDAqoCigJoYkaHR0cDovL2NybC5nb2RhZGR5LmNvbS9nZHJvb3QtZzIu # Y3JsMEYGA1UdIAQ/MD0wOwYEVR0gADAzMDEGCCsGAQUFBwIBFiVodHRwczovL2Nl # cnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMA0GCSqGSIb3DQEBCwUAA4IBAQAI # fmyTEMg4uJapkEv/oV9PBO9sPpyIBslQj6Zz91cxG7685C/b+LrTW+C05+Z5Yg4M # otdqY3MxtfWoSKQ7CC2iXZDXtHwlTxFWMMS2RJ17LJ3lXubvDGGqv+QqG+6EnriD # fcFDzkSnE3ANkR/0yBOtg2DZ2HKocyQetawiDsoXiWJYRBuriSUBAA/NxBti21G0 # 0w9RKpv0vHP8ds42pM3Z2Czqrpv1KrKQ0U11GIo/ikGQI31bS/6kA1ibRrLDYGCD # +H1QQc7CoZDDu+8CL9IVVO5EFdkKrqeKM+2xLXY2JtwE65/3YR8V3Idv7kaWKK2h # Jn0KCacuBKONvPi8BDABMIIE/jCCA+agAwIBAgIQDUJK4L46iP9gQCHOFADw3TAN # BgkqhkiG9w0BAQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQg # SW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2Vy # dCBTSEEyIEFzc3VyZWQgSUQgVGltZXN0YW1waW5nIENBMB4XDTIxMDEwMTAwMDAw # MFoXDTMxMDEwNjAwMDAwMFowSDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD # ZXJ0LCBJbmMuMSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMTCCASIw # DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMLmYYRnxYr1DQikRcpja1HXOhFC # vQp1dU2UtAxQtSYQ/h3Ib5FrDJbnGlxI70Tlv5thzRWRYlq4/2cLnGP9NmqB+in4 # 3Stwhd4CGPN4bbx9+cdtCT2+anaH6Yq9+IRdHnbJ5MZ2djpT0dHTWjaPxqPhLxs6 # t2HWc+xObTOKfF1FLUuxUOZBOjdWhtyTI433UCXoZObd048vV7WHIOsOjizVI9r0 # TXhG4wODMSlKXAwxikqMiMX3MFr5FK8VX2xDSQn9JiNT9o1j6BqrW7EdMMKbaYK0 # 2/xWVLwfoYervnpbCiAvSwnJlaeNsvrWY4tOpXIc7p96AXP4Gdb+DUmEvQECAwEA # AaOCAbgwggG0MA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB # /wQMMAoGCCsGAQUFBwMIMEEGA1UdIAQ6MDgwNgYJYIZIAYb9bAcBMCkwJwYIKwYB # BQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAfBgNVHSMEGDAWgBT0 # tuEgHf4prtLkYaWyoiWyyBc1bjAdBgNVHQ4EFgQUNkSGjqS6sGa+vCgtHUQ23eNq # erwwcQYDVR0fBGowaDAyoDCgLoYsaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3No # YTItYXNzdXJlZC10cy5jcmwwMqAwoC6GLGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNv # bS9zaGEyLWFzc3VyZWQtdHMuY3JsMIGFBggrBgEFBQcBAQR5MHcwJAYIKwYBBQUH # MAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBPBggrBgEFBQcwAoZDaHR0cDov # L2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3VyZWRJRFRpbWVz # dGFtcGluZ0NBLmNydDANBgkqhkiG9w0BAQsFAAOCAQEASBzctemaI7znGucgDo5n # Rv1CclF0CiNHo6uS0iXEcFm+FKDlJ4GlTRQVGQd58NEEw4bZO73+RAJmTe1ppA/2 # uHDPYuj1UUp4eTZ6J7fz51Kfk6ftQ55757TdQSKJ+4eiRgNO/PT+t2R3Y18jUmmD # gvoaU+2QzI2hF3MN9PNlOXBL85zWenvaDLw9MtAby/Vh/HUIAHa8gQ74wOFcz8QR # cucbZEnYIpp1FUL1LTI4gdr0YKK6tFL7XOBhJCVPst/JKahzQ1HavWPWH1ub9y4b # TxMd90oNcX6Xt/Q/hOvB46NJofrOp79Wz7pZdmGJX36ntI5nePk2mOHLKNpbh6aK # LzCCBRwwggQEoAMCAQICCGXB0JJJvDvXMA0GCSqGSIb3DQEBCwUAMIG0MQswCQYD # VQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEa # MBgGA1UEChMRR29EYWRkeS5jb20sIEluYy4xLTArBgNVBAsTJGh0dHA6Ly9jZXJ0 # cy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5LzEzMDEGA1UEAxMqR28gRGFkZHkgU2Vj # dXJlIENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTIxMDExOTE4MzczNloX # DTIyMDMwODE4NTgwMFowXjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x # DzANBgNVBAcTBk90dGF3YTEVMBMGA1UEChMMQ0FOQVJJRSBJbmMuMRUwEwYDVQQD # EwxDQU5BUklFIEluYy4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ # hfCjFqiTmN1uLoySixnwaOjf/ZAL9P6SvjlCaBA2mutoorEgnzUP8HnOIcvMRgEM # PmpaZ8egM93Bmx9d41xoarsQpCN3DhYOo+b3fWnPucVtpxbul2OFePv63mw/uvr+ # dqkv4b/f3Tg+ilQbpsNonbvh9MKEFv8Pn9koj0ySV+qxz34PxTVAe6g//pel3/3i # 9fqilCnIEcx4zg/+NKBeOWROSs4oXo3IvBjVrunmz+YuieSr78TqIE6hD8JF2q1w # KwfMB3+x7dEXZAus9WtIU/qITATtEfO9QAgrrYL4F1MLN+osSp8my5eCOjnLTQc4 # 7q574V3zQhsIHW7yBXLdAgMBAAGjggGFMIIBgTAMBgNVHRMBAf8EAjAAMBMGA1Ud # JQQMMAoGCCsGAQUFBwMDMA4GA1UdDwEB/wQEAwIHgDA1BgNVHR8ELjAsMCqgKKAm # hiRodHRwOi8vY3JsLmdvZGFkZHkuY29tL2dkaWcyczUtNi5jcmwwXQYDVR0gBFYw # VDBIBgtghkgBhv1tAQcXAjA5MDcGCCsGAQUFBwIBFitodHRwOi8vY2VydGlmaWNh # dGVzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMAgGBmeBDAEEATB2BggrBgEFBQcB # AQRqMGgwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmdvZGFkZHkuY29tLzBABggr # BgEFBQcwAoY0aHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNvbS9yZXBvc2l0 # b3J5L2dkaWcyLmNydDAfBgNVHSMEGDAWgBRAwr0njsw0gzCiM9f7bLPwtCyAzjAd # BgNVHQ4EFgQUUPnMg2nmYS8l7rmax3weVkrgz5AwDQYJKoZIhvcNAQELBQADggEB # AGabJLu09gdYHt7ZMbpJ4048ZIiXwVLE/HNcnApTghNaHnSSiMI2xTsmbrM/lYsm # pwFuws1c2fMBvyDRgkzR/4+RIjoQJpLrHy1QABYlWAIKMqdFmfqty0QApgIkGN2+ # scMxKMWJGND8qp3KM+5C8TNTsO0gPVfdaarX2TmLM6yIQcgxD8YZMd0mqdR7rcCe # bgMeAdHLYPQu/HM0Cj3qtzFx/CZzz93CAlh8Dx5woqeNJixQMLK28MhU8y6NSN5o # KnD/8EESudRzXyoowZ2N4YJzyye5UL9pxhniDKs444w1r5XcjQYDo11G8Y4up4XW # 1cFtLNulHYcKhAnQ7XHswxMwggUxMIIEGaADAgECAhAKoSXW1jIbfkHkBdo2l8IV # MA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lD # ZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xNjAxMDcxMjAwMDBaFw0zMTAxMDcx # MjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAX # BgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIg # QXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB # DwAwggEKAoIBAQC90DLuS82Pf92puoKZxTlUKFe2I0rEDgdFM1EQfdD5fU1ofue2 # oPSNs4jkl79jIZCYvxO8V9PD4X4I1moUADj3Lh477sym9jJZ/l9lP+Cb6+NGRwYa # VX4LJ37AovWg4N4iPw7/fpX786O6Ij4YrBHk8JkDbTuFfAnT7l3ImgtU46gJcWvg # zyIQD3XPcXJOCq3fQDpct1HhoXkUxk0kIzBdvOw8YGqsLwfM/fDqR9mIUF79Zm5W # YScpiYRR5oLnRlD9lCosp+R1PrqYD4R/nzEU1q3V8mTLex4F0IQZchfxFwbvPc3W # Te8GQv2iUypPhR3EHTyvz9qsEPXdrKzpVv+TAgMBAAGjggHOMIIByjAdBgNVHQ4E # FgQU9LbhIB3+Ka7S5GGlsqIlssgXNW4wHwYDVR0jBBgwFoAUReuir/SSy4IxLVGL # p6chnfNtyA8wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwEwYD # VR0lBAwwCgYIKwYBBQUHAwgweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhho # dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNl # cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEG # A1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy # dEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0 # LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwUAYDVR0gBEkwRzA4Bgpg # hkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNv # bS9DUFMwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4IBAQBxlRLpUYdWac3v # 3dp8qmN6s3jPBjdAhO9LhL/KzwMC/cWnww4gQiyvd/MrHwwhWiq3BTQdaq6Z+Cei # Zr8JqmDfdqQ6kw/4stHYfBli6F6CJR7Euhx7LCHi1lssFDVDBGiy23UC4HLHmNY8 # ZOUfSBAYX4k4YU1iRiSHY4yRUiyvKYnleB/WCxSlgNcSR3CzddWThZN+tpJn+1Nh # iaj1a5bA9FhpDXzIAbG5KHW3mWOFIoxhynmUfln8jA/jb7UBJrZspe6HUSHkWGCb # ugwtK22ixH67xCUrRwIIfEmuE7bhfEJCKMYYVs9BNLZmXbZ0e/VWMyIvIjayS6JK # ldj1po5SMYIEqDCCBKQCAQEwgcEwgbQxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdB # cml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNv # bSwgSW5jLjEtMCsGA1UECxMkaHR0cDovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9z # aXRvcnkvMTMwMQYDVQQDEypHbyBEYWRkeSBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0 # aG9yaXR5IC0gRzICCGXB0JJJvDvXMA0GCWCGSAFlAwQCAQUAoIGEMBgGCisGAQQB # gjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYK # KwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIJYxqL3w # yWb1Qm686rmVM9EIxzdnGjXZ2tzz1OPWLTojMA0GCSqGSIb3DQEBAQUABIIBAKe6 # YgrSR//PFG6d395uo1Gb1M1cVZ1ufyfyqVZLPtPup1f7HViUs+X7pjy3EQ/2DtdR # naK0N3Nk3Rtl/GUBnG+j7bXek+bAPcjqGH/cZNl410739KDaKjUok8lFYRws79pX # qOeYSCemt7H13JiOUkNQOR53ujLAIyE+jDd7CxtnkrOXzE5Ul588eoe0IcpMSxio # XtDlw5RPmTVcLiwxrfKlooJLkhOnlKYYunQidZpbONUDNruz303A2/OkmDi958+S # Dyi78LYgM9dzpHGA086qnZL5O0ryd9ahhshqEQpZhdHQbYLvujR7QBlaVoGNAfL6 # bC2Ez723HMUtpuYcSxOhggIwMIICLAYJKoZIhvcNAQkGMYICHTCCAhkCAQEwgYYw # cjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ # d3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVk # IElEIFRpbWVzdGFtcGluZyBDQQIQDUJK4L46iP9gQCHOFADw3TANBglghkgBZQME # AgEFAKBpMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8X # DTIyMDIwMTIyMTEwN1owLwYJKoZIhvcNAQkEMSIEIEHYKVPV9zBF6UEKzoqNC6q+ # xdIDLYcWTPLOK2XzChkqMA0GCSqGSIb3DQEBAQUABIIBAEo1d0i1+QFi5zaLfZxc # VVefx+uxsgqxJb4OJMNYgQ7S7iRHZKvgr2J0EZauBinlg/dYEEjW3NsgLR/uSlxq # +go9XVvcrqv/hnWP0M2amAJkMiw3hNkGUpOmuOFtr5PKkViNvC1E5YF820M6Ksl5 # xMBooH88j/PLxy6dX2GEuzgDke+Mxf7WJHgMfVXF2FmXv69x/Yj66hxUn4WYwfwy # 9/2887JiYY6EFHonFiEao/y3Yb43zG4lcPSqaq4c2DPNiuHpUxkkPxX/dYR5/nKM # uX1NuQ8HqQSPzLjTD9N1ON9IQK7/OsIjl2SFu9kxMTzWKQ+zWSMR+1cuABZtVggq # dDk= # SIG # End signature block |