ADLAB PowerShell source file: buildup-4.ps1

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




#=====================
# Domain things

DBG ("Build step: {0}" -f $MyInvocation.MyCommand.Definition)
DBG "Domain related steps..."
DBG ("Machine config: {0}, {1}" -f $vmName, $vmConfig.hostName)

# The only valid combinations:
# @join - member machine
# @new + @dcpromo - first DC of a forest
# @join + @new + @dcpromo - first dc in a new domain in an existing forest

DBGIF $MyInvocation.MyCommand.Name { (Is-ValidString $vmConfig.domain.join) -and (Is-ValidString $vmConfig.domain.new) -and (Is-EmptyString $vmConfig.domain.dcPromo)  }
DBGIF $MyInvocation.MyCommand.Name { (Is-ValidString $vmConfig.domain.join) -and (Is-ValidString $vmConfig.domain.new) -and ($vmConfig.domain.new -eq $vmConfig.domain.join) }
DBGIF $MyInvocation.MyCommand.Name { (Is-ValidString $vmConfig.domain.new) -and (Is-EmptyString $vmConfig.domain.dcPromo)  }


#===============
DBG ("Should wait for domain explicitly: {0}" -f $vmConfig.domain.wait)

[string] $firstDC = ''
[bool] $areWeFirstDC = $false

[string] $ourAdSite = $null
if (Is-ValidString $vmConfig.domain.adsite.site) {

  $ourAdSite = $vmConfig.domain.adsite.site
}

##
##

DBG ('Any machines that we should wait for explicitly: {0}' -f $vmConfig.domain.wait.machines)
if (Is-ValidString($vmConfig.domain.wait.machines)) {

  $machinesToWaitForFirst = Split-MultiValue $vmConfig.domain.wait.machines

  DBGIF '======== MACHINE WAIT START ========' { $true }
  DBG ('We are going to wait for some machines before anything else: #{0} | {1}' -f (Get-CountSafe $machinesToWaitForFirst), ($machinesToWaitForFirst -join ','))

  foreach ($oneMachineToWaitForFirst in $machinesToWaitForFirst) {

    Wait-Machine $oneMachineToWaitForFirst
  }

  DBGIF '========= MACHINE WAIT END =========' { $true }
}

##
##

DBG ('Not waiting explicitly. Check for domain membership')

$joinDomainFQDN = $vmConfig.domain.join
DBG ('Is this a domain joined machine or a new replica DC or a subdomain DC: {0}' -f (Is-ValidString $joinDomainFQDN))
DBG ('Arent we already domain members in case we are adapting: adapting = {0} | domain = {1}' -f $global:adaptingExistingEnv, $global:thisComputerDomain)

if ((Is-ValidString $joinDomainFQDN) -and ($joinDomainFQDN -ne $global:thisComputerDomain)) {

  DBG ('We will have to wait for the domain to become stable')

  $firstDC = Get-FirstDC $joinDomainFQDN
  $areWeFirstDC = $vmConfig.hostName -eq $firstDC

  DBG ('Are we the first DC ourselves: {0} | {1} | {2}' -f $joinDomainFQDN, $vmConfig.hostName, ((Is-ValidString $joinDomainFQDN) -and ($vmConfig.hostName -eq $firstDC)))
    
  # Just a matter of some backward compatibility
  DBGIF 'First DC of a (sub)domain must have @new (plus)instead of @join' { $vmConfig.hostName -eq $firstDC } 
     
  if (-not $areWeFirstDC) {
  
    if (Is-EmptyString $vmConfig.domain.dcPromo) {

      # Note: this actually gets machines from the whole forest, so
      #       we have to compare the domain name later again
      $ourDomainMachines = Get-ForestMachines $joinDomainFQDN
      DBGIF $MyInvocation.MyCommand.Name { (Get-CountSafe $ourDomainMachines) -lt 2 }

      $waitForTheWholeForest = Is-NonNull $vmConfig.SelectSingleNode('.//app[translate(@waitForTheWholeForest,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")="true"]')

      DBG ('Joining domain as a member, so wait for all our DCs first: wholeForest = {0}' -f $waitForTheWholeForest)
      DBGIF '======= DOMAIN WAIT START =======' { $true }

      foreach ($oneOurDomainMachine in $ourDomainMachines) {

        # Note: generally, we do not wait for RODCs to complete
        #       but if the machine specifies its site, it might be because of a RODC site
        #       so in such a case we wait indiscrimately for RWDC or RODC
        if ($oneOurDomainMachine.isDC -and (($oneOurDomainMachine.domain -eq $joinDomainFQDN) -or $waitForTheWholeForest) -and $oneOurDomainMachine.do -and ((-not $oneOurDomainMachine.isRODC) -or ((Is-ValidString $ourAdSite) -and ($oneOurDomainMachine.adSite -eq $ourAdSite)))) {

          Wait-Machine (Format-MultiValue @($oneOurDomainMachine.hostName, $oneOurDomainMachine.domain))
        }
      }

      DBGIF '======== DOMAIN WAIT END ========' { $true }

    } else {

      DBG ('Joining domain as a DC and we are not the first DC, so wait')
      DBGIF '======= DOMAIN WAIT START =======' { $true }
      Wait-Machine (Format-MultiValue @($firstDC, $joinDomainFQDN))
      DBGIF '======== DOMAIN WAIT END ========' { $true }
    }
  }
}





#===============
[bool] $joinDomainAsMember = (Is-ValidString $vmConfig.domain.join) -and (Is-EmptyString $vmConfig.domain.dcPromo)
[bool] $joinDomainBeforeDCPROMO = (Is-ValidString $vmConfig.domain.join) -and (Is-ValidString $vmConfig.domain.dcPromo) -and (-not $areWeFirstDC) -and ($global:phaseRetrial -eq 1)
[bool] $joinDomainBeforeDCPROMODone = (Is-ValidString $vmConfig.domain.join) -and (Is-ValidString $vmConfig.domain.dcPromo) -and (-not $areWeFirstDC) -and ($global:phaseRetrial -gt 1)
DBG ("Join domain as member: {0}" -f $joinDomainAsMember)
DBG ('Join domain as DC before DCPROMO: should = {0} | alreadyDone = {1}' -f $joinDomainBeforeDCPROMO, $joinDomainBeforeDCPROMODone)
DBGIF $MyInvocation.MyCommand.Name { $joinDomainAsMember -and $joinDomainBeforeDCPROMO }
DBG ('New domain name vs. current domain name if adapting: current = {0} | new = {1}' -f $vmConfig.domain.join, $global:thisComputerDomain)

if (($joinDomainAsMember -or $joinDomainBeforeDCPROMO) -and ($vmConfig.domain.join -ne $global:thisComputerDomain))
{
  DBG ('We should actually join domain: {0}' -f $vmConfig.domain.join)

  $joinerCred = Get-JoinerCredentials $vmConfig

  if (Parse-BoolSafe $vmConfig.domain.prestage) {
  
    $joinFlags = 1
  }

  else {

    $joinFlags = 3 # NETSETUP_JOIN_DOMAIN = 1, NETSETUP_ACCT_CREATE = 2
  }
  
  DBG ("Joining domain: {0} | {1} | {2}" -f $vmConfig.domain.join, $joinerCred.Login, $joinFlags)
  
  Run-Process 'IPCONFIG' '/flushdns'
  #$cmdRs = & { IPCONFIG /flushdns } | Out-String
  #DBG ("CMD output: {0}" -f $cmdRs)   

  Run-Process 'nslookup' ('-q=SRV _ldap._tcp.dc._msdcs.{0}' -f $vmConfig.domain.join)


  DBGSTART
  $thisComp = Get-WMIQuerySingleObject '.' "SELECT * FROM Win32_ComputerSystem"
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND


  Wait-Periodically -maxTrialCount 3 -sleepSec 37 -sleepMsg 'If the domain join operation fails, retry again' -sleepMsgOnlyIfNotFinishedImmediatelly -scriptBlockWhichReturnsTrueToStop { 

    # Note: here we try to fix the problems which arise when an old DC is hit during the join attempt
    #       such as the SMBv1 on 2003 DCs or any later potential problems
    Run-Process 'IPCONFIG' '/flushdns'

    DBG ('Calling JoinDomainOrWorkgroup: {0} | {1}' -f $vmConfig.domain.join, $joinerCred.FullLogin)
    DBGSTART
    $wmiRs = $null
    $wmiRs = $thisComp.JoinDomainOrWorkGroup(
      $vmConfig.domain.join,
      $joinerCred.Pwd,
      $joinerCred.FullLogin,
      $null,
      3)
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND
    DBGWMI $wmiRs

    DBGIF $MyInvocation.MyCommand.Name { ($wmiRs.returnValue -ne 0) -and ($wmiRs.returnValue -ne 1316) }
    return (($wmiRs.returnValue -eq 0) -or ($wmiRs.returnValue -eq 1316))
  }


  if ($joinDomainBeforeDCPROMO) {

    DBG ('This is a secondary DC in a domain, we restart now to let it promote in the next round only')
    $global:restartCurrentPhase = $true
    exit
  }
}




#===============
DBG ("Install AD DS: {0}" -f (Is-ValidString($vmConfig.domain.dcPromo)))

if (Is-ValidString $vmConfig.domain.dcPromo)
{
  DBGIF $MyInvocation.MyCommand.Name { $global:thisOSVersionNumber -gt 10 }
  DBGIF $MyInvocation.MyCommand.Name { $global:thisOSVersionNumber -lt 5 }

  if ($joinDomainBeforeDCPROMODone) {

    DBG ('Precheck domain membership quality for already joined machine')
    Run-Process 'gpupdate' '/force'

    if ($global:thisOSVersionNumber -ge 6.1) {

      Run-Process 'nltest' ('/sc_reset:{0}' -f $vmConfig.domain.join)
      Run-Process 'nltest' ('/sc_verify:{0}' -f $vmConfig.domain.join)
    }
  }

  if (($global:thisOSVersionNumber -ge 5) -and ($global:thisOSVersionNumber -le 10)) {
  
    if ($global:thisOSVersionNumber -ge 6.2) {

      DBG ('On some newer systems DFSRDIAG is missing, so we just go for all the features implicitly')
      $restartRequired = Install-WindowsFeaturesUniversal @('AD-Domain-Services', 'RSAT-ADDS', 'RSAT-DFS-Mgmt-Con', 'RSAT-Feature-Tools-BitLocker') $false $global:installMediaVolume $global:installISOVolume
      DBGIF $MyInvocation.MyCommand.Name { $restartRequired }
    }

    DBG ("Preparing DCPROMO for Win5/6.0/6.1/6.2/6.3: {0}" -f $vmConfig.domain.dcPromo)
    $dcPromoParams = Split-MultiValue $vmConfig.domain.dcPromo
    DBG ("Loaded DCPROMO parameters:")
    DBG ("{0}" -f ($dcPromoParams | Out-String))

    if ($thisOSVersion -like '5.*') {

      if ($dcPromoParams[0].Trim() -like '/*:*') {

        DBG ('DCPROMO params come in 6.x format. Reformat to 5.x INI syntax.')
        
        [System.Collections.ArrayList] $dcPromoParams50 = @()

        foreach ($oneDcPromoParam in $dcPromoParams) {

          $oneDcPromoParamTrimmed = $oneDcPromoParam.Trim()

          DBGIF $MyInvocation.MyCommand.Name { $oneDcPromoParamTrimmed -like '=' }
          DBGIF $MyInvocation.MyCommand.Name { $oneDcPromoParamTrimmed -notlike '/?*:?*' }

          if ($oneDcPromoParamTrimmed -like '/*:*') {
            
            $oneParam = $oneDcPromoParamTrimmed.SubString(1, $oneDcPromoParamTrimmed.IndexOf(':') - 1).Trim()
            $oneValue = $oneDcPromoParamTrimmed.SubString($oneDcPromoParamTrimmed.IndexOf(':') + 1).Trim()

            if ($oneParam -eq 'RebootOnCompletion') {

              DBG ('RebootOnCompletion parameter is 6.x syntax. Replace it with 5.x version of RebootOnSuccess:NoAndNoPromptEither: {0}' -f $oneDcPromoParamTrimmed)
              $oneParam = 'RebootOnSuccess'
              $oneValue = 'NoAndNoPromptEither'
            }

            [void] $dcPromoParams50.Add(('{0}={1}' -f $oneParam, $oneValue))
          
          } else {

            [void] $dcPromoParams50.Add($oneDcPromoParamTrimmed)
          }
        }

        $dcPromoParams = $dcPromoParams50

        DBG ('Raparsed 5.x DCPROMO parameters:')
        DBG ('{0}' -f ($dcPromoParams | Out-String))
      }

      $dcPromoFile = Get-DataFileApp "dcpromoUnattend" $null '.txt'

      DBG ("Building Win5.x DCPROMO answer file: {0}" -f $dcPromoFile)
      @('[Unattended]', 'UnattendMode=FullUnattended', '', '[DCINSTALL]') | Out-File -FilePath $dcPromoFile -Force -EV er -EA SilentlyContinue
      DBGER $MyInvocation.MyCommand.Name $er 
      $dcPromoParams | Out-File -FilePath $dcPromoFile -Append -Force -EV er -EA SilentlyContinue
      DBGER $MyInvocation.MyCommand.Name $er 
    
      DBG ("Starting Win5.x DCPROMO")
      Run-Process "DCPROMO" "/adv /answer:`"$dcPromoFile`""

    } else {

      DBG ('We have to reparse all DC promo params to enclose the values in double quotes in order to fix problems with random password generations')

      [Collections.ArrayList] $reparsedDcPromoParams = @()
      foreach ($oneDcPromoParam in $dcPromoParams) {

        $oneDcPromoParamTrimmed = $oneDcPromoParam.Trim()
        DBGIF $MyInvocation.MyCommand.Name { $oneDcPromoParamTrimmed -notlike '/?*:?*' }
        
        $oneParam = $oneDcPromoParamTrimmed.SubString(1, $oneDcPromoParamTrimmed.IndexOf(':') - 1).Trim()
        $oneValue = $oneDcPromoParamTrimmed.SubString($oneDcPromoParamTrimmed.IndexOf(':') + 1).Trim()

        [void] $reparsedDcPromoParams.Add(('/{0}:"{1}"' -f $oneParam, $oneValue))
      }
     
      DBG ('Starting Win6.x DCPROMO')
      Run-Process "DCPROMO" ("/unattend {0}" -f ($reparsedDcPromoParams -join ' '))
    }
  }
  
#  if ($thisOSVersion -like '6.2.*') {
#  
#    DBG ("Starting Install-ADDSForest for Win6.2: {0}" -f $vmConfig.domain.dcPromo)
#  }

  #$firstDC = Get-FirstDC $vmConfig.domain.join
  
  DBG ('Are we a secondary DC in an existing domain: {0}' -f (Is-EmptyString $vmConfig.domain.new))
  if (Is-EmptyString $vmConfig.domain.new) {

    DBG ('Going to adjust autologon account as we are secondary DC and might not be able to continue with the current account anymore.')
    
    # Note: we can safely proceed with JoinerCredentials as the secondary DC requires login/pwd in the  element
    $joinerCred = Get-JoinerCredentials $vmConfig

    DBG ('New autologon identity: {0} | {1}' -f $joinerCred.login, $joinerCred.domain)
    $global:phaseCfg.sevecekBuildup.login.autoLogin = [string] $joinerCred.login
    $global:phaseCfg.sevecekBuildup.login.pwd = [string] $joinerCred.pwd
    $global:phaseCfg.sevecekBuildup.login.domain = [string] $joinerCred.domain
  }


  # Note: there is a slight difference, exactly and only on 2008 RTM, with autologon
  #       all other systems can autologon with .\builtin-admin except for 2008 RTM DC which cannot use the . as the domain
  #       name in the autologon registry. Thus we must adjust the domain name to be our new promoted domain name
  DBG ('Are we a first 2008 RTM DC for a new domain: {0}' -f (($global:thisOSVersionNumber -eq 6.0) -and (Is-ValidString $vmConfig.domain.new)))
  if (($global:thisOSVersionNumber -eq 6.0) -and (Is-ValidString $vmConfig.domain.new)) {

    DBG ('New autologon domain on first 2008 RTM DC: {0}' -f $vmConfig.domain.new)
    $global:phaseCfg.sevecekBuildup.login.domain = [string] $vmConfig.domain.new
  }
}


#===============
DBG ("Do not wait for initial DC sync: {0}" -f (Parse-BoolSafe($vmConfig.domain.noWaitInitSync)))

if (Parse-BoolSafe($vmConfig.domain.noWaitInitSync))
{
  Set-RegistryValue 'HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters' 'Repl Perform Initial Synchronizations' 0 DWord
}




# SIG # Begin signature block
# MIIYMAYJKoZIhvcNAQcCoIIYITCCGB0CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBBBG2HQ7sLUG+7
# WXoHS4sE5i9Qegerl2UOQ7ctqyFnyaCCE0cwggYEMIID7KADAgECAgoqHIRwAAEA
# AAB/MA0GCSqGSIb3DQEBCwUAMGwxCzAJBgNVBAYTAkNaMRcwFQYDVQQIEw5DemVj
# aCBSZXB1YmxpYzENMAsGA1UEBxMEQnJubzEQMA4GA1UEChMHU2V2ZWNlazEjMCEG
# A1UEAxMaU2V2ZWNlayBFbnRlcnByaXNlIFJvb3QgQ0EwHhcNMTkwNjExMTkyMzMy
# WhcNMjQwNjA5MTkyMzMyWjCBjzELMAkGA1UEBhMCQ1oxFzAVBgNVBAgTDkN6ZWNo
# IFJlcHVibGljMQ0wCwYDVQQHEwRCcm5vMRwwGgYDVQQKExNJbmcuIE9uZHJlaiBT
# ZXZlY2VrMRcwFQYDVQQDEw5PbmRyZWogU2V2ZWNlazEhMB8GCSqGSIb3DQEJARYS
# b25kcmVqQHNldmVjZWsuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
# AQEAnkjWNkK4FfUUN8iAN91ry+wsSn8cFKJbMnROAqTrx8t3H315p2/bUG2DosCF
# Odu0WcaTOLdm5obhT+/3O7BqpdcnlWKlSEz4AL9zQeCbe4++NObBVPBbPE16j9C4
# xELoXW/Ti86C2PEkN5azGUvxGxzQQ45g32OsEI+Bh05qHMkk3oQ6L8O0Fpd5W4e+
# L4HuKS3JOikNhhryTNPD9grF/0wXTzn94TrL1GohuaCPh8g9HOtMoDCd+ExnqV8q
# 4k60D37BOK1I81hYFIBn8MvCsjMRC5TK87MtI7aUUIeve5kopc8ZpxNti3F/+Puh
# 4UUxL3nKjfAM6HE0b7FqkfkRpwIDAQABo4IBgjCCAX4wEwYDVR0lBAwwCgYIKwYB
# BQUHAwMwDgYDVR0PAQH/BAQDAgbAMBsGCSsGAQQBgjcVCgQOMAwwCgYIKwYBBQUH
# AwMwHQYDVR0OBBYEFOKbNkkiAht2GxCISJMJxLg4gOC9MB0GA1UdEQQWMBSBEm9u
# ZHJlakBzZXZlY2VrLmNvbTAfBgNVHSMEGDAWgBQNnMgyfdUi8l9UfithS4FQ88Vs
# wDBSBgNVHR8ESzBJMEegRaBDhkFodHRwOi8vcGtpLnNldmVjZWsuY29tL0NBL1Nl
# dmVjZWslMjBFbnRlcnByaXNlJTIwUm9vdCUyMENBKDEpLmNybDCBhgYIKwYBBQUH
# AQEEejB4ME0GCCsGAQUFBzAChkFodHRwOi8vcGtpLnNldmVjZWsuY29tL0NBL1Nl
# dmVjZWslMjBFbnRlcnByaXNlJTIwUm9vdCUyMENBKDEpLmNydDAnBggrBgEFBQcw
# AYYbaHR0cDovL3BraS5zZXZlY2VrLmNvbS9vY3NwMA0GCSqGSIb3DQEBCwUAA4IC
# AQCfr6XDtt/O8OBr+X5l49UBLaJrjUXHkAHofdC7p7BLCXIs4GYIti1lf6pas5yB
# Q428aKITITq/vEHUTyiiyKtzVkafILWXXKPxy+zmmuw9odB3Hea4ECNpcaG8UNtz
# vMm1Dr0ZrkENhcv6I3tNhRr2AOE9AKOfnVEullFD/mZqfmaNkhpnl31jk7OMSUQc
# oY8qD6BDQP9371C10gJOmp57sHfPa4Vn5E4aNzn8+o9C9HI7nNagZF5BamKOFdR2
# ui7K3krMbTuDHo+ZcA9nHnzZqiVKpEBFu8lGv9Mf+GDb9yxz6EjV3xS2RcnywX2v
# z0VUt2NGno8LudrnWpgrRy4Sl7x6FwVVKtS/o7zFSIiHgntIKFv8urSKSTukCLFK
# Y9fBIDDlWFV1ZV1DNpNWxnexWIRv2AH7YlzKQCA4Rysn01hVeBGsWFkCr9J33LmV
# enQYpk9eoYMPRwAYg48r65wOOOzLvmyLSGllH88BMvmTQ9myXqwp6NDH1psljXTl
# PUbpf7w6IZwsY0dhGhP9iyqbcrGdK0Bnf8Za6Qdj3iXtwd1VgpatFZrxOM5KawCL
# pkYl1ABupbzNpWzmC+nfymqwbYiCogPt1vHOyF4EJ73ExVDCqXkpiNvFRqmu1eaZ
# IOdbPCdl00a9rk52NKqo/BUsw16TKsDEYTA/7ACbEsnERzCCBmowggVSoAMCAQIC
# EAMBmgI6/1ixa9bV6uYX8GYwDQYJKoZIhvcNAQEFBQAwYjELMAkGA1UEBhMCVVMx
# FTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNv
# bTEhMB8GA1UEAxMYRGlnaUNlcnQgQXNzdXJlZCBJRCBDQS0xMB4XDTE0MTAyMjAw
# MDAwMFoXDTI0MTAyMjAwMDAwMFowRzELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERp
# Z2lDZXJ0MSUwIwYDVQQDExxEaWdpQ2VydCBUaW1lc3RhbXAgUmVzcG9uZGVyMIIB
# IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo2Rd/Hyz4II14OD2xirmSXU7
# zG7gU6mfH2RZ5nxrf2uMnVX4kuOe1VpjWwJJUNmDzm9m7t3LhelfpfnUh3SIRDsZ
# yeX1kZ/GFDmsJOqoSyyRicxeKPRktlC39RKzc5YKZ6O+YZ+u8/0SeHUOplsU/UUj
# joZEVX0YhgWMVYd5SEb3yg6Np95OX+Koti1ZAmGIYXIYaLm4fO7m5zQvMXeBMB+7
# NgGN7yfj95rwTDFkjePr+hmHqH7P7IwMNlt6wXq4eMfJBi5GEMiN6ARg27xzdPpO
# 2P6qQPGyznBGg+naQKFZOtkVCVeZVjCT88lhzNAIzGvsYkKRrALA76TwiRGPdwID
# AQABo4IDNTCCAzEwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0l
# AQH/BAwwCgYIKwYBBQUHAwgwggG/BgNVHSAEggG2MIIBsjCCAaEGCWCGSAGG/WwH
# ATCCAZIwKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMw
# ggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0AGgA
# aQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1AHQA
# ZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABpAGcA
# aQBDAGUAcgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBlAGwA
# eQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBoAGkA
# YwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAgAGEA
# cgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAgAGIA
# eQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wCwYJYIZIAYb9bAMVMB8GA1UdIwQYMBaA
# FBUAEisTmLKZB+0e36K+Vw0rZwLNMB0GA1UdDgQWBBRhWk0ktkkynUoqeRqDS/Qe
# icHKfTB9BgNVHR8EdjB0MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20v
# RGlnaUNlcnRBc3N1cmVkSURDQS0xLmNybDA4oDagNIYyaHR0cDovL2NybDQuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEQ0EtMS5jcmwwdwYIKwYBBQUHAQEE
# azBpMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYB
# BQUHMAKGNWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3Vy
# ZWRJRENBLTEuY3J0MA0GCSqGSIb3DQEBBQUAA4IBAQCdJX4bM02yJoFcm4bOIyAP
# gIfliP//sdRqLDHtOhcZcRfNqRu8WhY5AJ3jbITkWkD73gYBjDf6m7GdJH7+IKRX
# rVu3mrBgJuppVyFdNC8fcbCDlBkFazWQEKB7l8f2P+fiEUGmvWLZ8Cc9OB0obzpS
# CfDscGLTYkuw4HOmksDTjjHYL+NtFxMG7uQDthSr849Dp3GdId0UyhVdkkHa+Q+B
# 0Zl0DSbEDn8btfWg8cZ3BigV6diT5VUW8LsKqxzbXEgnZsijiwoc5ZXarsQuWaBh
# 3drzbaJh6YoLbewSGL33VVRAA5Ira8JRwgpIr7DUbuD0FAo6G+OPPcqvao173NhE
# MIIGzTCCBbWgAwIBAgIQBv35A5YDreoACus/J7u6GzANBgkqhkiG9w0BAQUFADBl
# MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
# d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
# b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMjExMTEwMDAwMDAwWjBiMQswCQYDVQQG
# EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
# cnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBBc3N1cmVkIElEIENBLTEwggEiMA0G
# CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDogi2Z+crCQpWlgHNAcNKeVlRcqcTS
# QQaPyTP8TUWRXIGf7Syc+BZZ3561JBXCmLm0d0ncicQK2q/LXmvtrbBxMevPOkAM
# Rk2T7It6NggDqww0/hhJgv7HxzFIgHweog+SDlDJxofrNj/YMMP/pvf7os1vcyP+
# rFYFkPAyIRaJxnCI+QWXfaPHQ90C6Ds97bFBo+0/vtuVSMTuHrPyvAwrmdDGXRJC
# geGDboJzPyZLFJCuWWYKxI2+0s4Grq2Eb0iEm09AufFM8q+Y+/bOQF1c9qjxL6/s
# iSLyaxhlscFzrdfx2M8eCnRcQrhofrfVdwonVnwPYqQ/MhRglf0HBKIJAgMBAAGj
# ggN6MIIDdjAOBgNVHQ8BAf8EBAMCAYYwOwYDVR0lBDQwMgYIKwYBBQUHAwEGCCsG
# AQUFBwMCBggrBgEFBQcDAwYIKwYBBQUHAwQGCCsGAQUFBwMIMIIB0gYDVR0gBIIB
# yTCCAcUwggG0BgpghkgBhv1sAAEEMIIBpDA6BggrBgEFBQcCARYuaHR0cDovL3d3
# dy5kaWdpY2VydC5jb20vc3NsLWNwcy1yZXBvc2l0b3J5Lmh0bTCCAWQGCCsGAQUF
# BwICMIIBVh6CAVIAQQBuAHkAIAB1AHMAZQAgAG8AZgAgAHQAaABpAHMAIABDAGUA
# cgB0AGkAZgBpAGMAYQB0AGUAIABjAG8AbgBzAHQAaQB0AHUAdABlAHMAIABhAGMA
# YwBlAHAAdABhAG4AYwBlACAAbwBmACAAdABoAGUAIABEAGkAZwBpAEMAZQByAHQA
# IABDAFAALwBDAFAAUwAgAGEAbgBkACAAdABoAGUAIABSAGUAbAB5AGkAbgBnACAA
# UABhAHIAdAB5ACAAQQBnAHIAZQBlAG0AZQBuAHQAIAB3AGgAaQBjAGgAIABsAGkA
# bQBpAHQAIABsAGkAYQBiAGkAbABpAHQAeQAgAGEAbgBkACAAYQByAGUAIABpAG4A
# YwBvAHIAcABvAHIAYQB0AGUAZAAgAGgAZQByAGUAaQBuACAAYgB5ACAAcgBlAGYA
# ZQByAGUAbgBjAGUALjALBglghkgBhv1sAxUwEgYDVR0TAQH/BAgwBgEB/wIBADB5
# BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0
# LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0Rp
# Z2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0fBHoweDA6oDigNoY0aHR0
# cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNy
# bDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJl
# ZElEUm9vdENBLmNybDAdBgNVHQ4EFgQUFQASKxOYspkH7R7for5XDStnAs0wHwYD
# VR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZIhvcNAQEFBQADggEB
# AEZQPsm3KCSnOB22WymvUs9S6TFHq1Zce9UNC0Gz7+x1H3Q48rJcYaKclcNQ5IK5
# I9G6OoZyrTh4rHVdFxc0ckeFlFbR67s2hHfMJKXzBBlVqefj56tizfuLLZDCwNK1
# lL1eT7EF0g49GqkUW6aGMWKoqDPkmzmnxPXOHXh2lCVz5Cqrz5x2S+1fwksW5Etw
# TACJHvzFebxMElf+X+EevAJdqP77BzhPDcZdkbkPZ0XN1oPt55INjbFpjE/7WeAj
# D9KqrgB87pxCDs+R1ye3Fu4Pw718CqDuLAhVhSK46xgaTfwqIa1JMYNHlXdx3LEb
# S0scEJx3FMGdTy9alQgpECYxggQ/MIIEOwIBATB6MGwxCzAJBgNVBAYTAkNaMRcw
# FQYDVQQIEw5DemVjaCBSZXB1YmxpYzENMAsGA1UEBxMEQnJubzEQMA4GA1UEChMH
# U2V2ZWNlazEjMCEGA1UEAxMaU2V2ZWNlayBFbnRlcnByaXNlIFJvb3QgQ0ECCioc
# hHAAAQAAAH8wDQYJYIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAA
# oQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4w
# DAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg7DrLC0ZxVljikFbe+nKIMmHR
# 3B2RMY1D3lYfwKb+QygwDQYJKoZIhvcNAQEBBQAEggEAacTy0GWHL4PO4920rx7N
# GvhHKnlXz17bJNIqeQBZa0M/icBCAkRsxleh7vdiYmZoK2texrTn8RsJAawVEYGB
# +X+jfSeDcTKnXlxVydRG2XrHeo66E/svoGX+NbugFKdF7vaXUqC3Apc/IcVriqRc
# VI9SUQnCDQdp8EvRZo1OdQoOm+uhkjAxFx7TnJohEIw5aAjQtF0kbq+NYOcYEn2K
# b5twcKHUjzncbT5vP1sX5sCfd8TuKlTgqtab7bWXefe9REAkd8+4cnXasV7wcvZe
# J1pcgTLLpG2IVhFVeHLcPA3XVTf8XbgQQnkh81qa+a4rm3nIHnKaf4rZv+dtBUr5
# cKGCAg8wggILBgkqhkiG9w0BCQYxggH8MIIB+AIBATB2MGIxCzAJBgNVBAYTAlVT
# MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
# b20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMQIQAwGaAjr/WLFr
# 1tXq5hfwZjAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAc
# BgkqhkiG9w0BCQUxDxcNMTkwNzI5MDQyOTE0WjAjBgkqhkiG9w0BCQQxFgQUVNma
# +pv4zcEI0BS5Jqj54IDLHfEwDQYJKoZIhvcNAQEBBQAEggEAMTn6mnlcIGXUWqkj
# 085xauKUdTRUu6rE32yh99w39oVSmlXbdptN4Ym3po/cGPoo7FiqXFF/pPVU32Tw
# CZc+SlGwR4cuCcWj+po9m6K59tLpsc0BagbwjhczC9L+l4ZxoQkhTrg18/5D8w8s
# BLoYfwMCXtzsq3QvXzwiGWLDmBOAI4NiODlG1eOcYtn1uLOmtQmU4/7fR0u3FBSl
# io4BHzSduAqD+41eV17rBEgZQ9KWyvHt0J6Iof0acA1iFeWWfuTfqhIBaV9fP/mB
# WtJ42/oU1ahj7LnQsslCeiPQ5ya++UXKVVT+DHofLw/mJa/mbFeJzZ1+wMj6SGzc
# szEhCQ==
# SIG # End signature block