ADLAB PowerShell source file: lib-scom.ps1

(C) Ondrej Sevecek, 2019 - www.sevecek.com, ondrej@sevecek.com



# ============================================================
# Note: Make sure that the lib-common has been loaded       ||
#                                                           ||

if (($global:adlabVersion -lt 1) -or (-not $global:libCommonScriptInitialized)) {

  $msgLibCommonNotInitialized = 'ADLAB: lib-common not loaded or initialized. Exiting'
  Write-Host $msgLibCommonNotInitialized -ForegroundColor Red
  throw $msgLibCommonNotInitialized
  exit 1
}

$adlabVersionThisLib = 13
$adlabReleaseDateThisLib = [DateTime]::Parse('2016-03-17')
DBG ('Library loaded: {0} | v{1} | {2:yyyy-MM-dd}' -f (Split-Path -leaf $MyInvocation.MyCommand.Definition), $adlabVersionThisLib, $adlabReleaseDateThisLib)

#                                                           ||
# Note: Make sure that the lib-common has been loaded       ||
# ============================================================

$global:rxTextGUID = "\A\{$global:rxGUID\}\Z"



function global:Decode-Base64 ([string] $base64string)
{
  [string] $result = ''

  DBGSTART
  $result = [Text.Encoding]::ASCII.GetString([Convert]::FromBase64String($base64string))
  DBGER $MyInvocation.MyCommand.Name $Error
  DBGEND

  return $result
}


function global:Prepare-DiscoScript (
    [object] $parentPSBoundParameters,
    [string] $thisScriptName,
    [string] $thisScriptFileName,
    [psobject[]] $discoKeys,
    [string] $sourceId,
    [string] $targetId,
    [string] $computerName
    )
{
  DBG ('{0}: Parameters: {1}' -f $MyInvocation.MyCommand.Name, (($PSBoundParameters.Keys | ? { $_ -ne 'object' } | % { '{0}={1}' -f $_, $PSBoundParameters[$_]}) -join $multivalueSeparator))

  [System.__ComObject] $scomAPI = $null
  [System.__ComObject] $discoData = $null


  DBG ('Disco script: {0} | {1}' -f $thisScriptName, $thisScriptFileName)
  DBG ('Script parameters: {0}' -f (($parentPSBoundParameters.Keys | ? { $_ -ne 'object' } | % { '{0}={1}' -f $_, $parentPSBoundParameters[$_]}) -join $multivalueSeparator))

  DBG ('Instance count: {0}' -f $discoKeys.Count)
  
  foreach ($oneDiscoKeys in $discoKeys) {

    DBG ('Instance type: {0} | {1}' -f $oneDiscoKeys.Keys[0], (Decode-Base64 $oneDiscoKeys.Codes[0]))
    DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $oneDiscoKeys.Keys[0] }
    DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $oneDiscoKeys.Codes[0] }

    $oneDiscoKeys.Keys.Keys | sort | % { 

      DBG ('Disco key: {0,2} | {1} | {2}' -f $_, $oneDiscoKeys.Keys[$_], (Decode-Base64 $oneDiscoKeys.Codes[$_]))
      DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $oneDiscoKeys.Keys[$_] }
      DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $oneDiscoKeys.Codes[$_] }
      DBGIF $MyInvocation.MyCommand.Name { $oneDiscoKeys.Keys[$_] -notmatch $global:rxTextGUID }
    }
  }

  DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $sourceId }
  DBGIF $MyInvocation.MyCommand.Name { $sourceId -notmatch $rxTextGUID }
  DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $targetId }
  DBGIF $MyInvocation.MyCommand.Name { $targetId -notmatch $rxTextGUID }
  DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $computerName }
  DBGIF $MyInvocation.MyCommand.Name { $computerName -notmatch $rxDNS }

  if ((Is-ValidString $sourceId) -and (Is-ValidString $targetId) -and (Is-ValidString $computerName)) {

    DBG ('Open the MOM.ScriptAPI library')
    DBGSTART
    $scomAPI = New-Object -ComObject MOM.ScriptAPI -EA SilentlyContinue
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND
    DBGIF $MyInvocation.MyCommand.Name { Is-Null $scomAPI }

    if (Is-NonNull $scomAPI) {

      DBG ('Create the disco data object')
      DBGSTART
      $discoData = $scomAPI.CreateDiscoveryData(0, $sourceId, $targetId)
      DBGER $MyInvocation.MyCommand.Name $error
      DBGEND
      DBGIF $MyInvocation.MyCommand.Name { Is-Null $discoData }
    }
  }

  return $discoData
}


function global:Add-DiscoDataInstance ([System.__ComObject] $discoData, [hashtable] $discoKeys, [hashtable] $discoValues, [hashtable] $discoKeysCD)
{
  DBG ('{0}: Parameters: {1}' -f $MyInvocation.MyCommand.Name, (($PSBoundParameters.Keys | ? { $_ -ne 'object' } | % { '{0}={1}' -f $_, $PSBoundParameters[$_]}) -join $multivalueSeparator))

  DBGIF $MyInvocation.MyCommand.Name { Is-Null $discoData }
  DBGIF $MyInvocation.MyCommand.Name { Is-Null $discoKeys[0] } # instance type
  DBGIF $MyInvocation.MyCommand.Name { Is-Null $discoKeys[1] } # displayName
  DBGIF $MyInvocation.MyCommand.Name { Is-Null $discoValues[1] }
  DBGIF $MyInvocation.MyCommand.Name { (Get-CountSafe $discoKeys.Keys) -ne ((Get-CountSafe $discoValues.Keys) + 1) }
  DBGIF $MyInvocation.MyCommand.Name { Contains-Safe ([Collections.ArrayList] $discoValues.Keys) 0 } # no value in instance type
  DBGIF $MyInvocation.MyCommand.Name { (Get-CountSafe $discoKeys.Keys) -lt 2 }

  if (Is-NonNull $discoData) {

    DBG ('Create and fill a new class instance: {0} | {1} | {2}' -f $discoKeys[0], (Decode-Base64 $discoKeysCD[0]), $discoValues[1])
    
    [System.__ComObject] $newInstance = $null
    DBGSTART
    $newInstance = $discoData.CreateClassInstance($discoKeys[0])
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND
    DBGIF $MyInvocation.MyCommand.Name { Is-Null $newInstance }

    if (Is-NonNull $newInstance) {

      DBG ('Fill the new instance: keys = {0} | values = {1}' -f (Get-CountSafe $discoKeys.Keys), (Get-CountSafe $discoValues.Keys))

      foreach ($oneDiscoKey in ($discoKeys.Keys | ? { $_ -ne 0 } | sort)) {

        DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $discoKeys[$oneDiscoKey] }
        DBGIF $MyInvocation.MyCommand.Name { $discoKeys[$oneDiscoKey] -notmatch "\A\{$global:rxGUID\}\Z" }
        DBGIF $MyInvocation.MyCommand.Name { -not (Contains-Safe ([Collections.ArrayList] $discoValues.Keys) $oneDiscoKey) }

        $discoKeyName = ''
        if (Is-NonNull $discoKeysCD) {

          $oneDiscoKeyName = Decode-Base64 $discoKeysCD[$oneDiscoKey]
        }
        
        DBG ('One disco property to add: {0} | {1} | {2} = {3}' -f $oneDiscoKey, $discoKeys[$oneDiscoKey], $oneDiscoKeyName, $discoValues[$oneDiscoKey])
        DBGSTART
        $newInstance.AddProperty($discoKeys[$oneDiscoKey], $discoValues[$oneDiscoKey])
        DBGER $MyInvocation.MyCommand.Name $error
        DBGEND
      }

      DBG ('Add the instance into disco data')
      DBGSTART
      $discoData.AddInstance($newInstance)
      DBGER $MyInvocation.MyCommand.Name $error
      DBGEND
    }
  }
}


function global:ToString-SCOMInstance ([object] $instance, [string[]] $members)
{
  DBGIF $MyInvocation.MyCommand.Name { Is-Null $instance }

  [string] $outString = ''
  [Collections.ArrayList] $tokens = @()

  [void] $tokens.Add(('display = {0}' -f $instance.'[System.Entity].DisplayName'.Value))
  [void] $tokens.Add(('comp = {0}' -f $instance.'[Microsoft.Windows.Computer].PrincipalName'.Value))
  [void] $tokens.Add(('changed = {0:s}' -f $instance.LastModified))
  [void] $tokens.Add(('type = {0}' -f $instance.GetType().Name))
  [void] $tokens.Add(('name = {0}' -f $instance.Name))
  [void] $tokens.Add(('id = {0}' -f $instance.Id))

  foreach ($oneMember in $members) {

    DBGIF ('Weird member format: {0}' -f $oneMember) { $oneMember -notmatch '\[(?:\w+\.)+(?:\w+)\]\.\w+' }

    $mbrField = $oneMember.SubString(($oneMember.LastIndexOf('.') + 1))
    $mbrTypeFull = $oneMember.SubString(1, (($oneMember.LastIndexOf(']')) - 1))
    $mbrType = $mbrTypeFull.SubString(($mbrTypeFull.LastIndexOf('.') + 1))
    [void] $tokens.Add(('{0}.{1} = {2}' -f $mbrType, $mbrField, $instance."$oneMember".Value))
  }

  $outString = $tokens -join ' | '

  return $outString
}


function global:Prepare-MonitorScript (
    [object] $parentPSBoundParameters,
    [string] $thisScriptName,
    [string] $thisScriptFileName
    )
{
  DBG ('{0}: Parameters: {1}' -f $MyInvocation.MyCommand.Name, (($PSBoundParameters.Keys | ? { $_ -ne 'object' } | % { '{0}={1}' -f $_, $PSBoundParameters[$_]}) -join $multivalueSeparator))

  [System.__ComObject] $scomAPI = $null
  [System.__ComObject] $propertyBag = $null


  DBG ('Monitor script: {0} | {1}' -f $thisScriptName, $thisScriptFileName)
  DBG ('Script parameters: {0}' -f (($parentPSBoundParameters.Keys | ? { $_ -ne 'object' } | % { '{0}={1}' -f $_, $parentPSBoundParameters[$_]}) -join $multivalueSeparator))

  DBG ('Open the MOM.ScriptAPI library')
  DBGSTART
  $scomAPI = New-Object -ComObject MOM.ScriptAPI -EA SilentlyContinue
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND
  DBGIF $MyInvocation.MyCommand.Name { Is-Null $scomAPI }

  if (Is-NonNull $scomAPI) {

    DBG ('Create the monitor property bag object')
    DBGSTART
    $propertyBag = $scomAPI.CreatePropertyBag()
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND
    DBGIF $MyInvocation.MyCommand.Name { Is-Null $propertyBag }
  }

  return $propertyBag
}


function global:Add-PropertyBagValues ([System.__ComObject] $propertyBag, [hashtable] $propertyBagValues)
{
  DBG ('{0}: Parameters: {1}' -f $MyInvocation.MyCommand.Name, (($PSBoundParameters.Keys | ? { $_ -ne 'object' } | % { '{0}={1}' -f $_, $PSBoundParameters[$_]}) -join $multivalueSeparator))

  DBG ('Will add properties: {0} | {1}' -f $propertyBagValues.Keys.Count, (($propertyBagValues.Keys | % { '{0} = {1}' -f $_, $propertyBagValues[$_] }) -join ', '))

  foreach ($onePropertyBagValue in $propertyBagValues.Keys) {

    DBG ('One property value to be added to the bag: {0} | {1}' -f $onePropertyBagValue, $propertyBagValues[$onePropertyBagValue])
    DBGSTART
    [void] $propertyBag.AddValue($onePropertyBagValue, $propertyBagValues[$onePropertyBagValue])
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND
  }
}


function global:Wait-SCOMManagementServer ()
{
  DBG ('{0}: Parameters: {1}' -f $MyInvocation.MyCommand.Name, (($PSBoundParameters.Keys | ? { $_ -ne 'object' } | % { '{0}={1}' -f $_, $PSBoundParameters[$_]}) -join $multivalueSeparator))

  [Microsoft.EnterpriseManagement.Administration.ManagementServer] $scomMgmtSrv = $null

  $trialCount = 77
  $trialCounter = $trialCount
  do {

    if ($trialCounter -ne $trialCount) {

      if ($trialCounter -eq 57) {

        DBG ('Not even 57 rounds helped waiting for SCOM server, try restarting HealthService: {0}' -f (Get-Service HealthService).Status)
        DBGSTART
        Restart-Service HealthService -Force
        DBGER $MyInvocation.MyCommand.Name $error
        DBGEND
      }

      DBG ('Sleeping 6sec to allow the management server init itself')
      Start-Sleep 6
    }

    DBG ('Open the local SCOM management server and assert some points')
    DBGSTART
    $scomMgmtSrv = Get-SCOMManagementServer -Name $global:thisComputerFQDN
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND

    DBG ('Management server opened: {0} | {1} | {2} | {3} | {4}' -f $scomMgmtSrv.DisplayName, $scomMgmtSrv.HealthState, $scomMgmtSrv.Version, $scomMgmtSrv.ActionAccountIdentity, $scomMgmtSrv.ManagementGroup)
    $trialCounter --
      
  } while ((Is-NonNull $scomMgmtSrv) -and ($trialCounter -gt 0) -and ((Is-EmptyString $scomMgmtSrv.Version) -or ($scomMgmtSrv.HealthState -eq 'Uninitialized') -or (Is-EmptyString $scomMgmtSrv.ActionAccountIdentity)))

  DBGIF $MyInvocation.MyCommand.Name { $trialCounter -lt 1 }
  DBGIF $MyInvocation.MyCommand.Name { Is-Null $scomMgmtSrv }

  return $scomMgmtSrv
}


function global:Wait-SCOMAgent ([string] $agentFQDN, [Management.Automation.PSCredential] $credentials)
{
  DBG ('{0}: Parameters: {1}' -f $MyInvocation.MyCommand.Name, (($PSBoundParameters.Keys | ? { $_ -ne 'object' } | % { '{0}={1}' -f $_, $PSBoundParameters[$_]}) -join $multivalueSeparator))

  [Microsoft.EnterpriseManagement.Administration.AgentManagedComputer] $agentToWait = $null

  $trialCount = 67
  $trialCounter = $trialCount
  do {

    if ($trialCounter -ne $trialCount) {

      DBG ('Sleeping 6sec to allow the agent init itself')
      Start-Sleep 9
    }

    $agentToWait = $null
    $agentServiceOnly = $null

    DBG ('Get the agent and verify it exists: {0}' -f $agentFQDN)
    DBGSTART
    $agentToWait = Get-SCOMAgent -DnsHostName $agentFQDN
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND

    if (Is-Null $agentToWait) {

      # Note: sometimes we are such fast that the Get-SCOMAgent returns $null
      #       even after it has just been installed

      $agentServiceOnly = Get-WMIQuerySingleObject -fqdn $agentFQDN -query 'SELECT * FROM Win32_Service WHERE Name = "HealthService"' -credentials $credentials
      DBG ('Agent service opened: {0} | {1} | {2} | {3} | {4}' -f $agentServiceOnly.DisplayName, $agentServiceOnly.State, $agentServiceOnly.StartName, $agentServiceOnly.StartMode, $agentServiceOnly.PathName)
      DBGIF ('{0} - {1}' -f $MyInvocation.MyCommand.Name, $agentFQDN) { Is-Null $agentServiceOnly }
    
    } else {
        
      DBG ('Agent opened: {0} | {1} | {2} | {3} | proxy = {4}' -f $agentToWait.DisplayName, $agentToWait.HealthState, $agentToWait.Version, $agentToWait.ActionAccountIdentity, $agentToWait.ProxyingEnabled)
    }

    $trialCounter --
      
  } while (((Is-NonNull $agentToWait) -or (Is-NonNull $agentServiceOnly)) -and ($trialCounter -gt 0) -and ((Is-EmptyString $agentToWait.Version) -or ($agentToWait.HealthState -eq 'Uninitialized') -or (Is-EmptyString $agentToWait.ActionAccountIdentity)))

  DBGIF ('{0} - {1}' -f $MyInvocation.MyCommand.Name, $agentFQDN) { $trialCounter -lt 1 }
  DBGIF ('{0} - {1}' -f $MyInvocation.MyCommand.Name, $agentFQDN) { Is-Null $agentToWait }

  return $agentToWait
}



# SIG # Begin signature block
# MIIYLgYJKoZIhvcNAQcCoIIYHzCCGBsCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUkxUTXyMWwcOOmBNz451GaYnr
# kzigghNeMIID7jCCA1egAwIBAgIQfpPr+3zGTlnqS5p31Ab8OzANBgkqhkiG9w0B
# AQUFADCBizELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIG
# A1UEBxMLRHVyYmFudmlsbGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhh
# d3RlIENlcnRpZmljYXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcg
# Q0EwHhcNMTIxMjIxMDAwMDAwWhcNMjAxMjMwMjM1OTU5WjBeMQswCQYDVQQGEwJV
# UzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xMDAuBgNVBAMTJ1N5bWFu
# dGVjIFRpbWUgU3RhbXBpbmcgU2VydmljZXMgQ0EgLSBHMjCCASIwDQYJKoZIhvcN
# AQEBBQADggEPADCCAQoCggEBALGss0lUS5ccEgrYJXmRIlcqb9y4JsRDc2vCvy5Q
# WvsUwnaOQwElQ7Sh4kX06Ld7w3TMIte0lAAC903tv7S3RCRrzV9FO9FEzkMScxeC
# i2m0K8uZHqxyGyZNcR+xMd37UWECU6aq9UksBXhFpS+JzueZ5/6M4lc/PcaS3Er4
# ezPkeQr78HWIQZz/xQNRmarXbJ+TaYdlKYOFwmAUxMjJOxTawIHwHw103pIiq8r3
# +3R8J+b3Sht/p8OeLa6K6qbmqicWfWH3mHERvOJQoUvlXfrlDqcsn6plINPYlujI
# fKVOSET/GeJEB5IL12iEgF1qeGRFzWBGflTBE3zFefHJwXECAwEAAaOB+jCB9zAd
# BgNVHQ4EFgQUX5r1blzMzHSa1N197z/b7EyALt0wMgYIKwYBBQUHAQEEJjAkMCIG
# CCsGAQUFBzABhhZodHRwOi8vb2NzcC50aGF3dGUuY29tMBIGA1UdEwEB/wQIMAYB
# Af8CAQAwPwYDVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NybC50aGF3dGUuY29tL1Ro
# YXd0ZVRpbWVzdGFtcGluZ0NBLmNybDATBgNVHSUEDDAKBggrBgEFBQcDCDAOBgNV
# HQ8BAf8EBAMCAQYwKAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMTEFRpbWVTdGFtcC0y
# MDQ4LTEwDQYJKoZIhvcNAQEFBQADgYEAAwmbj3nvf1kwqu9otfrjCR27T4IGXTdf
# plKfFo3qHJIJRG71betYfDDo+WmNI3MLEm9Hqa45EfgqsZuwGsOO61mWAK3ODE2y
# 0DGmCFwqevzieh1XTKhlGOl5QGIllm7HxzdqgyEIjkHq3dlXPx13SYcqFgZepjhq
# IhKjURmDfrYwggSjMIIDi6ADAgECAhAOz/Q4yP6/NW4E2GqYGxpQMA0GCSqGSIb3
# DQEBBQUAMF4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3Jh
# dGlvbjEwMC4GA1UEAxMnU3ltYW50ZWMgVGltZSBTdGFtcGluZyBTZXJ2aWNlcyBD
# QSAtIEcyMB4XDTEyMTAxODAwMDAwMFoXDTIwMTIyOTIzNTk1OVowYjELMAkGA1UE
# BhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMTQwMgYDVQQDEytT
# eW1hbnRlYyBUaW1lIFN0YW1waW5nIFNlcnZpY2VzIFNpZ25lciAtIEc0MIIBIjAN
# BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomMLOUS4uyOnREm7Dv+h8GEKU5Ow
# mNutLA9KxW7/hjxTVQ8VzgQ/K/2plpbZvmF5C1vJTIZ25eBDSyKV7sIrQ8Gf2Gi0
# jkBP7oU4uRHFI/JkWPAVMm9OV6GuiKQC1yoezUvh3WPVF4kyW7BemVqonShQDhfu
# ltthO0VRHc8SVguSR/yrrvZmPUescHLnkudfzRC5xINklBm9JYDh6NIipdC6Anqh
# d5NbZcPuF3S8QYYq3AhMjJKMkS2ed0QfaNaodHfbDlsyi1aLM73ZY8hJnTrFxeoz
# C9Lxoxv0i77Zs1eLO94Ep3oisiSuLsdwxb5OgyYI+wu9qU+ZCOEQKHKqzQIDAQAB
# o4IBVzCCAVMwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAO
# BgNVHQ8BAf8EBAMCB4AwcwYIKwYBBQUHAQEEZzBlMCoGCCsGAQUFBzABhh5odHRw
# Oi8vdHMtb2NzcC53cy5zeW1hbnRlYy5jb20wNwYIKwYBBQUHMAKGK2h0dHA6Ly90
# cy1haWEud3Muc3ltYW50ZWMuY29tL3Rzcy1jYS1nMi5jZXIwPAYDVR0fBDUwMzAx
# oC+gLYYraHR0cDovL3RzLWNybC53cy5zeW1hbnRlYy5jb20vdHNzLWNhLWcyLmNy
# bDAoBgNVHREEITAfpB0wGzEZMBcGA1UEAxMQVGltZVN0YW1wLTIwNDgtMjAdBgNV
# HQ4EFgQURsZpow5KFB7VTNpSYxc/Xja8DeYwHwYDVR0jBBgwFoAUX5r1blzMzHSa
# 1N197z/b7EyALt0wDQYJKoZIhvcNAQEFBQADggEBAHg7tJEqAEzwj2IwN3ijhCcH
# bxiy3iXcoNSUA6qGTiWfmkADHN3O43nLIWgG2rYytG2/9CwmYzPkSWRtDebDZw73
# BaQ1bHyJFsbpst+y6d0gxnEPzZV03LZc3r03H0N45ni1zSgEIKOq8UvEiCmRDoDR
# EfzdXHZuT14ORUZBbg2w6jiasTraCXEQ/Bx5tIB7rGn0/Zy2DBYr8X9bCT2bW+IW
# yhOBbQAuOA2oKY8s4bL0WqkBrxWcLC9JG9siu8P+eJRRw4axgohd8D20UaF5Mysu
# e7ncIAkTcetqGVvP6KUwVyyJST+5z3/Jvz4iaGNTmr1pdKzFHTx/kuDDvBzYBHUw
# ggTlMIIDzaADAgECAhA5vUKe0oFuutW8yQO0umXnMA0GCSqGSIb3DQEBCwUAMHUx
# CzAJBgNVBAYTAklMMRYwFAYDVQQKEw1TdGFydENvbSBMdGQuMSkwJwYDVQQLEyBT
# dGFydENvbSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEjMCEGA1UEAxMaU3RhcnRD
# b20gQ2xhc3MgMiBPYmplY3QgQ0EwHhcNMTYxMjAxMTU1MTEzWhcNMTgxMjAxMTU1
# MTEzWjBRMQswCQYDVQQGEwJDWjEaMBgGA1UECAwRSmlob21vcmF2c2t5IEtyYWox
# DTALBgNVBAcMBEJybm8xFzAVBgNVBAMMDk9uZHJlaiBTZXZlY2VrMIIBIjANBgkq
# hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr9E9hNj06bash9JX97kpsqK9Z/ciOBC6
# trI4nvlW9CPwhKBTb5wArhxLYZBG9jWPWrdy1nL/cm5qMqBb/mogYwMwvEYWMvsI
# OOVn6HD9lVhNAovD6PHz0ziBBKIszXTjyUPQaoIlIELovz967m78HJdUZJGxqhlu
# AsS9o9/fEzA7XXUhUuqRKsetuZV/Asfh5sOveeoRsbeW4daTWvtz3TJuULL0w43L
# NVYJkd6LL8cegvLPVZUe1N7skvidEvntdlowQsJlqFdrH3SGKIPKA6ObcY8SZWkE
# QSbVBF8Kum1UT+jN0gm+84FwOg5WqKx+VvTK2ljVWnPrCD0Zzu2oIQIDAQABo4IB
# kzCCAY8wDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMAkGA1Ud
# EwQCMAAwHQYDVR0OBBYEFG2vSo3NhQWILeUs0oN9XzHTejcfMB8GA1UdIwQYMBaA
# FD5ik5rXxxnuPo9JEIVVFSDjlIQcMG0GCCsGAQUFBwEBBGEwXzAkBggrBgEFBQcw
# AYYYaHR0cDovL29jc3Auc3RhcnRzc2wuY29tMDcGCCsGAQUFBzAChitodHRwOi8v
# YWlhLnN0YXJ0c3NsLmNvbS9jZXJ0cy9zY2EuY29kZTIuY3J0MDYGA1UdHwQvMC0w
# K6ApoCeGJWh0dHA6Ly9jcmwuc3RhcnRzc2wuY29tL3NjYS1jb2RlMi5jcmwwIwYD
# VR0SBBwwGoYYaHR0cDovL3d3dy5zdGFydHNzbC5jb20vMFEGA1UdIARKMEgwCAYG
# Z4EMAQQBMDwGCysGAQQBgbU3AQIFMC0wKwYIKwYBBQUHAgEWH2h0dHBzOi8vd3d3
# LnN0YXJ0c3NsLmNvbS9wb2xpY3kwDQYJKoZIhvcNAQELBQADggEBAJuRiEvHtIYS
# psmMkPhTz4QOOShN3p5KWdf8vm71A33CR9fds10d8D2B2aE+vjmHJ69GY0bbfg5o
# ZY2Lsq2euL7Da5/hS8+6T3MEtD4hnjfHV7mxmoSfFuy/KDipoV6uwhI+ksqchXYd
# UH+5uCQO0MOO8ITjAgzUQsnZ4UIBHBGeP+e+3ljxSYSXWdPIrgxdR971P/HhWSVf
# KNlmBgEKMQM5Jy0aAd4jxSl/AzdYt0+6pliFJ1peGhdFni2Fm8fu5oN68aTIrNtc
# 5WY7Lzgf+sRTVeWORWS37+1zAD0mjzd8gyfBLxRuaRSfjYxny0rLXelAwfiA3ze2
# DU2Bfg9/rfcwggXYMIIDwKADAgECAhBsO9J+3TyUnpWOKKmzx1egMA0GCSqGSIb3
# DQEBCwUAMH0xCzAJBgNVBAYTAklMMRYwFAYDVQQKEw1TdGFydENvbSBMdGQuMSsw
# KQYDVQQLEyJTZWN1cmUgRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTaWduaW5nMSkwJwYD
# VQQDEyBTdGFydENvbSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNTEyMTYw
# MTAwMDVaFw0zMDEyMTYwMTAwMDVaMHUxCzAJBgNVBAYTAklMMRYwFAYDVQQKEw1T
# dGFydENvbSBMdGQuMSkwJwYDVQQLEyBTdGFydENvbSBDZXJ0aWZpY2F0aW9uIEF1
# dGhvcml0eTEjMCEGA1UEAxMaU3RhcnRDb20gQ2xhc3MgMiBPYmplY3QgQ0EwggEi
# MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5FARY97LFhiwIMmCtCCbAgXe5
# aBnZFSsdGGnk2hqWBZcuZHkaqT1RM1rQd2r0ApNBw466cBur2Ht0b5jo17mpPmh2
# pImgIqwX1in4u7hhn9IH0GYOMEcgK3ACHv5zCRxxNLXifqmsqKfxjjpABnaSyvd4
# bO9YBXN9f4NQ6aJVAuMArpanxsJke+P4WECVLk17v92CAN5JVaczI+baT/lgo5NV
# cTEkloCViSbIfU6ILeyhOSQZvpomMYk8eJqI0nimOTJJfmXangNDsrX8np+3lXD0
# +6rCZisXRWIaeffyTMHZ31Qj1D50WYdRtX5yev4WgaXoKJQN3lkgXUcytvyHAgMB
# AAGjggFaMIIBVjAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMw
# EgYDVR0TAQH/BAgwBgEB/wIBADAyBgNVHR8EKzApMCegJaAjhiFodHRwOi8vY3Js
# LnN0YXJ0c3NsLmNvbS9zZnNjYS5jcmwwZgYIKwYBBQUHAQEEWjBYMCQGCCsGAQUF
# BzABhhhodHRwOi8vb2NzcC5zdGFydHNzbC5jb20wMAYIKwYBBQUHMAKGJGh0dHA6
# Ly9haWEuc3RhcnRzc2wuY29tL2NlcnRzL2NhLmNydDAdBgNVHQ4EFgQUPmKTmtfH
# Ge4+j0kQhVUVIOOUhBwwHwYDVR0jBBgwFoAUTgvvGqRAW6UXaYcwyjRoQ9BBrvIw
# PwYDVR0gBDgwNjA0BgRVHSAAMCwwKgYIKwYBBQUHAgEWHmh0dHA6Ly93d3cuc3Rh
# cnRzc2wuY29tL3BvbGljeTANBgkqhkiG9w0BAQsFAAOCAgEAY6U81bNtJyjY67pT
# rzAL6kpdEtX5mspw+kxjjNdNVH5G6lLnhaEkIxqdpvY/Wdw+UdNtExs+N8efKPSw
# h2m/BxXj2fSeLMwXcwHFookScEER8ez0quCNzioqNHac7LCXPEnQzbtG2FHlePKN
# DWh8eU6KxiAzNzIrIxPthinHGgLTBOACHQM2YTlD8YoU5oN3dLmBOqtH0BDMZoLc
# jEIoEW1zC+TnVb3yU1G0xub6gnN7lP50vbAiHJYrnywQiXaloBV8B9YYfe6Zgvjq
# xwufwFcMVyE3UmCuDTsOpjqDEKpJ25s+FUdkie5VqCS1aaudLo31X+9UvP45pfgy
# RqzyfUnVEhH4ZXxlBWZMzj2Xov5+m/+H3kxYuFA5xdqdshj/Zx00S7PkCSF+8M1N
# CcvFgQwjIw61bZAjDBl3P3a8xNTXsb2CjFdiNKbT3LD6IGeIf0b/EbPf0FXdvBrx
# m0ofMOhnngdPolPYCtoOGtZPAVe/xeu+/ZyKv6TSHlshaUO0iYfsmbXnZ51vvt/k
# kjwms9/qPFxSuE0fjEfF7aQazwREDf2hiVPR0pAhvShtM3oU4XreEFEUWEYHs25f
# YV4WMmxkUKSgmSmwRq45tvtGH4LTb5+cd+iLqK8rBQL0E6xaUjjGfsYx7bueIvqT
# vCkrQvoxMbn/qDHCiypowDVq6TAxggQ6MIIENgIBATCBiTB1MQswCQYDVQQGEwJJ
# TDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjEpMCcGA1UECxMgU3RhcnRDb20gQ2Vy
# dGlmaWNhdGlvbiBBdXRob3JpdHkxIzAhBgNVBAMTGlN0YXJ0Q29tIENsYXNzIDIg
# T2JqZWN0IENBAhA5vUKe0oFuutW8yQO0umXnMAkGBSsOAwIaBQCgeDAYBgorBgEE
# AYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwG
# CisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJBDEWBBQeYkuz
# lPDMBcsOPzibfKbgyRXDrjANBgkqhkiG9w0BAQEFAASCAQA00UOohFTP1a2b46p+
# peT0UmEdmp28SLbYoZYOT3SdEfELaAh6DX2SXwNiBAA1J1HF4GOoXlHslIMDrfCF
# QQEGQHgsad9LNDleD253cHWMErgu8rGm9dNTc0byPSRqEk8mUxZ1xLJ3rBrpiGy7
# /XYX40e+xiHB8SwpI+DCznKWF4SMAgYPbSIArL96HN+jGM9iHsT0OLZK557erO8q
# c7KDmin+1N8VD7J6dxH1WEz0uv3HcdxVph8Ve2SSex1/jfBaqN/N02krSqZJ1phV
# V5Cjznt8F9/zWySuPSfxm0VJs+TtMEQe8MyjPrxXw6zvNEmP8op2fmVvJnY4kUv8
# ffL5oYICCzCCAgcGCSqGSIb3DQEJBjGCAfgwggH0AgEBMHIwXjELMAkGA1UEBhMC
# VVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMTAwLgYDVQQDEydTeW1h
# bnRlYyBUaW1lIFN0YW1waW5nIFNlcnZpY2VzIENBIC0gRzICEA7P9DjI/r81bgTY
# apgbGlAwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJ
# KoZIhvcNAQkFMQ8XDTE3MDEyNDIwMjAwOFowIwYJKoZIhvcNAQkEMRYEFEnIOuGI
# LqMX6gBUrrsWxk2jiaDgMA0GCSqGSIb3DQEBAQUABIIBABapP5dQVxE2pnUOPQYu
# EpaASETSLO0BYZW3xJmywT9/9XTeOHuQ5rRZIe2AH6mFdM9sVLAb48XP1VGtqbQX
# NZ+oNz/QHnZqV2T13S4L/yBoLJF0BJBWc6J/M1i/1oAeLQb6z55b6pTIzLdfznR2
# mwkaR96nKRZqnrcU1FtYTWHT3nXtRKqGOwJeOmPeLy8FKKObX6IBrdJ8h9c2Bsy8
# 8MHNjnmrhUo3h7mpdO+CkKXpuPAQBNQvqjAEl0+hGOZlo2OfXFESKgfL1dpsJ+em
# esN+WfZ8ZHoKK/k/N3Noc0SuXxdHYwE/QGbzB1Y6vzERPzGIWhquZpWZ4llr/H7n
# Tok=
# SIG # End signature block