Config-ExManagementNode.ps1

<#PSScriptInfo
 
.VERSION 2.0
 
.GUID e76c58d1-4fa1-48e0-91a4-68cae6568985
 
.DESCRIPTION Configure Exchange 2013+ as Exchange Online Management Node.
 
.AUTHOR Aaron Guilmette
 
.COMPANYNAME Microsoft
 
.COPYRIGHT 2022
 
.TAGS Exchange Management node
 
.LICENSEURI
 
.PROJECTURI https://www.undocumented-features.com/2015/11/02/exchange-2013-management-server-for-office-365/
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
#>


<#
.SYNOPSIS Exchange 2013+ Management Node Script and Defaults Script
 
.NOTE This script will attempt to configure an Exchange 2013+ server for managing an
Office 365 tenant WITHOUT HYBRID FUNCTIONALITY. It can also be used to set core minimum
defaults prior to runnign the HCW.
 
THIS DOES NOT AND IS NOT INTENDED TO REPLACE THE HYBRID CONFIGURATION WIZARD.
 
There are, however, instances where a full hybrid configuration is not required
or desired (such as cloud-only mailboxes or no need to move mailboxes between an
on-premises organization and Office 365).
 
THIS CODE AND ANY ASSOCIATED INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK OF USE, INABILITY TO USE, OR RESULTS FROM THE USE OF
THIS CODE REMAINS WITH THE USER.
 
Author: Aaron Guilmette
        aaron.guilmette@microsoft.com
 
#>


Write-Host -ForegroundColor Yellow "In order to use this tool, you must meet the following prerequisites:"
Write-Host -ForegroundColor Yellow `n
Write-Host -ForegroundColor Yellow "1. Please ensure you are running this from the Exchange Management Shell"
Write-Host -ForegroundColor Yellow "on the Exchange 2013 server you wish to configure."
Write-Host -ForegroundColor Yellow `n
Write-Host -ForegroundColor Yellow "2. You must have an Office 365 Global Admin credential with either"
Write-Host -ForegroundColor Yellow "the tenant/managed domain or a verified domain in the tenant"
Write-Host -ForegroundColor Yellow "such as user@contoso.onmicrosoft.com."

$continue = $null
While ($continue -ne $yes) { $continue = Read-Host "Type yes to continue or CTRL-C to abort." }

Function o365Logon()
     {
     # Connect to Office 365 Environment
     Write-Host -ForegroundColor Green "Connecting to Office 365 ..."
     #$proxySettings = New-PSSessionOption -ProxyAccessType IEConfig
     $Office365Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell -Credential $o365Credential -Authentication Basic -AllowRedirection # -SessionOption $proxySettings
     Import-PSSession $Office365Session -AllowClobber -WarningAction SilentlyContinue -ErrorAction SilentlyContinue -Prefix Cloud | Out-Null
     }

# Set some things
$defaultServer = $env:COMPUTERNAME
$exServer = Read-Host "Enter Exchange 2013+ Management Server Name [$($defaultServer)]"
$exServer = ($defaultServer,$exServer)[[bool]$exServer]

$o365Credential = $null
$o365Credential = Get-Credential -Message "Enter Office tenant admin credential as user@tenant.onmicrosoft.com or user@domain.com"
If ($o365Credential.Username -like "*onmicrosoft.com")
    {
    $tenant = $o365credential.UserName.Split("@")[1].Split(".")[0]
    $tenantDomain = $o365Credential.UserName.Split("@")[1].Split(".")[0]+".mail.onmicrosoft.com"
    }
    
# Close Internet Explorer Sessions and Disable IE Enhanced Security
Write-Host -ForegroundColor Green "Closing all Internet Explorer processes."
If (Get-Process -Name iexplore -ErrorAction SilentlyContinue) { Get-Process -Name iexplore | % { Stop-Process $_ -Force } }
Write-Host -ForegroundColor Green "Disabling IE Enhanced Security for Administrators."
$AdminKey = "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}"
Set-ItemProperty -Path $AdminKey -Name "IsInstalled" -Value 0

# Choose whether we enable a TLS connector to Office 365
$tlsEnable = "Enable TLS Connector"
$tlsMessage = "Do you wish to enable a TLS connector to route mail to Office 365? (Requires SSL cert to be installed)"
$tlsNo = New-Object System.Management.Automation.Host.ChoiceDescription "&No","Does not enable TLS Send Connector to Office 365. (Default)."
$tlsYes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes","Enables TLS Send Connector to Office 365."
$tlsOptions = [System.Management.Automation.Host.ChoiceDescription[]]($tlsNo,$tlsYes)
$tlsResult = $host.UI.PromptForChoice($tlsEnable,$tlsMessage,$tlsOptions,0)

# Choose whether we enable anonymous relay
$relayEnable = "Enable Anonymous Relay on Default Connectors"
$relayMessage = "Do you wish to enable anonymous anonymous relay on the default connectors?"
$relayYes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes","Enables anonymous relay (Defaut)."
$relayNo = New-Object System.Management.Automation.Host.ChoiceDescription "&No","Does not enable anonymous relay."
$relayOptions = [System.Management.Automation.Host.ChoiceDescription[]]($relayYes,$relayNo)
$relayResult = $host.UI.PromptForChoice($relayEnable,$relayMessage,$relayOptions,0)

Write-Host -ForegroundColor Green "Using tenant name $tenantDomain."

<#
Add Trusted and Intranet Sites
- Add "localhost" to trusted sites
- Add https://login.microsoftonline.com to intranet
- Add https://outlook.office365.com to intranet
- Add https://portal.microsoftonline.com to intranet
- Add https://portal.office.com to intranet
#>


[string] $regkey = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains"
$trustedSites = @()
$trustedSites = @('http://localhost','https://localhost')
$intranetSites = @()
$intranetSites = @('https://login.microsoftonline.com','https://outlook.office365.com','https://portal.office.com','https://portal.microsoftonline.com')

# Set IE Trusted Sites
Foreach ($site in $trustedSites)
     {
     [object] $uri = [system.URI] $site
     [string] $protocol = ($uri).Scheme
     [string] $hostname = ($uri).host
     [string[]] $split = $hostname.split(".")
     [string] $hosturi = $split[0]
     [string] $domainname = $split[1]+"."+$split[2]
     [string] $zone = "2"
     
     # Verify domain exists, if not, create it
     if (Test-Path "$regkey\$domainname") { }
           else { New-Item -Path "$regkey\$domainname" | Out-Null }
     
     # Verify hostname in domain exists, if not, create it
     if (Test-Path "$regkey\$domainname\$hosturi") { }
           else { New-Item -Path "$regkey\$domainname\$hosturi" | Out-Null } 
     
     # erify protocol exists, if not, create it
     if (Get-ItemProperty -Name $protocol -path "$regkey\$domainname\$hosturi" -ErrorAction SilentlyContinue) { Set-ItemProperty "$regkey\$domainname\$hosturi" -Name $protocol -Value $zone | Out-Null }
           else { New-ItemProperty "$regkey\$domainname\$hosturi" -Name $protocol -Value $zone -PropertyType "DWORD" | Out-Null }
     }

# Set IE Intranet Sites
Foreach ($site in $intranetSites)
     {
     [object] $uri = [system.URI] $site
     [string] $protocol = ($uri).Scheme
     [string] $hostname = ($uri).host
     [string[]] $split = $hostname.split(".")
     [string] $hosturi = $split[0]
     [string] $domainname = $split[1]+"."+$split[2]
     [string] $zone = "1"
     
     # Verify domain exists, if not, create it
     if (Test-Path "$regkey\$domainname") { }
           else { New-Item -Path "$regkey\$domainname" | Out-Null }
     
     # Verify hostname in domain exists, if not, create it
     if (Test-Path "$regkey\$domainname\$hosturi") { }
           else { New-Item -Path "$regkey\$domainname\$hosturi" | Out-Null } 
     
     # Verify protocol exists, if not, create it
     if (Get-ItemProperty -Name $protocol -path "$regkey\$domainname\$hosturi" -ErrorAction SilentlyContinue) { Set-ItemProperty "$regkey\$domainname\$hosturi" -Name $protocol -Value $zone | Out-Null }
           else { New-ItemProperty "$regkey\$domainname\$hosturi" -Name $protocol -Value $zone -PropertyType "DWORD" | Out-Null }
     }

Write-Host -ForegroundColor Cyan "Internet Explorer will now open to the Exchange Admin Center."
Write-Host -ForegroundColor Cyan "Please select the Office 365 tab from the top and sign in using"
Write-Host -ForegroundColor Cyan "your Office 365 Global Administrator account. After you have"
Write-Host -ForegroundColor Cyan "signed in, please close Internet Explorer and return to this"
Write-Host -ForegroundColor Cyan "to continue configuration."
Write-Host -ForegroundColor Cyan `n
Write-Host -ForegroundColor Cyan "Press any key to launch the Exchange Admin Center."

[void][System.Console]::ReadKey($False)

# Prompt Sign-In to Office 365 in Browser
$ie = New-Object -ComObject InternetExplorer.Application
$ie.Navigate2("https://$exServer/ecp")
$ie.Visible = $True

$continue = $null
While ($continue -ne "yes") { $continue = Read-Host "Type yes to continue configuring the management server." }

# Set Client Access Server AutoD InternalURI
Write-Host -ForegroundColor Green "Setting AutodDiscoverServiceInternalUri on $exServer to NULL."
Set-ClientAccessServer -Identity $exServer -AutoDiscoverServiceInternalUri $null
Write-Host -ForegroundColor Green "Configure your internal and external autodiscover CNAME to point to outlook.office365.com."

# Configure Default Database on Management Server with Circular Logging
Write-Host -ForegroundColor Green "Enabling Circular Logging on $exServer."
Get-MailboxDatabase -Server $exServer | % { Set-MailboxDatabase $_ -CircularLoggingEnabled $true }

# Configure All Domains
Write-Host -ForegroundColor Green "Setting up Office 365 domains."

# Connect to Office 365 and retrieve configured domains
o365Logon
$global:CloudAcceptedDomain = Get-CloudAcceptedDomain | ? { $_.DomainName -notlike "*onmicrosoft.com" }
If (!($tenantDomain))
    {
    $global:managedDomain = Get-CloudAcceptedDomain | ? { $_.DomainName -like "*mail.onmicrosoft.com" }
    $global:tenant = $managedDomain.DomainName.Split(".")[0]
    $global:tenantDomain = $managedDomain.DomainName
    }
Get-PSSession | ? { $_.Computername -like "*office365.com" -or $_.ComputerName -like "*lync.com" } | Remove-PSSession

If (!(Get-RemoteDomain | ? { $_.DomainName -eq $tenantDomain }))
                {
                Write-Host -ForegroundColor Green "Adding new Remote Domain $tenantDomain."
                New-RemoteDomain -Name "Office 365" -DomainName $tenantDomain
                Set-RemoteDomain -Identity "Office 365" -TargetDeliveryDomain $True
                }
                Else
                    {
                    Write-Host -ForegroundColor Yellow "$tenantDomain is already configured as a remote domain."
                    }
If (!(Get-AcceptedDomain | ? { $_.DomainName -eq $tenantDomain } ))
                {
                Write-Host -ForegroundColor Green "Adding new Accepted Domain $tenantDomain."
                New-AcceptedDomain -DomainName $tenantDomain -DomainType InternalRelay -Name "Office 365 $tenantDomain"
                }
                Else
                    {
                    Write-Host -ForegroundColor Yellow "$tenantDomain is already configured as an accepted domain. Updating to InternalRelay."
                    Get-AcceptedDomain | ? { $_.DomainName -eq $tenantDomain } | Set-AcceptedDomain -DomainType InternalRelay -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
                    }
ForEach ($CloudDomain in $CloudAcceptedDomain)
    {
    If (!(Get-AcceptedDomain | ? { $_.DomainName -eq $CloudDomain.DomainName } ))
        {
        Write-Host -ForegroundColor Green "Adding registered Office 365 domain $CloudDomain.DomainName as new Accepted Domain."
        New-AcceptedDomain -DomainName $CloudDomain.DomainName -DomainType InternalRelay -Name "$CloudDomain.DomainName"
        }
        Else
            {
            Write-Host -ForegroundColor Yellow "$CloudDomaain.DomainName is already configured as an accepted domain. Updating to InternalRelay."
            Get-AcceptedDomain | ? { $_.DomainName -eq $CloudDomain.DomainName } | Set-AcceptedDomain -DomainType InternalRelay -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
            }
    }
    
# Check AutoDiscover DNS Settings
Write-Host -ForegroundColor Green "Checking Autodiscover CNAMES."
$AutoDDomains = Get-AcceptedDomain
Foreach ($domain in $AutoDDomains)
    {
    $domain = $domain.DomainName
    $autoDExt = nslookup $domain 4.2.2.2
    $autoDInt = nslookup $domain
    
    # Check autodiscover against external public DNS
    If (!($autoDExt -match "outlook.office365.com"))
        {
        Write-Host -ForegroundColor Cyan " Add or update external CNAME record for autodiscover.$domain to outlook.office365.com."
        }
    Else
        {
        Write-Host -ForegroundColor Green "External CNAME record for $domain appears to be correct."
        }
    
    # Check autodiscover against internal server DNS
    If (!($autoDInt -match "outlook.office365.com"))
        {
        Write-Host -ForegroundColor Cyan " Add or update internal CNAME record for autodiscover.$domain to outlook.office365.com."
        }
    Else
        {
        Write-Host -ForegroundColor Green "Internal CNAME record for $domain appears to be correct."
        }
    }                        
                                
# Configure Email Address Policy
$policies = Get-EmailAddressPolicy
Foreach ($policy in $policies)
    {
    Write-Host -fore green "Updating policy $policy"
    $templates = $policy.EnabledEmailAddressTemplates
    $primary = $policy.EnabledPrimarySMTPAddressTemplate
    if (!($policy.EnabledEmailAddressTemplates -contains "smtp:%m@$tenantDomain"))
        {
        $templates += "smtp:%m@$tenantdomain"
        Set-EmailAddressPolicy -Identity $policy.Name -EnabledEmailAddressTemplates $templates 
        }
    Else
        {
        Write-Host -ForegroundColor Cyan " Template smtp:%m@$tenantdomain already present."
        }
    }

# Update Email Address Policy
Get-EmailAddressPolicy | Update-EmailAddressPolicy

Switch ($tlsResult)
     {
     0         { 
                Write-Host -ForegroundColor Green "TLS connector will NOT be enabled. Enabling Standard Connector."
           
                # Configure standard connector to Office 365
                $externalHostName = Read-Host "Enter external hostname of server"
                If (!(Get-SendConnector -Identity "To Office 365" -ErrorAction SilentlyContinue))
                    {
                    New-SendConnector -Name "To Office 365" -AddressSpaces "smtp:$tenantDomain;1" -CloudServicesMailEnabled $true -Fqdn $externalHostName -RequireTLS $false -SmartHosts $tenant.mail.protection.outlook.com
                    }
                
                # Log on to Office 365
                o365Logon
                $ip = (Invoke-WebRequest -Uri http://checkip.dyndns.com).content -replace '[^\d\.]'
                Write-Host -ForegroundColor Green "External NAT IP of server is $ip."
                $SenderDomains = Get-CloudAcceptedDomain | Where { $_.DomainName -notlike "*onmicrosoft.com*" }
                If (!(Get-CloudInboundConnector 'Inbound from On-Premises' -ErrorAction SilentlyContinue))
                    {
                    New-CloudInboundConnector -Name 'Inbound from On-Premises' -SenderIPAddresses $ip -SenderDomains @($SenderDomains.DomainName)
                    }
                }

     1          {
                Write-Host -ForegroundColor Green "TLS connector WILL be enabled."
                $externalHostName = Read-Host "Enter name on certificate used to secure SMTP (such as hybrid.contoso.com)"
                $exCertificate = ( Get-ExchangeCertificate | ? { $_.CertificateDomains -like "*$externalHostName*" } )
                if (!($exCertificate)) { Write-Host -ForegroundColor Red "Certificate with specified name $externalHostName not found. Exiting." ; Break }
                if (!($exCertificate.Services -like "*SMTP*")) { Write-Host -ForegroundColor Red "Certificate $exCertificate.Thumbrint not bound to SMTP. Exiting." ; Break }
                $TlsCertificateName = "<I>"+$exCertificate.Issuer+"<S>"+$exCertificate.Subject
           
                # Configure On-premises to Office 365 Relay
                If (!(Get-SendConnector -Identity "To Office 365" -ErrorAction SilentlyContinue))
                    {
                    New-SendConnector -Name "To Office 365" -AddressSpaces "smtp:$tenantDomain;1" -CloudServicesMailEnabled $true -Fqdn $externalHostName -RequireTLS $true -SmartHosts $tenant.mail.protection.outlook.com -TlsAuthLevel DomainValidation -TlsCertificateName $TlsCertificateName
                    }
                
                # Log on to Office 365
                o365Logon
                $ip = (Invoke-WebRequest -Uri http://checkip.dyndns.com).content -replace '[^\d\.]'
                Write-Host -ForegroundColor Green "External NAT IP of server is $ip."
                $SenderDomains = Get-CloudAcceptedDomain | Where { $_.DomainName -notlike "*onmicrosoft.com*" }
                If (!(Get-CloudInboundConnector 'Inbound from On-Premises' -ErrorAction SilentlyContinue))
                    {
                    New-CloudInboundConnector -Name 'Inbound from On-Premises' -SenderIPAddresses $ip -SenderDomains @($SenderDomains.DomainName)
                    } 
                }
     }
     
Switch ($relayResult)
     {
     0    {
          Write-Host -ForegroundColor Green "Enabling Anonymous Relay on Default Connectors on $exServer."
          Get-ReceiveConnector -Server $exServer | ? { $_.Identity -like "*Default FrontEnd*" } | % { Add-ADPermission -Identity $_.Identity -User "NT AUTHORITY\ANONYMOUS LOGON" -ExtendedRights "ms-Exch-SMTP-Accept-Any-Recipient" -WarningAction SilentlyContinue | Out-Null }
          }
     1    {
          Write-Host -ForegroundColor Green "Anonymous relay will not be enabled."
          }
     }

Get-PSSession | Remove-PSSession
Write-Host -ForegroundColor Green "Configuration completed."