ADLAB PowerShell source file: buildup-7.ps1

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




#=====================
# Application installations

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


$restartRequired = $false
$subPhasesToDo = 0
$subPhasesDone = 0


#=========================
DBG ("Should install NLB: {0}" -f (Is-ValidString $vmConfig.nlb.instance))
DBG ('Will postpone to the next round: {0}' -f $restartRequired)

if ((Is-ValidString $vmConfig.nlb.instance) -and (-not (Parse-BoolSafe $vmConfig.nlb.dnd)) -and (Do-SubPhase nlb)) {

  $subPhasesToDo ++

  if (-not $restartRequired) {

    DBG ("Going to run NLB library to complete the installation.")
    $buildLibExitCode = Run-Process 'powershell' ('-ExecutionPolicy Bypass -File "{0}" "{1}"' -f "$global:rootDir\buildup-nlb.ps1", $vmName) $true $vmConfig.nlb.app.iLogin $vmConfig.nlb.app.iDomain $vmConfig.nlb.app.iPwd $false $null $true -showWindow

    if ($buildLibExitCode -eq $global:VM_RECYCLE_REQUIRED) {

      DBG ('AppLibrary requested recycle and be re-run the next time')
      $restartRequired = $true

    } else {

      $restartRequired = $true # we ignore other statuses and always restart

      Finish-SubPhase nlb
      $subPhasesDone ++
    }
  }
}


#=========================
DBG ("Should enable BitLocker for OS volume: {0}" -f ((Is-NonNull $vmConfig.bitlocker) -and (Parse-BoolSafe $vmConfig.bitlocker.os)))
DBG ('Will postpone to the next round: {0}' -f $restartRequired)
 
if (((Is-NonNull $vmConfig.bitlocker) -and (Parse-BoolSafe $vmConfig.bitlocker.os)) -and (-not (Parse-BoolSafe $vmConfig.bitlocker.dnd)) -and (Do-SubPhase bitlockerOS)) {

  $subPhasesToDo ++

  if (-not $restartRequired) {

    DBG ('Enable BitLocker for OS volume: {0}' -f (Parse-BoolSafe $vmConfig.bitlocker.os))

    Enable-BitLocker $env:SystemDrive -algo $vmConfig.bitlocker.algo -usedOnly (Parse-BoolSafe $vmConfig.bitlocker.usedOnly) -protectors $vmConfig.bitlocker.decrypt
    $restartRequired = $true

    Finish-SubPhase bitlockerOS
    $subPhasesDone ++
  }
}


#=========================
DBGSTART
$bitLockerVolumes = $vmConfig.SelectNodes('./bitLocker/partition')
DBGER $MyInvocation.MyCommand.Name $error
DBGEND
DBG ("Should enable BitLocker for volumes: {0}" -f ((Get-CountSafe $bitLockerVolumes) -gt 0))
DBG ('Will postpone to the next round: {0}' -f $restartRequired)
 
if (((Get-CountSafe $bitLockerVolumes) -gt 0) -and (-not (Parse-BoolSafe $vmConfig.bitlocker.dnd)) -and (Do-SubPhase bitlockerVolumes)) {

  $subPhasesToDo ++

  if (-not $restartRequired) {

    DBG ('Enable BitLocker for volumes: {0}' -f (Get-CountSafe $bitLockerVolumes))

    foreach ($oneBitLockerVolume in $bitLockerVolumes) {

      Enable-BitLocker $oneBitLockerVolume.letter -algo $oneBitLockerVolume.algo -usedOnly (Parse-BoolSafe $oneBitLockerVolume.usedOnly) -protectors $oneBitLockerVolume.decrypt -autoUnlock (Parse-BoolSafe $oneBitLockerVolume.autoUnlock)
    }

    Finish-SubPhase bitlockerVolumes
    $subPhasesDone ++
  }
}


#=========================
DBG ("Should install AD CS: {0}" -f (Is-ValidString $vmConfig.ca.instance))
DBG ('Will postpone to the next round: {0}' -f $restartRequired)

if ((Is-ValidString $vmConfig.ca.instance) -and (-not (Parse-BoolSafe $vmConfig.ca.dnd)) -and (Do-SubPhase ca)) {

  $subPhasesToDo ++

  if (-not $restartRequired) {

    DBG ("Going to run AD CS library to complete the installation.")
    $buildLibExitCode = Run-Process 'powershell' ('-ExecutionPolicy Bypass -File "{0}" "{1}"' -f "$global:rootDir\buildup-ca.ps1", $vmName) $true $vmConfig.ca.app.iLogin $vmConfig.ca.app.iDomain $vmConfig.ca.app.iPwd $false $null $true -showWindow

    Finish-SubPhase ca
    $subPhasesDone ++
  }
}


#=========================
DBG ("Should install IIS: {0}" -f (Is-ValidString $vmConfig.iis.instance))
DBG ('Will postpone to the next round: {0}' -f $restartRequired)

if ((Is-ValidString $vmConfig.iis.instance) -and (-not (Parse-BoolSafe $vmConfig.iis.dnd)) -and (Do-SubPhase iis)) {

  $subPhasesToDo ++

  if (-not $restartRequired) {

    PossiblyBind-HttpsCertificatesUnderInstallAccount -app iis -vmName $vmName -iLogin $vmConfig.iis.app.iLogin -iDomain $vmConfig.iis.app.iDomain -iPwd $vmConfig.iis.app.iPwd

    DBG ("Going to run IIS library to complete the installation.")
    $buildLibExitCode = Run-Process 'powershell' ('-ExecutionPolicy Bypass -File "{0}" "{1}"' -f "$global:rootDir\buildup-iis.ps1", $vmName) $true $vmConfig.iis.app.iLogin $vmConfig.iis.app.iDomain $vmConfig.iis.app.iPwd $false $null $true -showWindow

    Finish-SubPhase iis
    $subPhasesDone ++
  }
}


DBG ('Should install FTP site: {0}' -f (Is-ValidString $vmConfig.ftp.instance))
DBG ('Will postpone to the next round: {0}' -f $restartRequired)

if ((Is-ValidString $vmConfig.ftp.instance) -and (-not (Parse-BoolSafe $vmConfig.ftp.dnd)) -and (Do-SubPhase ftp)) {

  $subPhasesToDo ++

  if (-not $restartRequired) {

    DBG ('Going to install the FTP site requested')

    Install-FTPSite $vmConfig.ftp

    Finish-SubPhase ftp
    $subPhasesDone ++
  }
}



#=========================
DBG ("Should install DHCP Server: {0}" -f (Is-ValidString $vmConfig.dhcp.instance))
DBG ('Will postpone to the next round: {0}' -f $restartRequired)

if ((Is-ValidString $vmConfig.dhcp.instance) -and (-not (Parse-BoolSafe $vmConfig.dhcp.dnd)) -and (Do-SubPhase dhcp)) {

  $subPhasesToDo ++

  if (-not $restartRequired) {

    DBG ("Going to run DHCP Server library to complete the installation.")
    $buildLibExitCode = Run-Process 'powershell' ('-ExecutionPolicy Bypass -File "{0}" "{1}"' -f "$global:rootDir\buildup-dhcp.ps1", $vmName) $true  $vmConfig.dhcp.app.iLogin $vmConfig.dhcp.app.iDomain $vmConfig.dhcp.app.iPwd $false $null $false -showWindow

    Finish-SubPhase dhcp
    $subPhasesDone ++
  }
}




#===============                             
$cawebInstances = $vmConfig.SelectNodes('./caweb[@instance and (not(@dnd) or @dnd="false")]')
DBG ('Should install CA web services: #{0}' -f (Get-CountSafe $cawebInstances))
DBG ('Will postpone to the next round: {0}' -f $restartRequired)

if (((Get-CountSafe $cawebInstances) -gt 0) -and (Do-SubPhase caweb)) {

  $subPhasesToDo ++

  if (-not $restartRequired) {

    $cawebInstances | % {

      $oneInstance = $_
      DBG ('Going to run CA web services library to install instance: {0}' -f $oneInstance.instance)
      $buildLibExitCode = Run-Process 'powershell' ('-ExecutionPolicy Bypass -File "{0}" "{1}" "{2}"' -f "$global:rootDir\buildup-caweb.ps1", $vmName, $oneInstance.instance) $true $oneInstance.app.iLogin $oneInstance.app.iDomain $oneInstance.app.iPwd $false $null $true -showWindow
    }

    $restartRequired = $true
    Finish-SubPhase caweb
    $subPhasesDone ++
  }
}



#===============                             
$sqlInstances = $vmConfig.SelectNodes('./sql[@instance and (not(@dnd) or @dnd="false")]')
DBG ("Should install SQL servers: #{0}" -f (Get-CountSafe $sqlInstances))
DBG ('Will postpone to the next round: {0}' -f $restartRequired)

if (((Get-CountSafe $sqlInstances) -gt 0) -and (Do-SubPhase sql)) {

  $subPhasesToDo ++

  if (-not $restartRequired) {

    $sqlInstances | % {

      $oneInstance = $_
      DBG ('Going to run SQL Server library to install instance: {0}' -f $oneInstance.instance)
      $buildLibExitCode = Run-Process 'powershell' ('-ExecutionPolicy Bypass -File "{0}" "{1}" "{2}"' -f "$global:rootDir\buildup-sql.ps1", $vmName, $oneInstance.instance) $true $oneInstance.app.iLogin $oneInstance.app.iDomain $oneInstance.app.iPwd $false $null $true -showWindow
    }

    $restartRequired = $true
    Finish-SubPhase sql
    $subPhasesDone ++
  }
}



#===================
DBG ("Should install SharePoint binaries: {0}" -f (Is-ValidString $vmConfig.sp.instance))
DBG ('Will postpone to the next round: {0}' -f $restartRequired)

if ((Is-ValidString $vmConfig.sp.instance) -and (-not (Parse-BoolSafe $vmConfig.sp.dnd)) -and (Do-SubPhase sp)) {

  $subPhasesToDo ++

  if (-not $restartRequired) {

    DBG ("Going to run SharePoint binaries library to complete the installation.")
    Wait-IfRequested sp | Out-Null

    $buildLibExitCode = Run-Process 'powershell' ('-ExecutionPolicy Bypass -File "{0}" "{1}"' -f "$global:rootDir\buildup-sp.ps1", $vmName) $true $vmConfig.sp.app.iLogin $vmConfig.sp.app.iDomain $vmConfig.sp.app.iPwd $false $null $true -showWindow
         
    if ($buildLibExitCode -eq $global:VM_RECYCLE_REQUIRED) {

      DBG ('AppLibrary requested recycle and be re-run the next time')
      $restartRequired = $true

    } else {

      $restartRequired = $true # we ignore other statuses and always restart
      Finish-SubPhase sp
      $subPhasesDone ++
    }
  }
}



#===================
DBG ("Should initialize SharePoint server: {0}" -f (Is-ValidString $vmConfig.sp.instance))
DBG ('Will postpone to the next round: {0}' -f $restartRequired)

if ((Is-ValidString $vmConfig.sp.instance) -and (-not (Parse-BoolSafe $vmConfig.sp.dnd)) -and (Do-SubPhase spinit)) {

  $subPhasesToDo ++

  if (-not $restartRequired) {

    DBG ("Going to run SharePoint initialization library to complete the installation.")
    Wait-IfRequested spinit | Out-Null

    PossiblyBind-HttpsCertificatesUnderInstallAccount -app spinit -vmName $vmName -iLogin $vmConfig.sp.app.iLogin -iDomain $vmConfig.sp.app.iDomain -iPwd $vmConfig.sp.app.iPwd

    $buildLibExitCode = Run-Process 'powershell' ('-ExecutionPolicy Bypass -File "{0}" "{1}"' -f "$global:rootDir\buildup-spinit.ps1", $vmName) $true $vmConfig.sp.app.iLogin $vmConfig.sp.app.iDomain $vmConfig.sp.app.iPwd $false $null $true -showWindow
         
    if ($buildLibExitCode -eq $global:VM_RECYCLE_REQUIRED) {

      DBGIF ('SPINIT requested recycle and be re-run the next time') { $true }
      $restartRequired = $true

    } else {

      $restartRequired = $true # we ignore other statuses and always restart
      Finish-SubPhase spinit
      $subPhasesDone ++
    }
  }
}



#===================
# Note: "instance" may not be present because the organization name can be specified on a first exchange installation only
DBG ("Should install Exchange server: {0}" -f (Is-ValidString $vmConfig.ex.roles))
DBG ('Will postpone to the next round: {0}' -f $restartRequired)

if ((Is-ValidString $vmConfig.ex.roles) -and (-not (Parse-BoolSafe $vmConfig.ex.dnd)) -and (Do-SubPhase ex)) {

  $subPhasesToDo ++

  if (-not $restartRequired) {

    DBG ("Going to run Exchange library to complete the installation.")
    $buildLibExitCode = Run-Process 'powershell' ('-ExecutionPolicy Bypass -File "{0}" "{1}"' -f "$global:rootDir\buildup-ex.ps1", $vmName) $true $vmConfig.ex.app.iLogin $vmConfig.ex.app.iDomain $vmConfig.ex.app.iPwd $false $null $true -showWindow

    $restartRequired = $true
    Finish-SubPhase ex
    $subPhasesDone ++
  }
}



#=========================
DBG ("Should install DPM: {0}" -f (Is-ValidString $vmConfig.dpm.instance))
DBG ('Will postpone to the next round: {0}' -f $restartRequired)

if ((Is-ValidString $vmConfig.dpm.instance) -and (-not (Parse-BoolSafe $vmConfig.dpm.dnd)) -and (Do-SubPhase dpm)) {

  $subPhasesToDo ++

  if (-not $restartRequired) {

    DBG ("Going to run DPM installation library to complete the installation.")
    $buildLibExitCode = Run-Process 'powershell' ('-ExecutionPolicy Bypass -File "{0}" "{1}"' -f "$global:rootDir\buildup-dpm.ps1", $vmName) $true $vmConfig.dpm.app.iLogin $vmConfig.dpm.app.iDomain $vmConfig.dpm.app.iPwd $false $null $true -showWindow

    Finish-SubPhase dpm
    $subPhasesDone ++
  }
}



#=========================
DBG ("Should install RDP farm: {0}" -f (Is-ValidString $vmConfig.rdp.instance))
DBG ('Will postpone to the next round: {0}' -f $restartRequired)

if ((Is-ValidString $vmConfig.rdp.instance) -and (-not (Parse-BoolSafe $vmConfig.rdp.dnd)) -and (Do-SubPhase rdp)) {

  $subPhasesToDo ++

  if (-not $restartRequired) {

    DBG ("Going to run RDP farm installation library to complete the installation.")
    $buildLibExitCode = Run-Process 'powershell' ('-ExecutionPolicy Bypass -File "{0}" "{1}"' -f "$global:rootDir\buildup-rdp.ps1", $vmName) $true $vmConfig.rdp.app.iLogin $vmConfig.rdp.app.iDomain $vmConfig.rdp.app.iPwd $false $null $true -showWindow

    Finish-SubPhase rdp
    $subPhasesDone ++
  }
}



#=========================
DBG ("Should install SCOM: {0}" -f (Is-ValidString $vmConfig.scom.instance))
DBG ('Will postpone to the next round: {0}' -f $restartRequired)

if ((Is-ValidString $vmConfig.scom.instance) -and (-not (Parse-BoolSafe $vmConfig.scom.dnd)) -and (Do-SubPhase scom)) {

  $subPhasesToDo ++

  if (-not $restartRequired) {

    DBG ("Going to run SCOM installation library to complete the installation.")
    $buildLibExitCode = Run-Process 'powershell' ('-ExecutionPolicy Bypass -File "{0}" "{1}"' -f "$global:rootDir\buildup-scom.ps1", $vmName) $true $vmConfig.scom.app.iLogin $vmConfig.scom.app.iDomain $vmConfig.scom.app.iPwd $false $null $true -showWindow

    Finish-SubPhase scom
    $subPhasesDone ++
  }
}



#===================
DBG ("Should configure RRAS: {0}" -f (Is-ValidString $vmConfig.rras.instance))
DBG ('Will postpone to the next round: {0}' -f $restartRequired)

if ((Is-ValidString $vmConfig.rras.instance) -and (-not (Parse-BoolSafe $vmConfig.rras.dnd)) -and (Do-SubPhase rras)) {

  $subPhasesToDo ++

  if (-not $restartRequired) {

    DBG ("Going to run RRAS configuration library to complete the installation.")

    $buildLibExitCode = Run-Process 'powershell' ('-ExecutionPolicy Bypass -File "{0}" "{1}"' -f "$global:rootDir\buildup-rras.ps1", $vmName) $true $vmConfig.rras.app.iLogin $vmConfig.rras.app.iDomain $vmConfig.rras.app.iPwd $false $null $true -showWindow

    Finish-SubPhase rras
    $subPhasesDone ++
  }
}



#===================
DBG ("Should install TMG: {0}" -f (Is-ValidString $vmConfig.tmg.type))
DBG ('Will postpone to the next round: {0}' -f $restartRequired)

if ((Is-ValidString $vmConfig.tmg.type) -and (-not (Parse-BoolSafe $vmConfig.tmg.dnd)) -and (Do-SubPhase tmg)) {

  $subPhasesToDo ++

  if (-not $restartRequired) {

    DBG ("Going to run TMG library to do the installation.")
    $buildLibExitCode = Run-Process 'powershell' ('-ExecutionPolicy Bypass -File "{0}" "{1}"' -f "$global:rootDir\buildup-tmg.ps1", $vmName) $true $vmConfig.tmg.app.iLogin $vmConfig.tmg.app.iDomain $vmConfig.tmg.app.iPwd $false $null $true -showWindow

    $restartRequired = $true
    Finish-SubPhase tmg
    $subPhasesDone ++
  }
}



#===================
DBG ("Should install VisualStudio: {0}" -f (Is-ValidString $vmConfig.vs.version))
DBG ('Will postpone to the next round: {0}' -f $restartRequired)

if ((Is-ValidString $vmConfig.vs.version) -and (-not (Parse-BoolSafe $vmConfig.vs.dnd)) -and (Do-SubPhase vs)) {

  $subPhasesToDo ++

  if (-not $restartRequired) {

    DBG ("Going to run VisualStudio library to do the installation.")
    $buildLibExitCode = Run-Process 'powershell' ('-ExecutionPolicy Bypass -File "{0}" "{1}"' -f "$global:rootDir\buildup-vs.ps1", $vmName) $true $vmConfig.vs.app.iLogin $vmConfig.vs.app.iDomain $vmConfig.vs.app.iPwd $false $null $true -showWindow

    $restartRequired = $true
    Finish-SubPhase vs
    $subPhasesDone ++
  }
}



#=========================
DBG ("Should install ADFS/WAP: {0}" -f (Is-ValidString $vmConfig.adfs.instance))
DBG ('Will postpone to the next round: {0}' -f $restartRequired)

if ((Is-ValidString $vmConfig.adfs.instance) -and (-not (Parse-BoolSafe $vmConfig.adfs.dnd)) -and (Do-SubPhase adfs)) {

  $subPhasesToDo ++

  if (-not $restartRequired) {

    DBG ("Going to run ADFS library to complete the installation.")
    $buildLibExitCode = Run-Process 'powershell' ('-ExecutionPolicy Bypass -File "{0}" "{1}"' -f "$global:rootDir\buildup-adfs.ps1", $vmName) $true $vmConfig.adfs.app.iLogin $vmConfig.adfs.app.iDomain $vmConfig.adfs.app.iPwd $false $null $true -showWindow

    Finish-SubPhase adfs
    $subPhasesDone ++
  }
}


#===================
DBG ("Should initialize Forefront/Microsoft Identity Manager (FIM/MIM): {0}" -f (Is-ValidString $vmConfig.mim.instance))
DBG ('Will postpone to the next round: {0}' -f $restartRequired)

if ((Is-ValidString $vmConfig.mim.instance) -and (-not (Parse-BoolSafe $vmConfig.mim.dnd)) -and (Do-SubPhase mim)) {

  $subPhasesToDo ++

  if (-not $restartRequired) {

    DBG ("Going to run MIM installation library to complete the installation.")

    $buildLibExitCode = Run-Process 'powershell' ('-ExecutionPolicy Bypass -File "{0}" "{1}"' -f "$global:rootDir\buildup-mim.ps1", $vmName) $true $vmConfig.mim.app.iLogin $vmConfig.mim.app.iDomain $vmConfig.mim.app.iPwd $false $null $true -showWindow
         
    if ($buildLibExitCode -eq $global:VM_RECYCLE_REQUIRED) {

      DBGIF ('MIM requested recycle and be re-run the next time') { $true }
      $restartRequired = $true

    } else {

      $restartRequired = $true # we ignore other statuses and always restart
      Finish-SubPhase mim
      $subPhasesDone ++
    }
  }
}


#=========================
DBG ("Should run nested BUILDER: {0}" -f (Is-ValidString $vmConfig.builder.build))
DBG ('Will postpone to the next round: {0}' -f $restartRequired)

if ((Is-ValidString $vmConfig.builder.build) -and (-not (Parse-BoolSafe $vmConfig.builder.dnd)) -and (Do-SubPhase builder)) {

  $subPhasesToDo ++

  if (-not $restartRequired) {

    DBG ("Going to run nested BUILDER to complete the installation.")
    $buildLibExitCode = Start-NestedBuilder

    if ($buildLibExitCode -eq $global:VM_RECYCLE_REQUIRED) {

      DBG ('AppLibrary requested recycle and be re-run the next time')
      $restartRequired = $true

    } elseif ($buildLibExitCode -eq $global:VM_FINISHED_RESTART_REQUIRED) {
    
      $restartRequired = $true

      Finish-SubPhase builder
      $subPhasesDone ++

    } else {

      $restartRequired = $false

      Finish-SubPhase builder
      $subPhasesDone ++
    }
  }
}


##
ReDisable-SystemRestore
ReDisable-Updates


DBG ('Phases already finished: todo = {0} | done = {1}' -f $subPhasesToDo, $subPhasesDone)
DBGIF $MyInvocation.MyCommand.Name { $subPhasesDone -gt $subPhasesToDo }
if ($subPhasesToDo -eq $subPhasesDone) {

  DBG ('Last phase hit. Do some finalizations')

  
  DBG ('Should we announce our IPv4 addresses to the Hyper-V host by using KVP: {0}' -f (Parse-BoolSafe $vmConfig.wks.ipv4kvp))
  if (Parse-BoolSafe $vmConfig.wks.ipv4kvp) {

    [string[]] $validIPv4sToNotifyHost = (Get-WmiQueryArray '.' $global:wmiFltValidNIC) | ? { Is-NonNull $_.IPAddress } | select -Expand IPAddress | ? { $_ -match $global:rxIPv4 }
    DBGIF $MyInvocation.MyCommand.Name { (Get-CountSafe $validIPv4sToNotifyHost) -lt 1 }

    if ((Get-CountSafe $validIPv4sToNotifyHost) -gt 0) {

      Set-KvpValue 'sevecek-VMB-IPv4' (Format-MultiValue $validIPv4sToNotifyHost)
    }
  }


  if (Is-LocalComputerDomainController) {

    # Note: rather force replication again because RODCs are not 
    #       masaged with change notifications so we rather be on the safe side
    Run-Process 'repadmin' '/SyncAll /e /A'
  }


  DBG ('Should we remove any members of local groups: {0} | {1}' -f (Is-ValidString $vmConfig.wks.removeLocalMbr), $vmConfig.wks.removeLocalMbr)
  if (Is-ValidString $vmConfig.wks.removeLocalMbr) {

    [string[]] $removeLocalMbrs = Split-MultiValue $vmConfig.wks.removeLocalMbr
    DBG ('Local group member removals: #{0} | {1}' -f (Get-CountSafe $removeLocalMbrs), ($removeLocalMbrs -join ' ; '))

    if ((Get-CountSafe $removeLocalMbrs) -gt 0) {

      foreach ($removeLocalMember in $removeLocalMbrs) {

        $localGroup = Get-ValueFlags $removeLocalMember
        $memberToRemove = Strip-ValueFlags $removeLocalMember

        DBG ('Will remove local membership: {0} | {1}' -f $localGroup, $memberToRemove)
        DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $localGroup }
        DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $memberToRemove }

        if ((Is-ValidString $localGroup) -and (Is-ValidString $memberToRemove)) {

          Add-MemberLocalGroup -localGroup $localGroup -memberLogin $memberToRemove -memberDomain $null -removeInstead $true
        }
      }
    }
  }

  
  # Note: this weird check is here due to a PS 2.0 bug which loads
  #       empty XML elements as an empty string and not as an empty XmlElement
  #       So if the XML contains empty  element it is parsed as [string] $vmConfig.wks.GetType() = string
  if ((Is-NonNull $vmConfig.wks) -and ($vmConfig.wks -is [System.Xml.XmlElement])) {

    $prelogons = $vmConfig.wks.SelectNodes('./prelogon')
    DBG ('Should we prelogon some users: {0} | {1}' -f ((Get-CountSafe $prelogons) -gt 0), (Get-CountSafe $prelogons))

    if ((Get-CountSafe $prelogons) -gt 0) {

      [System.Collections.ArrayList] $deList = @()

      foreach ($onePrelogon in $prelogons) {

        DBG ('One prelogon request: {0} | {1} | {2} | {3}' -f $onePrelogon.ou, $onePrelogon.domain, $onePrelogon.filter, $onePrelogon.scope)
        DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $onePrelogon.ou }
        DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $onePrelogon.domain }
        DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $onePrelogon.filter }
        DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $onePrelogon.scope }
        DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $onePrelogon.pwd }

        DBG ('Determine domain DN: {0}' -f $onePrelogon.domain)
        $rootDSE = Get-DE ('LDAP://{0}/RootDSE' -f $onePrelogon.domain) ([ref] $deList)
        $domainDN = GDES $rootDSE defaultNamingContext

        $searchRootDN = '{0},{1}' -f $onePrelogon.ou.Trim(','), $domainDN
        DBG ('Search root DN: {0}' -f $searchRootDN)

        $searchRootDE = Get-DE $searchRootDN ([ref] $deList)
        $adUsersFound = Get-ADSearch $searchRootDE $onePrelogon.scope $onePrelogon.filter @('userPrincipalName')
        DBGIF $MyInvocation.MyCommand.Name { -not $adUsersFound.found }

        if ($adUsersFound.found) {
    
          foreach ($oneAdUserFound in $adUsersFound.result) { 
                
            $oneAdUserUPN = GSRS $oneAdUserFound userPrincipalName
            DBG ('One user to be prelogged: {0}' -f $oneAdUserUPN)

            Prelogon-User $oneAdUserUPN '' $onePrelogon.pwd $true | Out-Null
          }
        }

        Dispose-ADSearch ([ref] $adUsersFound)
      }

      Dispose-List ([ref] $deList)
    }
  }


  DBG ('Check if any other account needs profile customization')
  $accountsWithProfileInit = $vmConfig.SelectNodes('./*/*[(name()="account" or name()="app" or name()="svc") and translate(@loadProfile,"TRUE","true")="true" and translate(@randomDesktop,"TRUE","true")="true"]')
  DBG ('Found accounts with profile customization: {0}' -f (Get-CountSafe $accountsWithProfileInit))
  
  if ((Get-CountSafe $accountsWithProfileInit) -gt 0) {

    foreach ($oneAccountWithProfileInit in $accountsWithProfileInit) {

      if (Is-ValidString $oneAccountWithProfileInit.iLogin) {

        Prelogon-User $oneAccountWithProfileInit.iLogin $oneAccountWithProfileInit.iDomain $oneAccountWithProfileInit.iPwd $true | Out-Null
      }

      if (Is-ValidString $oneAccountWithProfileInit.aLogin) {

        Prelogon-User $oneAccountWithProfileInit.aLogin $oneAccountWithProfileInit.iDomain $oneAccountWithProfileInit.aPwd $true | Out-Null
      }

      if (Is-ValidString $oneAccountWithProfileInit.login) {

        Prelogon-User $oneAccountWithProfileInit.login $oneAccountWithProfileInit.domain $oneAccountWithProfileInit.pwd $true | Out-Null
      }
    }
  }


  DBG ('Check if we installed from ISO and rather rename the autounattended files on A:')
  if ($global:phaseCfg.sevecekBuildup.media.source -eq 'iso') {

    if (Test-Path 'A:\winnt.sif') { 

      DBG ('WINNT.SIF found, disabling')    
      DBGSTART
      Rename-Item 'A:\winnt.sif' 'A:\winnt.sif.disabled' -Force
      DBGER $MyInvocation.MyCommand.Name $error
      DBGEND
    }

    if (Test-Path 'A:\autounattend.xml') { 

      DBG ('AUTOUNATTEND.XML found, disabling')
      DBGSTART    
      Rename-Item 'A:\autounattend.xml' 'A:\autounattend.xml.disabled' -Force
      DBGER $MyInvocation.MyCommand.Name $error
      DBGEND
    }
  }



  DBG ('Custom PowerShell execution policy requested: {0} | {1}' -f (Is-ValidString $vmConfig.commonVM.psPolicy), $vmConfig.commonVM.psPolicy)
  if (Is-ValidString $vmConfig.commonVM.psPolicy) {

    DBG ('Setting PS execution policy: {0}' -f $vmConfig.commonVM.psPolicy)
    DBGSTART
    Set-ExecutionPolicy $vmConfig.commonVM.psPolicy
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND
  }


  if ((Is-LocalComputerMemberOfDomain) -or ($global:thisOSVersionNumber -ge 6)) {

    DBG ('Pulse machine autoenrollment if the CAs are already finished')
    # Note: this can be done on domain members only in case of Windows 2003-
    #       because they do not have the Enrollment Web Service client
    Run-Process certutil '-pulse'
  }



  DBG ('Verify our analytics code at the last phase')
  DBGIF '======= ANALYTIC CODE START =======' { $true }

  $libDir = Split-Path -parent $MyInvocation.MyCommand.Definition
  & "$libDir\lib-analytics.ps1"


  DBG ('Assert NICs just before finishing.')
  Get-BestServerInternalNIC -returnAll $true -saveCsvDebugFile (Get-DataFileApp 'nics-assert' $null '.csv' -randomize $true) | Out-Null

  
  if (Is-LocalComputerDomainController) {

    DBG ('Also assert sensitive domain groups just before finishing')

    [Collections.ArrayList] $deList = @()

    $rootDSE = Get-DE ('LDAP://RootDSE') ([ref] $deList)

    DBG ('Are we running a sub-domain DC here?')
    if ((GDES $rootDSE configurationNamingContext) -ne ('CN=Configuration,{0}' -f (GDES $rootDSE defaultNamingContext))) {

      DBG ('We are a subdomain DC')    
      $SIDstoProcess = @{ 

        'BUILTIN\Administrators' = 'S-1-5-32-544'
        'BUILTIN\Backup Operators' = 'S-1-5-32-551'
        'BUILTIN\Server Operators' = 'S-1-5-32-549'
        'BUILTIN\Account Operators' = 'S-1-5-32-548'
        'BUILTIN\Pre-Windows 2000 Compantible Access' = 'S-1-5-32-554'
        'BUILTIN\Windows Authorization Access Group' = 'S-1-5-32-560'
        "$thisComputerDomainNetBIOS\Domain Admins" = "$thisComputerSID-512"
        "$thisComputerDomainNetBIOS\Domain Computers" = "$thisComputerSID-515"
      }
    
    } else { 

      DBG ('We are a forest root DC')    
      $SIDstoProcess = @{ 

        'BUILTIN\Administrators' = 'S-1-5-32-544'
        'BUILTIN\Backup Operators' = 'S-1-5-32-551'
        'BUILTIN\Server Operators' = 'S-1-5-32-549'
        'BUILTIN\Account Operators' = 'S-1-5-32-548'
        'BUILTIN\Pre-Windows 2000 Compantible Access' = 'S-1-5-32-554'
        'BUILTIN\Windows Authorization Access Group' = 'S-1-5-32-560'
        "$thisComputerDomainNetBIOS\Domain Admins" = "$thisComputerSID-512"
        "$thisComputerDomainNetBIOS\Domain Computers" = "$thisComputerSID-515"
        "$thisComputerDomainNetBIOS\Enterprise Admins" = "$thisComputerSID-519"
        "$thisComputerDomainNetBIOS\Schema Admins" = "$thisComputerSID-518"
      }
    }

    foreach ($oneSIDtoProcess in $SIDstoProcess.Keys) {

      DBG ('One principal to process: {0} | {1}' -f $oneSIDtoProcess, $SIDstoProcess[$oneSIDtoProcess])
      $groupDE = Get-DE ('LDAP://' -f $SIDstoProcess[$oneSIDtoProcess]) ([ref] $deList)
      $groupDN = GDES $groupDE distinguishedName

      Get-GroupMembership $groupDN -maximum 50 | Out-Null
    }


    DBG ('Also check some default user accounts')

    $SIDstoProcess = @{ 

      "$thisComputerDomainNetBIOS\administrator" = "$thisComputerSID-500"
      "$thisComputerDomainNetBIOS\guest" = "$thisComputerSID-501"
      "$thisComputerDomainNetBIOS\krbtgt" = "$thisComputerSID-502"
    }

    foreach ($oneSIDtoProcess in $SIDstoProcess.Keys) {

      DBG ('One principal to process: {0} | {1}' -f $oneSIDtoProcess, $SIDstoProcess[$oneSIDtoProcess])
      $userDE = Get-DE ('LDAP://' -f $SIDstoProcess[$oneSIDtoProcess]) ([ref] $deList)
      $userDN = GDES $userDE distinguishedName

      Get-UserMembership $userDN | Out-Null
    }


    Dispose-List ([ref] $deList)

  } else {

    DBG ('Also assert sensitive local groups just before finishing')
    $SIDstoProcess = @{ 

      'BUILTIN\Administrators' = 'S-1-5-32-544'
      'BUILTIN\Users' = 'S-1-5-32-545'
    }

    foreach ($oneSIDtoProcess in $SIDstoProcess.Keys) {
   
      Get-LocalGroupMembership $SIDstoProcess[$oneSIDtoProcess] $oneSIDtoProcess -doNotTraversDefaults $true -maximum 50 | Out-Null
    }
  }


  DBG ('We can also assert LSA secrets')
  $lsaSecrets = Get-LsaSecrets -name $null -returnAsByteArray $false -forceMachineSecretsDecryptionByKeyCopyOperation $true
  $machineAccPwd = $lsaSecrets | ? { $_.Secret -eq '$MACHINE.ACC' } | Select -Expand 'String'
  # Note: US ASCII examples
  #       0x20 = space
  #       0x21 = !
  #       0x7E = ~
  #       0x7F = DEL
  DBGIF ('Weird $MACHINE.ACC password: {0}' -f $machineAccPwd) { (Is-LocalComputerMemberOfDomain) -and (-not (Is-LocalComputerDomainController)) -and ($machineAccPwd -notmatch '\A[\x20-\x7E]{120}\Z') }


  DBG ('We also verify our audit policy code')
  $auditPolicies = Get-AuditPolicy
  DBG ('Current audit policy settings from API ADVAPI32: {0}' -f ($auditPolicies | Out-String))

  if ($global:thisOSVersionNumber -ge 6) {

    Run-Process 'auditpol' '/get /category:*'
    Run-Process 'auditpol' '/list /subcategory:* /v'
  }


  DBG ('Check USER certificates and private keys as well')
  # Note: there is a weird store on Vista called "Active Directory User Object"
  #       for which the Get-ChildItem (dir) command fails with "The system cannot open the device or file specified"
  #       Thus I had to make the enumeration a bit more sensitive
  Assert-CertificatesInStores CurrentUser


  DBG ('Check SYSTEM certificates and private keys as well')
  Assert-CertificatesInStores LocalMachine


  
  DBG ('Only verify our code for IE version detection here')
  [void] (Get-IEVersion)


  if ((Is-LocalComputerDomainController) -or (Is-ValidString $vmConfig.ex.roles)) {

    DBG ('Verify our OID BER transformation logic')
    # Note: this takes about 25 minutes to complete, no need to spend the time
    #Verify-OidEncoding
  }


  DBG ('What if the SVCHOST got corrupted sometime later than phase 5')
  Verify-SvchostServiceRegistrations -fix $false


  DBG ('Verify PATH environment variable contents. There were some issues with Ex2013 corrupting it')
  DBGSTART
  $envPATHvalueToBeValidated = [System.Environment]::GetEnvironmentVariable('PATH', 'MACHINE')
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND
  DBG ('Current PATH value: {0}' -f $envPATHvalueToBeValidated)
  $envPATHvalueToBeValidatedTokens = $envPATHvalueToBeValidated.Split(';')
  [Collections.ArrayList] $envPATHvalueToBeValidatedTokensProcessed = @()
  foreach ($oneEnvPATHvalueToBeValidatedToken in $envPATHvalueToBeValidatedTokens) {

    DBGIF ('Weird PATH token found: "{0}"' -f $oneEnvPATHvalueToBeValidatedToken) { $oneEnvPATHvalueToBeValidatedToken.Trim() -ne $oneEnvPATHvalueToBeValidatedToken }
    DBGIF ('Weird PATH token found: "{0}"' -f $oneEnvPATHvalueToBeValidatedToken) { Is-EmptyString $oneEnvPATHvalueToBeValidatedToken }
    DBGIF ('Weird PATH token found: "{0}"' -f $oneEnvPATHvalueToBeValidatedToken) { -not (Test-Path -Literal $oneEnvPATHvalueToBeValidatedToken) }
    DBGIF ('Duplicate PATH token found: "{0}"' -f $oneEnvPATHvalueToBeValidatedToken) { Contains-Safe $envPATHvalueToBeValidatedTokensProcessed $oneEnvPATHvalueToBeValidatedToken }

    [void] $envPATHvalueToBeValidatedTokensProcessed.Add($oneEnvPATHvalueToBeValidatedToken)
  }

  
  Detect-NetFxVersions | Out-Null


  DBGIF '======== ANALYTIC CODE END ========' { $true }

  DBGSTART; DBGEND
}


DBGIF $MyInvocation.MyCommand.Name { $subPhasesToDo -lt $subPhasesDone }

$global:restartCurrentPhase = ($subPhasesToDo -gt $subPhasesDone)
$global:proceedWithoutRestart = ($subPhasesToDo -eq 0)





# SIG # Begin signature block
# MIIYMAYJKoZIhvcNAQcCoIIYITCCGB0CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCChn1Fs2yO6focI
# Nhn9DvrZAZf6wMOmFxJssHPOzsIFO6CCE0cwggYEMIID7KADAgECAgoqHIRwAAEA
# 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
# DAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgBRxHEKWnwKoCGCLvnWlv203m
# JSi0yfkJQfa51MH2GhAwDQYJKoZIhvcNAQEBBQAEggEAjmTkx9BeMIoJWt0d39Eq
# /sTCVATV++ppBxFK6QsFuTP/17IwfkBpY6EwwydlV7py5OJtWx4GdfzqP5dOKhiX
# 1hQBGn7/mrn40Nwg/NYUxC6cwpag/OEDHE/qPbGMDVg6WZ64g0BBX46UWejF2OR0
# 8SucoLhNg5ejwQK3bMlGNNcAlDHVGrYIYUuIEWsnKnQyU15GA1Hr78YoN57Ooo2M
# GAAoNlXqMDs0595wzf5wZPXXr1avbz8fEzf8nuQ4SGoqTlbrzrhVXMCUM/vVMroH
# owze0B04pLLnRmBaKJnD9O5XibEsWPDfK/Wnho+Rx/P/tfEyFbFVNSB0wXOahriC
# t6GCAg8wggILBgkqhkiG9w0BCQYxggH8MIIB+AIBATB2MGIxCzAJBgNVBAYTAlVT
# MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
# b20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMQIQAwGaAjr/WLFr
# 1tXq5hfwZjAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAc
# BgkqhkiG9w0BCQUxDxcNMTkwOTIxMDgxNDA1WjAjBgkqhkiG9w0BCQQxFgQUH1n5
# HTcfjkg9mwci495HZnnRuZ4wDQYJKoZIhvcNAQEBBQAEggEAE2JrG2R2T6Bx+2sF
# gnN3FSTmTpquo+CUTpnd5jqiEeQqPzgf/ePBcn2IxTpTu69KYbkz7bRZBsmHbRXo
# AesYoSDg8/CogqrvTm2y4UKpbAiiPWT3KFXbYFi4e00UAQIKWC/1b0wFjKCl4ehl
# VVLro8kdD6BqBSh4Ghufnk44wyljj98AR9ny0R0kNM+Y/sI5gA7bgF5SSwchfO7n
# vSNSFGHnHunmvGI462hV08H5JFdxAQpzGkjVGuzIgRL9gKIVRalo8IhNrQrcsDXV
# yIxHy80IevqnvX1CNPIJzn2x/U0qW4Yrf1SD1fE4Mcli80kudLLFE0RvUu07dO3y
# pSPdgw==
# SIG # End signature block