ADLAB PowerShell source file: buildup-ex.ps1

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



$global:outClass = 'main'

$libDir = Split-Path -parent $MyInvocation.MyCommand.Definition
& "$libDir\lib-common.ps1" -defaultConfig -rootDir $libDir -outFile exLib
& "$libDir\lib-modifyActions.ps1"
& "$libDir\lib-buildup.ps1"

$vmName = $args[0]

DBG ("Exchange Installation library")
Redirect-TempToOutput
Load-VMConfig

Find-MarkedVolumes
$global:installSourceMediaEX = '!sevecek-installation-source-media-ExchangeSharePoint.txt'
$global:installMediaVolumeEX = Find-MarkedVolume $global:installSourceMediaEX 3
DBG ('Exchange install media volume: {0}' -f $installMediaVolumeEX)

$appTag = 'ex'
$appConfig = $vmConfig.$appTag
$firstAppHost = Get-FirstAppHostInInstance $appTag $appConfig.instance 'waitParams'
$firstAppHostInInstance = Check-FirstAppHostInInstance $appTag $appConfig.instance


#====================
#====================


#====================
DBG ('Installing Exchange.')

$exVersion = $appConfig.version
if (Is-EmptyString $exVersion) {

  $exVersion = '2010'
}

DBG ('Exchange version requested: {0}' -f $exVersion)

$instRoot = Get-FirstPathFromWildcard (Join-Path $installMediaVolumeEX ('ExchangeServer{0}-*' -f $exVersion))
$customRoot = Join-Path $global:rootDir ('ExchangeServer{0}' -f $exVersion)


#====================
DBG ('Basic prerequisites')



DBG ('Exchange requires the Net.Tcp Port Sharing Service (NetTcpPortSharing) to be started automatically')

$netTcpPortSharing = Get-WmiQuerySingleObject '.' 'SELECT * FROM Win32_Service WHERE Name = "NetTcpPortSharing"'
DBGIF $MyInvocation.MyCommand.Name { Is-Null $netTcpPortSharing }

if ($netTcpPortSharing.StartMode -ne 'Auto') {

  DBG ('Service Net.Tcp Port Sharing Service (NetTcpPortSharing) is not set tu Automatic startup. Set Auto.')
  Run-Process 'sc' 'config NetTcpPortSharing start= auto'
}

if ($netTcpPortSharing.State -ne 'Running') {

  DBG ('Service Net.Tcp Port Sharing Service (NetTcpPortSharing) is not running. Start it.')
  Start-Service NetTcpPortSharing -EA SilentlyContinue -EV er
  DBGER $MyInvocation.MyCommand.Name $er
}


#====================
DBG ('Preinstall Office 2010 filter pack: {0}' -f ($exVersion -eq '2010'))

if ($exVersion -eq '2010') {

  Run-Process (Join-Path $instRoot '!SevecekBuildup\FilterPack2010-x64-en-us.exe') '/quiet /passive /norestart'
  Run-Process (Join-Path $instRoot '!SevecekBuildup\filterpack2010sp1-kb2460041-x64-fullfile-en-us.exe') '/quiet /passive /norestart'
}


#====================
DBG ('Preinstall UCMA (Unified Communications Managed API) Runtime 4.0: {0}' -f ($exVersion -eq '2013'))

if ($exVersion -eq '2013') {

  Run-Process (Join-Path $instRoot '!SevecekBuildup\UcmaRuntimeSetup40.exe') '/passive /norestart'
}



#================  
if ($exVersion -eq '2010') {

  $setupFile = 'setup.com'
 
} else {

  $setupFile = 'setup.exe'
}
  


# Note: try autoenrolling certificate yet before installing exchange
#       so that it might use it directly itself instead of generating self-signed cert
#       Although note that the CA might not be yet finished at this point
#       To be sure the certificate is set correctly, we would have to use DTR phase
#       but that would be too much effort for the current really utility Exchange installs
Run-Process 'certutil' '-pulse'


[string] $commonSetupParams = '/CustomerFeedbackEnabled:False'

if (Is-ValidString $appConfig.mdbName) {

  DBG ('Custom MDB name: {0}' -f $appConfig.mdbName)
  $commonSetupParams = '{0} /MdbName:"{1}"' -f $commonSetupParams, $appConfig.mdbName
}


[string] $exInstallRoot = 'C:\Program Files\Microsoft\Exchange Server\V15'

if (Is-ValidString $appConfig.binDir) {

  $customDir = Resolve-VolumePath $appConfig.binDir
  
  DBG ('Non-default installation dir: {0} | {1}' -f $appConfig.binDir, $customDir)
  $commonSetupParams = '{0} /TargetDir:"{1}"' -f $commonSetupParams, $customDir

  $exInstallRoot = $customDir
}

DBG ('Installation binaries root path established: {0}' -f $exInstallRoot)


if (Is-ValidString $appConfig.dbDir) {

  $customDir = Resolve-VolumePath $appConfig.dbDir

  if (Is-ValidString $appConfig.mdbName) {

    $customDir = Join-Path $customDir $appConfig.mdbName
    $customDir = Join-Path $customDir ([IO.Path]::ChangeExtension($appConfig.mdbName, 'edb'))

  } else {

    DBGIF ('Default MDB name not specified with custom DB path') { $true }
    $customDir = Join-Path $customDir 'DefaultMdb'
    $customDir = Join-Path $customDir ([IO.Path]::ChangeExtension('DefaultMdb', 'edb'))
  }

  DBG ('Non-default database dir: {0} | {1}' -f $appConfig.dbDir, $customDir)
  $commonSetupParams = '{0} /DbFilePath:"{1}"' -f $commonSetupParams, $customDir
}

if (Is-ValidString $appConfig.logDir) {

  $customDir = Resolve-VolumePath $appConfig.logDir

  DBG ('Non-default log dir: {0} | {1}' -f $appConfig.logDir, $customDir)
  $commonSetupParams = '{0} /LogFolderPath:"{1}"' -f $commonSetupParams, $customDir
}

DBG ('Common setup params resolved: {0}' -f $commonSetupParams)


# Note: Exchange 2013 setup corrupts %PATH% environment variable in such a way that it prepends an empty path "; ;"
#       we assert first that the PATH gets really corrupted only by the setup
DBGIF ('Original PATH environment variable: {0}' -f $env:Path) { $env:Path -like '; ;*' }


DBG ('EXSETUP to be run. Give it some 37 seconds to calm down.')
Start-Sleep -Seconds 37

if ($firstAppHostInInstance) {

  DBG ('Install first Exchange and organization: {0} | {1}' -f $appConfig.roles, $appConfig.instance)

  if ($exVersion -eq '2010') {
  
    if ($appConfig.roles -like '*CA*') {

      Run-Process (Join-Path $instRoot $setupFile) ('/mode:Install /roles:"{0}" /InstallWindowsComponents /OrganizationName:"{1}" /NoSelfSignedCertificates {2}' -f $appConfig.roles, $appConfig.instance, $commonSetupParams)

    } else {
    
      Run-Process (Join-Path $instRoot $setupFile) ('/mode:Install /roles:"{0}" /InstallWindowsComponents /OrganizationName:"{1}" {2}' -f $appConfig.roles, $appConfig.instance, $commonSetupParams)
    }
  
  } else {

    Run-Process (Join-Path $instRoot $setupFile) ('/mode:Install /roles:"{0}" /InstallWindowsComponents /OrganizationName:"{1}" /IAcceptExchangeServerLicenseTerms {2}' -f $appConfig.roles, $appConfig.instance, $commonSetupParams)
  }

} else {

  DBG ('Install additional Exchange into an existing organization: {0} | {1}' -f $appConfig.roles, $appConfig.instance)

  DBG ('Wait until the first exchange is finished: {0}' -f $firstAppHost)
  Wait-Machine $firstAppHost

  if ($exVersion -eq '2010') {
  
    if ($appConfig.roles -like '*CA*') {

      Run-Process (Join-Path $instRoot $setupFile) ('/mode:Install /roles:"{0}" /InstallWindowsComponents /NoSelfSignedCertificates {1}' -f $appConfig.roles, $commonSetupParams)

    } else {
    
      Run-Process (Join-Path $instRoot $setupFile) ('/mode:Install /roles:"{0}" /InstallWindowsComponents {1}' -f $appConfig.roles, $commonSetupParams)
    }

  } else {

    Run-Process (Join-Path $instRoot $setupFile) ('/mode:Install /roles:"{0}" /InstallWindowsComponents /IAcceptExchangeServerLicenseTerms {1}' -f $appConfig.roles, $commonSetupParams)
  }
}


DBG ('EXSETUP finished. Give it some 167 seconds to calm down.')
Start-Sleep -Seconds 167



# Note: yes, the GetEnvironmentVariable correctly returns the real system PATH value
#       not just the currently running process PATH variable as was defined when process started
DBG ('Verify if PATH env variable is not corrupted by the installator')
DBGSTART
$corruptedEnvPath = [System.Environment]::GetEnvironmentVariable('PATH', 'Machine')
DBGER $MyInvocation.MyCommand.Name $error
DBGEND
DBG ('Current PATH environment variable value: {0}' -f $corruptedEnvPath)
DBGIF $MyInvocation.MyCommand.Name { ($exVersion -ne '2013') -and ($corruptedEnvPath -like '; ;*') }

if ($corruptedEnvPath -like '; ;*') {

  # Note: Exchange 2013 setup corrupts %PATH% environment variable in such a way that it prepends an empty path "; ;"
  Set-EnvironmentVariable -variable PATH -value $corruptedEnvPath.SubString(3) -machineOrUser Machine
}



#================
DBG ('Delegate administration to EX Admins')

[System.Collections.ArrayList] $deList = @()
$rootDSE = Get-DE 'RootDSE' ([ref] $deList)
$domainDN = GDES $rootDSE defaultNamingContext
$domainDE = Get-DE $domainDN ([ref] $deList)
                                          
Add-GroupMember 'Organization Management' $domainDN $appConfig.app.aGroup


#================
DBG ('Give it some 3 minutes to initialize at ease')
Start-Sleep 180


#================
DBG ('Load Exchange Management Snap-In')

Load-ExManagementShell $exVersion
Assert-ExPSSession


#================
DBG ('Authoritative domains: {0}' -f (Is-ValidString $appConfig.domains))

if (Is-ValidString $appConfig.domains) {

  $mailDomains = Split-MultiValue $appConfig.domains
  DBG ('Authoritative domains: {0}x = {1}' -f (Get-CountSafe $mailDomains), $appConfig.domains)

  $mailDomains | % {

    $oneDomainAndFlags = $_
    $oneDomain = Strip-ValueFlags $oneDomainAndFlags

    if (Has-ValueFlags $oneDomainAndFlags R) {
    
      DBG ('Remove accepted mail domain: {0}' -f $oneDomain)
      Remove-AcceptedDomain -Identity $oneDomain -Confirm:$false -EV er -EA SilentlyContinue
      DBGER $MyInvocation.MyCommand.Name $er
    }
    
    else {
    
      DBG ('New accepted mail domain: {0}' -f $oneDomain)
      New-AcceptedDomain -Name $oneDomain -DomainName $oneDomain -DomainType Authoritative -EV er -EA SilentlyContinue
      DBGER $MyInvocation.MyCommand.Name $er
    }

    if (Has-ValueFlags $oneDomainAndFlags D) {

      Set-AcceptedDomain -Identity $oneDomain -MakeDefault:$true -EV er -EA SilentlyContinue
      DBGER $MyInvocation.MyCommand.Name $er
      
      Set-EmailAddressPolicy -Identity "Default Policy" -EnabledEmailAddressTemplates "SMTP:@$oneDomain" -EV er -EA SilentlyContinue
      DBGER $MyInvocation.MyCommand.Name $er
      
      Update-EmailAddressPolicy -Identity "Default Policy" -EV er -EA SilentlyContinue
      DBGER $MyInvocation.MyCommand.Name $er
    }
  }
  
  # Note: this is here to just remember how to find all mail suffixes
  #Find-AttributeWildcard 'dc=gopas,dc=virtual' subTree '(proxyAddresses=*)' '*@gopas.virtual'
  #Find-AttributeWildcard 'cn=configuration,dc=gopas,dc=virtual' subTree '(proxyAddresses=*)' '*@gopas.virtual'
}


DBG ('Should remove mailbox from the installer account: {0}' -f (Parse-BoolSafe $appConfig.removeInstaller))

if (Parse-BoolSafe $appConfig.removeInstaller) {

  $iUserUPN = '{0}@{1}' -f $appConfig.app.iLogin, $appConfig.app.iDomain
  DBG ('Remove mailbox from: {0}' -f $iUserUPN)

  # Note: the installer account is member of Enterprise Admins and Schema Admins
  #       and as such is protected with AdminSDHolder
  #       so we need to give it Exchange Trusted Subsystem full control or something

  $iUser = Find-DE $domainDE 'userPrincipalName' $iUserUPN '(objectClass=user)' ([ref] $deList)
  Run-Process 'DSACLS' ('"{0}" /G "Exchange Windows Permissions:GA"' -f (GDES $iUser distinguishedName))
 
  DBG ('Calling Disable-Mailbox for: {0}' -f $iUserUPN)
  Disable-Mailbox -Identity $iUserUPN -Confirm:$false -EV er -EA SilentlyContinue
  DBGER $MyInvocation.MyCommand.Name $er

  # Note: it is not necessary to return the permissions to its original value,
  #       AdminSDHolder will take care himself
}


#==============
if ($exVersion -eq '2010') {

  DBG ('Repair Exchange Management Shell shortcut on Exchange 2010')
  Copy-Item -Path (Join-Path $customRoot 'Exchange Management Shell.lnk') -Destination "$env:ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Exchange Server 2010" -Force -EV er -EA SilentlyContinue
  DBGER $MyInvocation.MyCommand.Name $er
}


#==============
DBG ('Mailboxes: {0}' -f (Get-CountSafe $appConfig.mbx))

$appConfig.mbx | % {

  DBG ('Mailbox search criteria: {0} | {1}' -f $_.root, $_.filter)
  
  (DSQUERY '*' ('{0},{1}' -f $_.root, $domainDN) '-filter' ('{0}' -f $_.filter) '-limit' '0') | % { 
  
    $oneUser = $_.Trim('"')

    DBG ('Mailbox enable: {0}' -f $oneUser)
    DBGSTART
    $theEnabledMailbox = $null
    $theEnabledMailbox = Enable-Mailbox -Identity $oneUser -EA SilentlyContinue
    
    if ($error.Count -gt 0) {

      DBGIF ('Error occured. Debug PSSession: {0}' -f $exPSSession.State) { $true }
    }
    
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND

    DBG ('Enabled mailbox: db = {0} | who = {1} | new = {2}' -f $theEnabledMailbox.Database, $theEnabledMailbox.PrimarySmtpAddress, $theEnabledMailbox.WhenMailboxCreated)
  }
}


#===============
DBG ('Management roles')
$mgmtRoles = $appConfig.SelectNodes('./mgmtRole')
DBG ('Management roles requested: {0}' -f (Get-CountSafe $mgmtRoles))

if ((Get-CountSafe $mgmtRoles) -gt 0) {
 
  foreach ($oneMgmtRole in $mgmtRoles) {

    DBG ('Going to assign a management role: {0} | {1} | {2}' -f $oneMgmtRole.role, $oneMgmtRole.user, $oneMgmtRole.group)
     
    if (Is-ValidString $oneMgmtRole.user) {

      DBG ('Assign role to user: {0}' -f $oneMgmtRole.user)
      DBGSTART
      [void] (New-ManagementRoleAssignment ?Role $oneMgmtRole.role ?User $oneMgmtRole.user -EA SilentlyContinue)
      DBGER $MyInvocation.MyCommand.Name $error
      DBGEND
    }

    if (Is-ValidString $oneMgmtRole.group) {

      DBG ('Assign role to group: {0}' -f $oneMgmtRole.group)
      DBGSTART
      [void] (New-ManagementRoleAssignment ?Role $oneMgmtRole.role ?SecurityGroup $oneMgmtRole.group -EA SilentlyContinue)
      DBGER $MyInvocation.MyCommand.Name $error
      DBGEND
    }
  }
}



DBG ('Check if anybody needs any services from us')
$exSvcs = $xmlConfig.SelectNodes('./VMs/MACHINE[vm/@do="true"]/*/ex[translate(@instance,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")="{0}"]' -f $appConfig.instance.ToLower())
DBG ('Application request exchange services: {0}' -f (Get-CountSafe $exSvcs))

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

  foreach ($oneExSvc in $exSvcs) {

    DBG ('One exchange service requested: {0} | {1}' -f $oneExSvc.acceptedDomain, $oneExSvc.sendConnector)
    
    if (Is-ValidString $oneExSvc.acceptedDomain) {

      $oneAcceptedDomain = Strip-ValueFlags $oneExSvc.acceptedDomain
      $oneAcceptedDomainFlags = Get-ValueFlags $oneExSvc.acceptedDomain

      $oneAcceptedDomainType = 'Authoritative'

      if ($oneAcceptedDomainFlags -eq 'A') { $oneAcceptedDomainType = 'Authoritative' }
      if ($oneAcceptedDomainFlags -eq 'I') { $oneAcceptedDomainType = 'InternalRelay' }
      if ($oneAcceptedDomainFlags -eq 'E') { $oneAcceptedDomainType = 'ExternalRelay' }

      DBG ('Define one accepted domain: {0} | {1}' -f $oneAcceptedDomain, $oneAcceptedDomainType)
      DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $oneAcceptedDomain }
      DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $oneAcceptedDomainType }
      DBGSTART
      New-AcceptedDomain -Name $oneAcceptedDomain -DomainName $oneAcceptedDomain -DomainType $oneAcceptedDomainType
      DBGER $MyInvocation.MyCommand.Name $error
      DBGEND
    }

    if (Is-ValidString $oneExSvc.receiveConnector) {

      $oneReceiveConnectorParams = Split-MultiValue $oneExSvc.receiveConnector

      $oneReceiveConnectorName = $oneReceiveConnectorParams[0]
      $oneReceiveConnectorRemoteIPs = $oneReceiveConnectorParams[1]
      $oneReceiveConnectorPermissionGroups = $oneReceiveConnectorParams[2]

      [System.Collections.ArrayList] $oneReceiveConnectorRemoteIPsArray = @()
      ((Strip-ValueFlags $oneReceiveConnectorRemoteIPs).Split(',')) | ? { Is-ValidString $_.Trim() } | % { [void] $oneReceiveConnectorRemoteIPsArray.Add($_) }

      if (Has-ValueFlags $oneReceiveConnectorRemoteIPs S) {

        DBG ('We should add self IPs in the list of remote IP ranges')
        foreach ($oneExSvcIP in (Split-MultiValue (Get-MachineFromElement $oneExSvc).net.ip)) {

          if (Is-NonNull $oneExSvcIP) {

            DBGIF ('Weird self IP address requested: {0}' -f $oneExSvcIP) { -not (Is-IPv4OrIPv6Address $oneExSvcIP) }
            if (Is-IPv4OrIPv6Address $oneExSvcIP) {

              [void] $oneReceiveConnectorRemoteIPsArray.Add($oneExSvcIP)
            }
          }
        }
      }

      DBG ('Define the new receive connector: {0} | {1}' -f $oneReceiveConnectorName, ($oneReceiveConnectorRemoteIPsArray -join ','))
      DBGSTART
      $newReceiveConnector = $null
      $newReceiveConnector = New-ReceiveConnector -Name $oneReceiveConnectorName -Usage Custom -Bindings '0.0.0.0:25' -RemoteIPRanges $oneReceiveConnectorRemoteIPsArray
      DBGER $MyInvocation.MyCommand.Name $error
      DBGEND
      DBGIF $MyInvocation.MyCommand.Name { Is-Null $newReceiveConnector }

      if (Is-NonNull $newReceiveConnector) {

        DBG ('Add permission groups to the connector')
        DBGSTART
        Set-ReceiveConnector -Identity $newReceiveConnector -PermissionGroups $oneReceiveConnectorPermissionGroups
        DBGER $MyInvocation.MyCommand.Name $error
        DBGEND
      }
    }


    if (Is-ValidString $oneExSvc.sendConnector) {

      $oneSendConnectorParams = Split-MultiValue $oneExSvc.sendConnector

      $oneSendConnectorDomain = $oneSendConnectorParams[0]
      $oneSendConnectorRemoteFlags = Get-ValueFlags $oneSendConnectorParams[1]
      $oneSendConnectorRemoteName = Strip-ValueFlags $oneSendConnectorParams[1]

      DBG ('Define one send connector: {0} | {1} | {2}' -f $oneSendConnectorDomain, $oneSendConnectorRemoteFlags, $oneSendConnectorRemoteName)
      DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $oneSendConnectorDomain }
      DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $oneSendConnectorRemoteFlags }
      DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $oneSendConnectorRemoteName }

      if ($oneSendConnectorRemoteFlags -eq 'D') {

        DBG ('Define DNS based send connector')
        DBGSTART
        # Note: we cannot combine more than a single parameter of -Custom, -Internet, -Internal, -Partner
        #       even thoug we could use the :$false attitude, the API does not allow for more then one of the parameters
        #       to be explicitly specified
        New-SendConnector -Name $oneSendConnectorDomain -AddressSpaces $oneSendConnectorDomain -DNSRoutingEnabled $true -Custom -MaxMessageSize unlimited
        DBGER $MyInvocation.MyCommand.Name $error
        DBGEND
      }

      if ($oneSendConnectorRemoteFlags -eq 'S') {

        if ($oneSendConnectorRemoteName -eq $global:emptyValueMarker) {

          $oneSendConnectorSmartHost = Get-MachineFQDN $oneExSvc

        } else {

          $oneSendConnectorSmartHost = $oneSendConnectorRemoteName
        }

        DBG ('Define smart host based send connector: {0}' -f $oneSendConnectorSmartHost)
        DBGSTART
        # Note: we cannot combine more than a single parameter of -Custom, -Internet, -Internal, -Partner
        #       even thoug we could use the :$false attitude, the API does not allow for more then one of the parameters
        #       to be explicitly specified
        New-SendConnector -Name $oneSendConnectorDomain -AddressSpaces $oneSendConnectorDomain -DNSRoutingEnabled $false -SmartHosts $oneSendConnectorSmartHost -Custom -MaxMessageSize unlimited
        DBGER $MyInvocation.MyCommand.Name $error
        DBGEND
      }
    }
  }

}


DBG ('Fix the New-PerfCounters bug on 2013 https://support.microsoft.com/en-us/kb/2870416: {0}' -f ($exVersion -eq '2013'))
if ($exVersion -eq '2013') {

  Assert-SnapInSafe Microsoft.Exchange.Management.PowerShell.Setup

  $perfCounterSetupPath = Join-Path $exInstallRoot 'Setup\Perf'
  DBGIF ('The performance counters setup path does not exist: {0}' -f $perfCounterSetupPath) { -not (Test-Path $perfCounterSetupPath) }
  
  $perfCounterSetupDefs = Get-ChildItem -Recurse $perfCounterSetupPath | ? { $_.Extension -eq '.xml' }
  DBGIF $MyInvocation.MyCommand.Name { (Get-CountSafe $perfCounterSetupDefs) -lt 10 }
 
  [int] $perfSetupItemCounter = 0;

  foreach ($onePerfCounterSetupDef in $perfCounterSetupDefs)
  {
     $perfSetupItemCounter ++
 
     if ($onePerfCounterSetupDef.Name -eq 'InfoWorkerMultiMailboxSearchPerformanceCounters.xml') {

       DBG ('Buggy performance counter definition file found. Will correct: {0}' -f $onePerfCounterSetupDef.FullName)
       DBGSTART
       $repairXML = [XML] (cat $onePerfCounterSetupDef.FullName)
       DBGER $MyInvocation.MyCommand.Name $error
       DBGEND

       [int] $buggyCountersCount = 0
       foreach ($oneCounter in $repairXML.Category.Counters.Counter) {

         if ($oneCounter.Name.Trim() -ne $oneCounter.Name) {

           DBG ('Correcting one buggy counter name: "{0}" | {1}' -f $oneCounter.Name, $oneCounter.Type)
           $oneCounter.Name = $oneCounter.Name.Trim()

           $buggyCountersCount ++
         }
       }

       DBG ('Counter names corrected: {0}' -f $buggyCountersCount)
       DBGIF $MyInvocation.MyCommand.Name { $buggyCountersCount -lt 1 }

       if ($buggyCountersCount -gt 0) {

         DBG ('Save the correctected XML back into the original file: {0}' -f $onePerfCounterSetupDef.FullName)
         DBGSTART
         $repairXML.Save($onePerfCounterSetupDef.FullName)
         DBGER $MyInvocation.MyCommand.Name $error
         DBGEND
       }
     }

     DBG ('Registering performance counters: # = {0} | {1}' -f $perfSetupItemCounter, $onePerfCounterSetupDef.FullName)
     DBGSTART
     New-PerfCounters -DefinitionFileName $onePerfCounterSetupDef.FullName
     DBGER $MyInvocation.MyCommand.Name $error
     DBGEND
  }
}


DBG ('Should we customize the local computer CAS settings: {0}' -f (Is-ValidString $appConfig.cas.publicDnsName))
if (Is-ValidString $appConfig.cas.publicDnsName) {

  $publicDnsName = $appConfig.cas.publicDnsName
  DBG ('Going to set the public DNS name for the virtual directories: {0}' -f $publicDnsName)

  DBG ('ExternalUrl for OWA')
  DBGSTART
  Get-OwaVirtualDirectory -Server $global:thisComputerHost | Set-OwaVirtualDirectory -ExternalUrl "https://$publicDnsName/owa"
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND

  DBG ('ExternalUrl for ECP')
  DBGSTART
  Get-EcpVirtualDirectory -Server $global:thisComputerHost | Set-EcpVirtualDirectory -ExternalUrl "https://$publicDnsName/ecp"
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND

  DBG ('ExternalUrl for OAB')
  DBGSTART
  Get-OabVirtualDirectory -Server $global:thisComputerHost | Set-OabVirtualDirectory -ExternalUrl "https://$publicDnsName/OAB"
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND

  DBG ('ExternalUrl for EWS')
  DBGSTART
  Get-WebServicesVirtualDirectory -Server $global:thisComputerHost | Set-WebServicesVirtualDirectory -ExternalUrl "https://$publicDnsName/ews/exchange.asmx"
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND

  DBG ('ExternalUrl for ActiveSync')
  DBGSTART
  Get-ActiveSyncVirtualDirectory -Server $global:thisComputerHost | Set-ActiveSyncVirtualDirectory -ExternalUrl "https://$publicDnsName/Microsoft-Server-ActiveSync"
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND

  DBG ('ExternalUrl for MAPI')
  DBGSTART
  Get-MapiVirtualDirectory -Server $global:thisComputerHost | % { Set-MapiVirtualDirectory -Identity $_ -ExternalUrl https://$publicDnsName/mapi -IISAuthenticationMethods $_.IIsAuthenticationMethods }
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND

  DBG ('ExternalUrl for Anywhere')
  DBGSTART
  Get-OutlookAnywhere -Server $global:thisComputerHost | % { Set-OutlookAnywhere -Identity $_ -ExternalHostname $publicDnsName -ExternalClientsRequireSsl $true -ExternalClientAuthenticationMethod $_.ExternalClientAuthenticationMethod }
  DBGER $MyInvocation.MyCommand.Name $error
  DBGEND

  DBG ('Should we create and apply a new CAS certificate for public access: {0}' -f (Is-ValidString $appConfig.cas.publicCertDomain))
  if (Is-ValidString $appConfig.cas.publicCertDomain) {

    $publicCertDomain = $appConfig.cas.publicCertDomain

    DBG ('Get the app certificate parameters and create one')
    [System.Xml.XmlElement] $publicCertDef = $appConfig.cas.SelectSingleNode('./cert/one[@appTag="cas"]')
    [string] $publicCertThumbprint = Enroll-AppCertificate $publicCertDef
    DBGIF $MyInvocation.MyCommand.Name { Is-EmptyString $publicCertThumbprint }

    if (Is-ValidString $publicCertThumbprint) {

      DBG ('Configure the created TLS certificate for use with IIS,POP and IMAP: {0}' -f $publicCertThumbprint)
      DBGSTART
      Enable-ExchangeCertificate -Server $global:thisComputerHost -Thumbprint $publicCertThumbprint -Services IMAP,POP,IIS -Force
      DBGER $MyInvocation.MyCommand.Name $error
      DBGEND
    }

    DBG ('Apply the public certificate principal name to the outlook anywhere provider: {0}' -f $publicCertDomain)
    DBGSTART
    Set-OutlookProvider -Identity EXPR -CertPrincipalName "msstd:$publicCertDomain"
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND
  }

  DBG ('Should we enable Kerberos Constrained Delegation to be used from external sources: {0}' -f (Parse-BoolSafe $appConfig.cas.externalAuthKCD))
  if (Parse-BoolSafe $appConfig.cas.externalAuthKCD) {

    DBG ('Configure the external authentication methods to enable KCD')
    DBGSTART
    Get-OwaVirtualDirectory -Server $global:thisComputerHost | Set-OwaVirtualDirectory -ExternalAuthenticationMethods Negotiate
    Get-EcpVirtualDirectory -Server $global:thisComputerHost | Set-EcpVirtualDirectory -ExternalAuthenticationMethods Negotiate
    Get-ActiveSyncVirtualDirectory -Server $global:thisComputerHost | Set-ActiveSyncVirtualDirectory -ExternalAuthenticationMethods Negotiate
    Get-OutlookAnywhere -Server $global:thisComputerHost | Set-OutlookAnywhere -ExternalClientAuthenticationMethod Negotiate

    Get-OabVirtualDirectory -Server $global:thisComputerHost | Set-OabVirtualDirectory -WindowsAuthentication $true
    Get-WebServicesVirtualDirectory -Server $global:thisComputerHost | Set-WebServicesVirtualDirectory -WindowsAuthentication $true

    Get-MapiVirtualDirectory -Server $global:thisComputerHost | ? { -not (Contains-Safe $_.IISAuthenticationMethods 'Negotiate') } | % { Set-MapiVirtualDirectory -Identity $_ -IISAuthenticationMethods ($_.IIsAuthenticationMethods + 'Negotiate') }
    DBGER $MyInvocation.MyCommand.Name $error
    DBGEND
  }
}


DBG ('Throw up any remaining errors')
DBGSTART; DBGEND

# SIG # Begin signature block
# MIIc/QYJKoZIhvcNAQcCoIIc7jCCHOoCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCsW0WRtNvIRMir
# 5MHZA0HJy0ufKbfLNWiWL6L3SRFbS6CCGAQwggTlMIIDzaADAgECAhA5vUKe0oFu
# utW8yQO0umXnMA0GCSqGSIb3DQEBCwUAMHUxCzAJBgNVBAYTAklMMRYwFAYDVQQK
# Ew1TdGFydENvbSBMdGQuMSkwJwYDVQQLEyBTdGFydENvbSBDZXJ0aWZpY2F0aW9u
# IEF1dGhvcml0eTEjMCEGA1UEAxMaU3RhcnRDb20gQ2xhc3MgMiBPYmplY3QgQ0Ew
# HhcNMTYxMjAxMTU1MTEzWhcNMTgxMjAxMTU1MTEzWjBRMQswCQYDVQQGEwJDWjEa
# MBgGA1UECAwRSmlob21vcmF2c2t5IEtyYWoxDTALBgNVBAcMBEJybm8xFzAVBgNV
# BAMMDk9uZHJlaiBTZXZlY2VrMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
# AQEAr9E9hNj06bash9JX97kpsqK9Z/ciOBC6trI4nvlW9CPwhKBTb5wArhxLYZBG
# 9jWPWrdy1nL/cm5qMqBb/mogYwMwvEYWMvsIOOVn6HD9lVhNAovD6PHz0ziBBKIs
# zXTjyUPQaoIlIELovz967m78HJdUZJGxqhluAsS9o9/fEzA7XXUhUuqRKsetuZV/
# Asfh5sOveeoRsbeW4daTWvtz3TJuULL0w43LNVYJkd6LL8cegvLPVZUe1N7skvid
# EvntdlowQsJlqFdrH3SGKIPKA6ObcY8SZWkEQSbVBF8Kum1UT+jN0gm+84FwOg5W
# qKx+VvTK2ljVWnPrCD0Zzu2oIQIDAQABo4IBkzCCAY8wDgYDVR0PAQH/BAQDAgeA
# MBMGA1UdJQQMMAoGCCsGAQUFBwMDMAkGA1UdEwQCMAAwHQYDVR0OBBYEFG2vSo3N
# hQWILeUs0oN9XzHTejcfMB8GA1UdIwQYMBaAFD5ik5rXxxnuPo9JEIVVFSDjlIQc
# MG0GCCsGAQUFBwEBBGEwXzAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Auc3RhcnRz
# c2wuY29tMDcGCCsGAQUFBzAChitodHRwOi8vYWlhLnN0YXJ0c3NsLmNvbS9jZXJ0
# cy9zY2EuY29kZTIuY3J0MDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwuc3Rh
# cnRzc2wuY29tL3NjYS1jb2RlMi5jcmwwIwYDVR0SBBwwGoYYaHR0cDovL3d3dy5z
# dGFydHNzbC5jb20vMFEGA1UdIARKMEgwCAYGZ4EMAQQBMDwGCysGAQQBgbU3AQIF
# MC0wKwYIKwYBBQUHAgEWH2h0dHBzOi8vd3d3LnN0YXJ0c3NsLmNvbS9wb2xpY3kw
# DQYJKoZIhvcNAQELBQADggEBAJuRiEvHtIYSpsmMkPhTz4QOOShN3p5KWdf8vm71
# A33CR9fds10d8D2B2aE+vjmHJ69GY0bbfg5oZY2Lsq2euL7Da5/hS8+6T3MEtD4h
# njfHV7mxmoSfFuy/KDipoV6uwhI+ksqchXYdUH+5uCQO0MOO8ITjAgzUQsnZ4UIB
# HBGeP+e+3ljxSYSXWdPIrgxdR971P/HhWSVfKNlmBgEKMQM5Jy0aAd4jxSl/AzdY
# t0+6pliFJ1peGhdFni2Fm8fu5oN68aTIrNtc5WY7Lzgf+sRTVeWORWS37+1zAD0m
# jzd8gyfBLxRuaRSfjYxny0rLXelAwfiA3ze2DU2Bfg9/rfcwggXYMIIDwKADAgEC
# AhBsO9J+3TyUnpWOKKmzx1egMA0GCSqGSIb3DQEBCwUAMH0xCzAJBgNVBAYTAklM
# MRYwFAYDVQQKEw1TdGFydENvbSBMdGQuMSswKQYDVQQLEyJTZWN1cmUgRGlnaXRh
# bCBDZXJ0aWZpY2F0ZSBTaWduaW5nMSkwJwYDVQQDEyBTdGFydENvbSBDZXJ0aWZp
# Y2F0aW9uIEF1dGhvcml0eTAeFw0xNTEyMTYwMTAwMDVaFw0zMDEyMTYwMTAwMDVa
# MHUxCzAJBgNVBAYTAklMMRYwFAYDVQQKEw1TdGFydENvbSBMdGQuMSkwJwYDVQQL
# EyBTdGFydENvbSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEjMCEGA1UEAxMaU3Rh
# cnRDb20gQ2xhc3MgMiBPYmplY3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
# ggEKAoIBAQC5FARY97LFhiwIMmCtCCbAgXe5aBnZFSsdGGnk2hqWBZcuZHkaqT1R
# M1rQd2r0ApNBw466cBur2Ht0b5jo17mpPmh2pImgIqwX1in4u7hhn9IH0GYOMEcg
# K3ACHv5zCRxxNLXifqmsqKfxjjpABnaSyvd4bO9YBXN9f4NQ6aJVAuMArpanxsJk
# e+P4WECVLk17v92CAN5JVaczI+baT/lgo5NVcTEkloCViSbIfU6ILeyhOSQZvpom
# MYk8eJqI0nimOTJJfmXangNDsrX8np+3lXD0+6rCZisXRWIaeffyTMHZ31Qj1D50
# WYdRtX5yev4WgaXoKJQN3lkgXUcytvyHAgMBAAGjggFaMIIBVjAOBgNVHQ8BAf8E
# BAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAy
# BgNVHR8EKzApMCegJaAjhiFodHRwOi8vY3JsLnN0YXJ0c3NsLmNvbS9zZnNjYS5j
# cmwwZgYIKwYBBQUHAQEEWjBYMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5zdGFy
# dHNzbC5jb20wMAYIKwYBBQUHMAKGJGh0dHA6Ly9haWEuc3RhcnRzc2wuY29tL2Nl
# cnRzL2NhLmNydDAdBgNVHQ4EFgQUPmKTmtfHGe4+j0kQhVUVIOOUhBwwHwYDVR0j
# BBgwFoAUTgvvGqRAW6UXaYcwyjRoQ9BBrvIwPwYDVR0gBDgwNjA0BgRVHSAAMCww
# KgYIKwYBBQUHAgEWHmh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeTANBgkq
# hkiG9w0BAQsFAAOCAgEAY6U81bNtJyjY67pTrzAL6kpdEtX5mspw+kxjjNdNVH5G
# 6lLnhaEkIxqdpvY/Wdw+UdNtExs+N8efKPSwh2m/BxXj2fSeLMwXcwHFookScEER
# 8ez0quCNzioqNHac7LCXPEnQzbtG2FHlePKNDWh8eU6KxiAzNzIrIxPthinHGgLT
# BOACHQM2YTlD8YoU5oN3dLmBOqtH0BDMZoLcjEIoEW1zC+TnVb3yU1G0xub6gnN7
# lP50vbAiHJYrnywQiXaloBV8B9YYfe6ZgvjqxwufwFcMVyE3UmCuDTsOpjqDEKpJ
# 25s+FUdkie5VqCS1aaudLo31X+9UvP45pfgyRqzyfUnVEhH4ZXxlBWZMzj2Xov5+
# m/+H3kxYuFA5xdqdshj/Zx00S7PkCSF+8M1NCcvFgQwjIw61bZAjDBl3P3a8xNTX
# sb2CjFdiNKbT3LD6IGeIf0b/EbPf0FXdvBrxm0ofMOhnngdPolPYCtoOGtZPAVe/
# xeu+/ZyKv6TSHlshaUO0iYfsmbXnZ51vvt/kkjwms9/qPFxSuE0fjEfF7aQazwRE
# Df2hiVPR0pAhvShtM3oU4XreEFEUWEYHs25fYV4WMmxkUKSgmSmwRq45tvtGH4LT
# b5+cd+iLqK8rBQL0E6xaUjjGfsYx7bueIvqTvCkrQvoxMbn/qDHCiypowDVq6TAw
# ggZqMIIFUqADAgECAhADAZoCOv9YsWvW1ermF/BmMA0GCSqGSIb3DQEBBQUAMGIx
# CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
# dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0Et
# MTAeFw0xNDEwMjIwMDAwMDBaFw0yNDEwMjIwMDAwMDBaMEcxCzAJBgNVBAYTAlVT
# MREwDwYDVQQKEwhEaWdpQ2VydDElMCMGA1UEAxMcRGlnaUNlcnQgVGltZXN0YW1w
# IFJlc3BvbmRlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKNkXfx8
# s+CCNeDg9sYq5kl1O8xu4FOpnx9kWeZ8a39rjJ1V+JLjntVaY1sCSVDZg85vZu7d
# y4XpX6X51Id0iEQ7Gcnl9ZGfxhQ5rCTqqEsskYnMXij0ZLZQt/USs3OWCmejvmGf
# rvP9Enh1DqZbFP1FI46GRFV9GIYFjFWHeUhG98oOjafeTl/iqLYtWQJhiGFyGGi5
# uHzu5uc0LzF3gTAfuzYBje8n4/ea8EwxZI3j6/oZh6h+z+yMDDZbesF6uHjHyQYu
# RhDIjegEYNu8c3T6Ttj+qkDxss5wRoPp2kChWTrZFQlXmVYwk/PJYczQCMxr7GJC
# kawCwO+k8IkRj3cCAwEAAaOCAzUwggMxMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMB
# Af8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMIIBvwYDVR0gBIIBtjCCAbIw
# ggGhBglghkgBhv1sBwEwggGSMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdp
# Y2VydC5jb20vQ1BTMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4AeQAgAHUAcwBl
# ACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBu
# AHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0
# AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABTACAAYQBuAGQAIAB0
# AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABBAGcAcgBlAGUAbQBl
# AG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5
# ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQAZQBkACAAaABl
# AHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAuMAsGCWCGSAGG/WwD
# FTAfBgNVHSMEGDAWgBQVABIrE5iymQftHt+ivlcNK2cCzTAdBgNVHQ4EFgQUYVpN
# JLZJMp1KKnkag0v0HonByn0wfQYDVR0fBHYwdDA4oDagNIYyaHR0cDovL2NybDMu
# ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEQ0EtMS5jcmwwOKA2oDSGMmh0
# dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRENBLTEuY3Js
# MHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNl
# cnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20v
# RGlnaUNlcnRBc3N1cmVkSURDQS0xLmNydDANBgkqhkiG9w0BAQUFAAOCAQEAnSV+
# GzNNsiaBXJuGziMgD4CH5Yj//7HUaiwx7ToXGXEXzakbvFoWOQCd42yE5FpA+94G
# AYw3+puxnSR+/iCkV61bt5qwYCbqaVchXTQvH3Gwg5QZBWs1kBCge5fH9j/n4hFB
# pr1i2fAnPTgdKG86Ugnw7HBi02JLsOBzppLA044x2C/jbRcTBu7kA7YUq/OPQ6dx
# nSHdFMoVXZJB2vkPgdGZdA0mxA5/G7X1oPHGdwYoFenYk+VVFvC7Cqsc21xIJ2bI
# o4sKHOWV2q7ELlmgYd3a822iYemKC23sEhi991VUQAOSK2vCUcIKSK+w1G7g9BQK
# Ohvjjz3Kr2qNe9zYRDCCBs0wggW1oAMCAQICEAb9+QOWA63qAArrPye7uhswDQYJ
# KoZIhvcNAQEFBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IElu
# YzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQg
# QXNzdXJlZCBJRCBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTIxMTExMDAwMDAw
# MFowYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE
# CxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgQXNzdXJlZCBJ
# RCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6IItmfnKwkKV
# pYBzQHDSnlZUXKnE0kEGj8kz/E1FkVyBn+0snPgWWd+etSQVwpi5tHdJ3InECtqv
# y15r7a2wcTHrzzpADEZNk+yLejYIA6sMNP4YSYL+x8cxSIB8HqIPkg5QycaH6zY/
# 2DDD/6b3+6LNb3Mj/qxWBZDwMiEWicZwiPkFl32jx0PdAug7Pe2xQaPtP77blUjE
# 7h6z8rwMK5nQxl0SQoHhg26Ccz8mSxSQrllmCsSNvtLOBq6thG9IhJtPQLnxTPKv
# mPv2zkBdXPao8S+v7Iki8msYZbHBc63X8djPHgp0XEK4aH631XcKJ1Z8D2KkPzIU
# YJX9BwSiCQIDAQABo4IDejCCA3YwDgYDVR0PAQH/BAQDAgGGMDsGA1UdJQQ0MDIG
# CCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEFBQcD
# CDCCAdIGA1UdIASCAckwggHFMIIBtAYKYIZIAYb9bAABBDCCAaQwOgYIKwYBBQUH
# AgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5o
# dG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0
# AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1
# AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABp
# AGcAaQBDAGUAcgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBl
# AGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBo
# AGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAg
# AGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAg
# AGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wCwYJYIZIAYb9bAMVMBIGA1UdEwEB
# /wQIMAYBAf8CAQAweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8v
# b2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRp
# Z2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6
# MHgwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3Vy
# ZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9E
# aWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwHQYDVR0OBBYEFBUAEisTmLKZB+0e
# 36K+Vw0rZwLNMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqG
# SIb3DQEBBQUAA4IBAQBGUD7Jtygkpzgdtlspr1LPUukxR6tWXHvVDQtBs+/sdR90
# OPKyXGGinJXDUOSCuSPRujqGcq04eKx1XRcXNHJHhZRW0eu7NoR3zCSl8wQZVann
# 4+erYs37iy2QwsDStZS9Xk+xBdIOPRqpFFumhjFiqKgz5Js5p8T1zh14dpQlc+Qq
# q8+cdkvtX8JLFuRLcEwAiR78xXm8TBJX/l/hHrwCXaj++wc4Tw3GXZG5D2dFzdaD
# 7eeSDY2xaYxP+1ngIw/Sqq4AfO6cQg7PkdcntxbuD8O9fAqg7iwIVYUiuOsYGk38
# KiGtSTGDR5V3cdyxG0tLHBCcdxTBnU8vWpUIKRAmMYIETzCCBEsCAQEwgYkwdTEL
# MAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKTAnBgNVBAsTIFN0
# YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSMwIQYDVQQDExpTdGFydENv
# bSBDbGFzcyAyIE9iamVjdCBDQQIQOb1CntKBbrrVvMkDtLpl5zANBglghkgBZQME
# AgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEM
# BgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqG
# SIb3DQEJBDEiBCBM8i4WgBMGDRVV1C6KbDl3oDQBEGHcAz3OJkJlV5O9KjANBgkq
# hkiG9w0BAQEFAASCAQCIiuBQS1r0kGgbJsqi3eg3rhJzk8dwn1UowevqQr1tnGWc
# 0Df1HaxNxS9YEOzG8XmyKUNpM6bfk5kIaddL3qp58Bwls2X41E5GTWYgkC3fMulc
# nID9sblOYo41GNDOdhObum5MTI5rLMTEeVZbr354QiHUk1XBr9X2TUw3xjoer5EN
# tLXsYwjKuphKDfhjo/EshmBVucsvx7/hw3jWsfv7MlJ1bLOVUWgSxW6XSDaCXUOx
# 2kSAKvLEf+jVNmW8OhriOdwkmofFTzowIlbvP9+1GK8MVcIfXLukCsMv6iPkxpch
# EliebvsIm1g7d3Li6oNFizqkwGPiVdMYijISSyUhoYICDzCCAgsGCSqGSIb3DQEJ
# BjGCAfwwggH4AgEBMHYwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0
# IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNl
# cnQgQXNzdXJlZCBJRCBDQS0xAhADAZoCOv9YsWvW1ermF/BmMAkGBSsOAwIaBQCg
# XTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xNzA2
# MjgwNjU3NDdaMCMGCSqGSIb3DQEJBDEWBBS0bLG4bmfjfuEDgoMYCW5Wxh67+DAN
# BgkqhkiG9w0BAQEFAASCAQB9Vv/VYQjmyCcoUxkX6Dqs1q2riBmGU5DO+DhjuICo
# 6fFRS1HsN2qSZOcPr1t91JVNYZC97e6PmheCHIhN20uMaANGqzf7z8tBnXRpijZZ
# Wd0zmGDLwJM1X7QIru+Mc02XJXUo+2wSbCkUJPr6NYxMpy9sw7xforNsovgwwFlF
# 6gcZGKtTJq7aZwxqjb5CttNTJRxKIP0OWgFCvK/zbNLK8BiRG3nYBi6LN8UlWzZ0
# GLJLMprDJ7XxUx8SRi3t33y6xIf/snEgYJUxk8vxeN/8DaGuXx9qmzdgXcVd05+n
# 1ixm/Rx79BMGbjVF295HdycP7JAGDZLnNtYv5sGwcEcb
# SIG # End signature block