-->
Showing posts with label Exchange. Show all posts
Showing posts with label Exchange. Show all posts

Friday, August 15, 2025

Exchange - Server Update Checklist Script (Updated)

 A while back (a long while, 2015) I wrote a post where I created a Word doc checklist to help keep track of tasks when updating on-premises Exchange servers. It's helpful when you have a lot of servers to manage, but it required some manual editing if you have lots of servers.

I have several customers in the process of updating their servers since Exchange SE has been released, and Exchange 2016/2019 EOL is looming on 14 OCT 25.

A few of those customers have more than a handful of servers - one has 175 in multiple DAGs. So I thought it would be helpful to create an "interactive" spreadsheet where you can check off certain tasks when completed on each server, and what better way to build that spreadsheet than with PowerShell?

So, I created a little script that will prompt for the number of EXCH servers, then prompt for the server names.

UPDATE:

I've added the ability to import a TXT or CSV list of server names to populate the checklist quicker.

The script will prompt if you have a file to import (yes/no). If yes, it will prompt for the full path and import it.

The TXT file can just be the server names themselves, like so:

Text File Format

The CSV file will use "ServerName" as the header, then names listed, like:

CSV Format

If you answered "no", the script will fall back to input the number of servers and server names prompt.

It will then automatically build the spreadsheet in the folder where you run the script from, with the server names as the header, and the tasks in the first column. Then it adds drop-down cells with the status of each task, like: Pending, Complete, Complete With Issues or Not Applicable (for ones like Edge Servers that don't require tasks like load balancer pool removal or DAG work).

The drop-downs are color-coded for easier readability and there's a "notes" column that autowraps if you need to add notes for any issues.

The Script

Copy the text block and save as something like EXCH_Update_Checklist.ps1

# Ask if user wants to import server names from a file

$importChoice = Read-Host "Do you want to import server names from a CSV or TXT file? (yes/no)"

$serverNames = @()

if ($importChoice -eq "yes") {

    $filePath = Read-Host "Enter the full path to the CSV or TXT file"

    if (Test-Path $filePath) {

        $extension = [System.IO.Path]::GetExtension($filePath)

        if ($extension -eq ".csv") {

            $serverNames = Import-Csv $filePath | ForEach-Object { $_.ServerName }

        } elseif ($extension -eq ".txt") {

            $serverNames = Get-Content $filePath

        } else {

            Write-Host "Unsupported file type. Please use .csv or .txt"

            exit

        }

        Write-Host "Imported server names:"

        $serverNames | ForEach-Object { Write-Host $_ }

    } else {

        Write-Host "File not found. Exiting."

        exit

    }

} else {

    # Prompt for number of Exchange servers

    $numServers = Read-Host "Enter the number of Exchange servers"

    for ($i = 1; $i -le $numServers; $i++) {

        $serverName = Read-Host "Enter name for server $i"

        $serverNames += $serverName

    }

}

# Define Exchange maintenance tasks with new additions

$tasks = @(

    "Backup",

    "Disable in Load Balancer",

    "Stop Extra Services (A/V, backup)",

    "Stop Monitoring",

    "Start Exchange maintenance mode",

    "Uninstall old CU Language Pack (IF APPLICABLE)",

    "Install Windows Updates",

    "Reboot",

    "Run CU/SU Install",

    "Verify CU/SU",

    "Reboot",

    "Check Build Number",

    "Install new CU Language Pack (IF APPLICABLE)",

    "Stop Exchange maintenance mode",

    "Enable in Load Balancer",

    "Rebalance DAG DB’s",

    "Re-enable Monitoring",

    "Start Services (A/V, backup)"

)

# Create Excel COM object

$excel = New-Object -ComObject Excel.Application

$excel.Visible = $false

$workbook = $excel.Workbooks.Add()

$sheet = $workbook.Worksheets.Item(1)

# Write headers and bold them

$sheet.Cells.Item(1, 1).Value2 = "Tasks"

$sheet.Cells.Item(1, 1).Font.Bold = $true

for ($i = 0; $i -lt $serverNames.Count; $i++) {

    $cell = $sheet.Cells.Item(1, $i + 2)

    $cell.Value2 = $serverNames[$i]

    $cell.Font.Bold = $true

}

# Add Notes column header

$notesCol = $serverNames.Count + 2

$sheet.Cells.Item(1, $notesCol).Value2 = "Notes"

$sheet.Cells.Item(1, $notesCol).Font.Bold = $true

# Write tasks in first column and bold them

for ($i = 0; $i -lt $tasks.Count; $i++) {

    $cell = $sheet.Cells.Item($i + 2, 1)

    $cell.Value2 = $tasks[$i]

    $cell.Font.Bold = $true

}

# Add dropdowns and default value "Status [Dropdown]" to each cell

$dropdownOptions = 'Pending,Not Applicable,Complete,Complete with Issues'

for ($row = 2; $row -le $tasks.Count + 1; $row++) {

    for ($col = 2; $col -le $serverNames.Count + 1; $col++) {

        $cell = $sheet.Cells.Item($row, $col)

        $cell.Value2 = "Status [Dropdown]"

        $range = $sheet.Range($cell, $cell)

        $range.Validation.Delete()

        $range.Validation.Add(3, 1, 1, $dropdownOptions)

        $range.Validation.IgnoreBlank = $true

        $range.Validation.InCellDropdown = $true

        $cell.Interior.ColorIndex = -4142  # No fill

    }

    # Add empty Notes cell

    $sheet.Cells.Item($row, $notesCol).Value2 = ""

}

# Apply conditional formatting to dropdown cells

$xlCellValue = 1

$xlEqual = 3

$startCell = $sheet.Cells.Item(2, 2)

$endCell = $sheet.Cells.Item($tasks.Count + 1, $serverNames.Count + 1)

$dropdownRange = $sheet.Range($startCell, $endCell)

$dropdownRange.FormatConditions.Delete()

# "Pending" = Yellow

$formatPending = $dropdownRange.FormatConditions.Add($xlCellValue, $xlEqual, "Pending")

if ($formatPending) { $formatPending.Interior.Color = 65535 }

# "Complete" = Light Green

$formatComplete = $dropdownRange.FormatConditions.Add($xlCellValue, $xlEqual, "Complete")

if ($formatComplete) { $formatComplete.Interior.Color = 5296274 }

# "Complete with Issues" = Light Red

$formatIssues = $dropdownRange.FormatConditions.Add($xlCellValue, $xlEqual, "Complete with Issues")

if ($formatIssues) { $formatIssues.Interior.Color = 13408767 }

# Auto-fit columns (will not override manually set Notes width)

$usedRange = $sheet.UsedRange

$usedRange.Columns.AutoFit()

# Set Notes column width to 45 and enable text wrapping

$notesColumn = $sheet.Columns.Item($notesCol)

$notesColumn.ColumnWidth = 45

$notesColumn.WrapText = $true

# Save the workbook in the script's current directory with date in filename

$currentDate = Get-Date -Format "yyyy-MM-dd"

$scriptDirectory = Split-Path -Parent $MyInvocation.MyCommand.Definition

$savePath = Join-Path $scriptDirectory "Exchange Maintenance Checklist_$currentDate.xlsx"

$workbook.SaveAs($savePath)

# Cleanup

$workbook.Close($false)

$excel.Quit()

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($sheet) | Out-Null

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook) | Out-Null

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) | Out-Null

[GC]::Collect()

[GC]::WaitForPendingFinalizers()

# Automatically open the saved Excel file

Start-Process $savePath

Write-Output "Excel spreadsheet saved to $savePath"

When you run it, as stated above, it will ask for the server count, then ask for server names, save the spreadsheet and automatically open it with your pre-populated servers.


PowerShell Prompts

If you imported a CSV or TXT file it will output those server names from the file and save the spreadsheet and automatically open it with your pre-populated servers.

                              PowerShell TXT Import
                              PowerShell CSV Import

The output will look like so:

Update Checklist Spreadsheet

Hopefully that'll make it easier to keep track of what's been done, and if you have a team of engineers working on each task, they can update the columns if you share it out on SharePoint. If I get some time, I'll make it where you can pipe in a CSV of server names to populate the script quickly.

Thursday, April 3, 2025

Exchange - 2019 CU15 Install Error mmc.exe and where.exe

I had a customer trying to upgrade their Exchange 2019 Servers from CU14 to CU15 and hit an error during the Management Tools step. The installer complained about the mmc.exe and where.exe files. 

For reference "where.exe" is part of PowerShell. Basically the installer is looking in two places for those two files and they're either missing or corrupt.

Here's the full error:

The following error was generated when "$error.Clear(); 
          if (Test-Path ($Env:SystemRoot + "\system32\mmc.exe")) {Copy-Item -Path ($RoleInstallPath+"Bin\mmc.exe.config") -Destination (Split-Path (where.exe mmc)) -Force}
        " was run: "System.Management.Automation.ParameterBindingException: Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'Destination'. Specified method is not supported. ---> System.NotSupportedException: Specified method is not supported.
   at System.Management.Automation.ParameterBinderBase.CoerceTypeAsNeeded(CommandParameterInternal argument, String parameterName, Type toType, ParameterCollectionTypeInformation collectionTypeInfo, Object currentValue)
   --- End of inner exception stack trace ---
   at System.Management.Automation.ParameterBinderBase.CoerceTypeAsNeeded(CommandParameterInternal argument, String parameterName, Type toType, ParameterCollectionTypeInformation collectionTypeInfo, Object currentValue)
   at System.Management.Automation.ParameterBinderBase.BindParameter(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, ParameterBindingFlags flags)
   at System.Management.Automation.CmdletParameterBinderController.BindParameter(CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
   at System.Management.Automation.CmdletParameterBinderController.BindParameter(UInt32 parameterSets, CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
   at System.Management.Automation.CmdletParameterBinderController.BindParameters(UInt32 parameterSets, Collection`1 arguments)
   at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
   at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
   at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
   at System.Management.Automation.CommandProcessor.Prepare(IDictionary psDefaultParameterValues)
   at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
   at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
   at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
   at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
   at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
   at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)".

**Note** This issue happens quite often when installing EXCH Management Tools on Windows 10/11 as well and the fix is the same. I've also seen it with Exchange 2016.

The Fix:

First we need to check if both the where.exe and mcc.exe files exist in the following directories:

\windows\system32

\windows\syswow64

If not, copy them from a working server and run the install again.


If that doesn't work, we'll need to rename the c:\windows\syswow64\mmc.exe to mmc.old by using the TrustedInstaller account.

You can run CMD as TrustedInstaller with psexec:

Grab PsTools - Sysinternals | Microsoft Learn

In an elevated command prompt run: 

psexec -I -s cmd.exe

A new cmd window will open.

Running a “whoami” in that window should show nt authority\system as the user.

Then rename the mmc file like so:

cd c:\windows\syswow64

rename mmc.exe mmc.old

Now run the CU15 install and it should complete!

Tuesday, February 18, 2025

Exchange - Migrating EXCH 2016 to 2019/SE Guide

Alot of my customers are scrambling to get ready for Exchange SE (Subscription Edition) which requires Exchange 2019 CU14/CU15 for the in-place upgrade. Seriously, I've led 14 migrations in the past 6 months. The reason they're in a hurry is because Exchange 2016/2019 goes End Of Life (EOL) October 14, 2025. Exchange SE has been released. See how very little time there is to perform an upgrade before you're out of support?

Another issue is Microsoft throttles mail flow from out-of-date Exchange Hybrid servers...there's no word yet on when 2019 CU14/CU15 will be throttled, but since it will be EOL, I'd assume it would come pretty quickly after 10/2025.

A word on Edge Servers: if you still have them, and you're in a Hybrid, now would be a great time to get rid of them. Can they be useful? Yeah...sorta. Are they needed in a Hybrid? No! With email, less hops are better, so if you're building new Mailbox Servers, it would be beneficial to update inbound NAT for SMTP (TCP port 25) to point to the Exchange 2019 Mailbox server(s) and remove the unnecessary management and complexity of your environment.

Steps to upgrade Exchange Mailbox Servers and Edge Servers from 2016 to 2019/SE


Network and Directory Server Requirements for Exchange 2019/SE

Component

Requirement

Domain controllers

All domain controllers in the forest need to be running one of the following versions of Windows Server:

 

Windows Server 2025 Standard or Datacenter

 

Windows Server 2022 Standard or Datacenter

 

Windows Server 2019 Standard or Datacenter

 

Windows Server 2016 Standard or Datacenter

 

Windows Server 2012 R2 Standard or Datacenter

Active Directory Forest

The Active Directory Forest functional level is Windows Server 2012 R2 or higher.

Active Directory site

The Active Directory site where you install the Exchange Server must contain at least one writeable domain controller that's also a global catalog server; or else, the installation will fail. Furthermore, you can't install the Exchange server and then remove the domain controller from the Active Directory site.

IPv6

Exchange 2019/SE supports IPv6 only when IPv4 is also installed and enabled on the Exchange server.

 

If you deploy Exchange in this configuration, and your network supports IPv4 and IPv6, all Exchange servers can send data to and receive data from devices, servers, and clients that use IPv6 addresses.


Exchange Server 2019 system requirements, Exchange 2019 Requirements, Exchange 2019 Memory Requirements, Exchange 2019 Client Compatibility | Microsoft Learn

Hardware Plans for Exchange Server 2019/SE

1. Mailbox Server Role:

·         CPU: Minimum 4 cores (8 cores recommended) at 2.6 GHz or higher

·         Memory: 128 GB RAM (recommended, but not required – can match current EXCH 2016 Server)

·         OS Disk: 120 GB SSD

·         Disk Space: At least 30 GB of free disk space on the drive where Exchange is to be installed, at least 200 MB of free disk space on the system drive, at least 500 MB of free disk space for message queue database.

·         Database and Log Disks: Separate disks for databases and logs, recommended using SSDs or high-performance HDDs

·         Network: 1 Gbps Ethernet (10 Gbps recommended)

 2. Edge Transport Server Role (If Applicable):

·         CPU: Minimum 2 cores (4 cores recommended) at 2.6 GHz or higher

·         Memory: 64 GB RAM (recommended, but not required – can match current EXCH 2016 Server)

·         OS Disk: 120 GB SSD

·         Transport Database Disk: Separate disk for the transport database, SSD recommended

·         Network: 1 Gbps Ethernet (10 Gbps recommended)

Supported operating systems for Exchange 2019/SE

  • Windows Server 2025 Standard/Datacenter
  • Windows Server 2022 Standard/Datacenter
  • Windows Server 2019 Standard/Datacenter

Virtual Machine Plans

If you are opting for virtual machines, ensure that the virtual environment can support the same specifications as the hardware requirements mentioned above.

**Note** The Exchange Health Checker will flag specific settings when using VMs, pay attention to those call-outs and spec accordingly to avoid performance issues.

Exchange Server virtualization | Microsoft Learn

Exchange and Edge Server Naming Convention.

Per organization.

Prerequisites for Installing Exchange Server 2019/SE Mailbox Servers on Windows Server 2019/2022/2025

From a Domain Controller (AD rights are required)

Prepare Active Directory and Extend the Active Directory schema

The account you use must be a member of the Schema Admins and Enterprise Admins security groups to run /PrepareSchema, and a member of the Enterprise Admins security group to run /PrepareAD.

o   E:\Setup.exe /IAcceptExchangeServerLicenseTerms_DiagnosticDataON /PrepareSchema

o   E:\Setup.exe /IAcceptExchangeServerLicenseTerms_DiagnosticDataON /PrepareAD

**Note** If you only have one domain you skip this step, but if your Exchange Servers or recipients live in a child domain, you will need to prep that domain separately:

To prepare all domains, run:

o   E:\Setup.exe /IAcceptExchangeServerLicenseTerms_DiagnosticDataON / /PrepareAllDomains

To target a specific domain, run:

o   E:\Setup.exe /IAcceptExchangeServerLicenseTerms_DiagnosticDataON /PrepareDomain[:<DomainFQDN>]

On the Exchange Boxes:

1)      Install Other Necessary Components

E    Ensure that the server has the latest updates from Windows Update.

  • The target server must be a member of an Active Directory domain.

    Configure the server with a static IP address.

    Ensure time synchronization with an DC server.

2)      Install the following software:

·         .NET Framework 4.8

·         Visual C++ Redistributable Package for Visual Studio 2012

·         Visual C++ Redistributable Package for Visual Studio 2013

o   Both files will be named vcredist_x64 – best to rename with the version

·         Unified Communications Managed API 4.0

·         IIS URL Rewrite Module


Pre-requisite script (Setup Assist)


Review the optional Setup Assist script to prepare Exchange 2019/SE. This script is provided “as is” and is not supported, so be sure to test this script in your environment before using it in production.

3)      Install Required Windows Roles and Features

Install-WindowsFeature RSAT-ADDS

 Install-WindowsFeature Server-Media-Foundation, NET-Framework-45-Core, NET-Framework-45-ASPNET, NET-WCF-HTTP-Activation45, NET-WCF-Pipe-Activation45, NET-WCF-TCP-Activation45, NET-WCF-TCP-PortSharing45, RPC-over-HTTP-proxy, RSAT-Clustering, RSAT-Clustering-CmdInterface, RSAT-Clustering-Mgmt, RSAT-Clustering-PowerShell, WAS-Process-Model, Web-Asp-Net45, Web-Basic-Auth, Web-Client-Auth, Web-Digest-Auth, Web-Dir-Browsing, Web-Dyn-Compression, Web-Http-Errors, Web-Http-Logging, Web-Http-Redirect, Web-Http-Tracing, Web-ISAPI-Ext, Web-ISAPI-Filter, Web-Lgcy-Mgmt-Console, Web-Metabase, Web-Mgmt-Console, Web-Mgmt-Service, Web-Net-Ext45, Web-Request-Monitor, Web-Server, Web-Stat-Compression, Web-Static-Content, Web-Windows-Auth, Web-WMI, Windows-Identity-Foundation, RSAT-ADDS

IF APPLICABLE - Exchange 2019/SE Edge Transport servers on Windows Server 2019/2022/2025


1)      Install the following software:

a. .NET Framework 4.8

b. Visual C++ Redistributable Package for Visual Studio 2012

c. Run this command from Windows PowerShell

·         Install-WindowsFeature ADLDS

Follow the below documents to install the Mailbox and Edge server Step by Step.


Install Mailbox Server Using Unattended mode

    Setup.exe /IAcceptExchangeServerLicenseTerms_DiagnosticDataON /mode:Install /r:MB

https://learn.microsoft.com/en-us/exchange/plan-and-deploy/deploy-new-installations/unattended-installs?view=exchserver-2019

**Note** Extended Protection is enabled by default on CU14+. If you do not currently have Extended Protection enabled on Exchange 2016, you need to install 2019/SE with it off. You must install with the command-line using the following command:

    Setup.exe /IAcceptExchangeServerLicenseTerms_DiagnosticDataON /mode:Install /r:MB               /DoNotEnableEP

Install Exchange Mailbox servers using the Setup wizard

Install Exchange Mailbox servers using the Setup wizard | Microsoft Learn

Install Edge Server Using Unattended mode

    Setup.exe /IAcceptExchangeServerLicenseTerms_DiagnosticDataON /mode:Install /r:et

Install Exchange Edge Transport servers using the Setup wizard

Install Exchange Edge Transport servers using the Setup wizard | Microsoft Learn


Enter your Exchange Server product key

Enter your Exchange Server product key | Microsoft Learn

**Note** Your Exchange 2019 license key will carry over to SE at this time (CU2 will change that next year)

If you get a pending reboot error, open Regedit and navigate to:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager

Delete the “PendingFileRenameOperations” key

On Exchange Mailbox Servers

A.      Set Client Access URI:

As soon as your new 2019/SE server reboots, immediately set it into Maintenance mode to prevent it from causing client warnings. Then, run Set-ClientAccessService -AutoDiscoverServiceInternaluri to either set the SCP URI to $null or align it to your existing Exchange HTTPS namespace https://autodiscover.domain.com/AutoDiscover/Autodiscover.xml so that clients continue to connect to 2016 until you are fully ready to go.

Start Maintenance Mode on a Single Exchange Server:

$Computer = $ENV:ComputerName

Set-ServerComponentState $Computer -Component HubTransport –State Draining -Requester Maintenance

Set-ServerComponentState $Computer -Component ServerWideOffline -State Inactive -Requester Maintenance

Stop Maintenance Mode:

$Computer = $ENV:ComputerName

Set-ServerComponentState $Computer -Component ServerWideOffline -State Active -Requester Maintenance

Set-ServerComponentState $Computer -Component HubTransport -State Active -Requester Maintenance

Restart-Service MSExchangeTransport

Restart-Service MSExchangeFrontEndTransport

Get-ServerComponentState $Computer

 

B.      Configure the URLs on new Servers.

PowerShell commands to set the URLs for various virtual directories on Exchange 2019/SE servers.

Note: Change "EX19-01" with your new Exchange 2019/SE Server name, and “mail.domain.com” to match your namespace. Execute these commands on each new Exchange 2019/SE server.

Set OWA Virtual Directory:

Set-OwaVirtualDirectory -Identity "EX19-01\owa (Default Web Site)" -InternalUrl "https://mail.domain.com/owa" -ExternalUrl "https://mail.domain.com/owa"

Set ECP Virtual Directory:

Set-EcpVirtualDirectory -Identity "EX19-01\ecp (Default Web Site)" -InternalUrl "https://mail.domain.com/ecp" -ExternalUrl "https://mail.domain.com/ecp"

Set ActiveSync Virtual Directory:

Set-ActiveSyncVirtualDirectory -Identity "EX19-01\Microsoft-Server-ActiveSync (Default Web Site)" -InternalUrl "https://mail.domain.com/Microsoft-Server-ActiveSync" -ExternalUrl "https://mail.domain.com/Microsoft-Server-ActiveSync" 

Set OAB Virtual Directory:

Set-OabVirtualDirectory -Identity "EX19-01\OAB (Default Web Site)" -InternalUrl "https://mail.domain.com/OAB" -ExternalUrl "https://mail.domain.com/OAB" 

Set EWS Virtual Directory:

Set-WebServicesVirtualDirectory -Identity "EX19-01\EWS (Default Web Site)" -InternalUrl "https://mail.domain.com/EWS/Exchange.asmx" -ExternalUrl "https://mail.domain.com/EWS/Exchange.asmx" 

Set MAPI Virtual Directory:

Set-MapiVirtualDirectory -Identity "EX19-01\mapi (Default Web Site)" -InternalUrl "https://mail.domain.com/mapi" -ExternalUrl "https://mail.domain.com/mapi" 

Set Outlook Anywhere:

Set-OutlookAnywhere -Identity "EX19-01\Rpc (Default Web Site)" -InternalHostname "mail.domain.com" -ExternalHostname "mail.domain.com" -ExternalClientsRequireSsl $true -InternalClientsRequireSsl $true

Set Client Access URI:

Set-ClientAccessService -Identity "EX19-01" -AutoDiscoverServiceInternalUri https://autodiscover.domain.com/AutoDiscover/Autodiscover.xml.

 

You can also run a script to check and set all virtual directories: PowerShell Script to Configure Exchange Server URLs (practical365.com)

C.      Disable legacy TLS

I strongly recommend disabling TLS 1.0 and 1.1 in your organization as part of your migration. Be sure to read the guidance for this carefully since mistakes can cause problems.

Exchange Server TLS configuration best practices

How to enable Transport Layer Security (TLS) 1.2 on clients

Enable TLS 1.2 on servers

D.      Configure anti-virus exclusions

If deploying file-level scanners on Exchange servers, make sure that the appropriate exclusions, such as directory exclusions, process exclusions, and file name extension exclusions, are in place for both scheduled and real-time scanning.

When configuring antivirus software for Exchange Server, be sure to exclude all the items described here and here. If using Windows Defender, you can use a script we built to make this easier.

 

E.       Export and install the 3rd party SSL certificate on new Exchange Servers.


F.       In the EMS on an Exchange 2016 Server, run:

Get-ExchangeCertificate | Format-Table Subject,Thumbprint

It will list your certs, and you will need the SAN (or public) cert:

Subject                                                   Thumbprint

-------                                                      ----------

CN=mail.domain.com                           BF09E758DF562307138D888697D3A766BDBEBF46

CN=EX16-01                                         C232F4D642F74B9DC7E4ED33D4AB56E68C10CA76

CN=Microsoft Exchange Serve...           411A27BA64FD140523E4D1CF088589C228CA9C5E

CN=WMSvc-SHA2-EX16-01                D894B092E2ABE925E7104A0C9DFF6C448182CA30

G.      Copy the thumbprint and then, run:

$cert = Export-ExchangeCertificate -Thumbprint BF09E758DF562307138D888697D3A766BDBEBF46 -BinaryEncoded -Password (Get-Credential).password

**Note** Change the thumbprint to match the output from step 1.

H.      Then run:

[System.IO.File]::WriteAllBytes('\\EX16-01\C$\Users\<user>\Desktop\mail_domain_com.pfx', $cert.FileData)

**Note** This will export the cert on the desktop of the current server. Change “EX16-01” to the name of your Exchange 2016 Server; change \<user>\ to your username; change mail_domain_com.pfx' to the filename of your preference.

I.         Next, run:

Import-ExchangeCertificate -Server EX19-01 -FileData ([System.IO.File]::ReadAllBytes('\\EX16-01\C$\Users\<user>\Desktop\mail_domain_com.pfx')) -Password (Get-Credential).password  -PrivateKeyExportable $true

**Note** Change “EX1901” to the name of your new Exchange 2019/SE Server; change “EX1601” to the name of your Exchange 2016 server where you saved the cert file; change \<user>\ to your username; change mail_domain_com.pfx' to the filename you chose in step 3.

J.        Now, assign services on the new server:

Enable-ExchangeCertificate -Server EX19-01 –Thumbprint BF09E758DF562307138D888697D3A766BDBEBF46 –Services IIS,SMTP

**Note** Change the thumbprint to match the output from step 1.

K.       Repeat the Import/enabling on your other Exchange 2019/SE Mailbox servers.

**Note** This SAN certificate should be Installed and assigned SMTP service on Edge servers as well.

L.       Migrate all the arbitration mailboxes to new Exchange Server

Get-mailbox –Arbitration -Database <Old2016DatabaseNameGoesHere> | New-MoveRequest –TargetDatabase <New2019DatabaseNameGoesHere>

M.    Configure connectors

The next step is to configure Connectors. Send Connectors will carry over from previous Exchange Servers.

Receive connectors

Exchange 2019/SE has the same default receive connectors as Exchange 2016. Only custom Receive Connectors such as Relay, will need to be re-built.

SMTP relay receive connector

Configure an SMTP relay receive connector on Exchange 2019/SE with the same configuration as 2016. This is required for any on-premises applications that need to relay emails through Exchange 2019/SE.

**Note** If you use DNS names for relay, you will need to update those records.

We will use this script to Copy a selected receive connector and it's configuration and permissions to other Exchange Servers.

Copy-ReceiveConnector/Copy-ReceiveConnector.ps1 at master · Apoc70/Copy-ReceiveConnector · GitHub

Copy receive connector to another Exchange Server - ALI TAJRAN

Send connectors

Replace the 2016 servers with 2019/SE servers in the ‘SourceTransportServers’ for Send connectors.

If routing via Edge, use your new 2019/SE Edge servers as SourceTransport.

In an Exchange hybrid environment, we need to make sure that TLSCertificateName is configured correctly on our Connectors in both Exchange Server and Exchange Online. The Hybrid Configuration Wizard (HCW) is responsible for making the required changes on hybrid connectors.

Run in Exchange Management Shell

**Note** These two cmdlets will show you all the Send and Receive Connectors that currently use a TLS Certificate.

Get-ReceiveConnector | ? TlsCertificateName -ne $null | FL Identity,TlsCertificateName

Get-SendConnector | ? TlsCertificateName -ne $null | FL Identity,TlsCertificateName

Check the TLS Certificate Name for the new certificate

$cert = Get-ExchangeCertificate -Thumbprint "‎ef 7a 47 18 fa 6e c1 f6 05 61 c2 cc 9e 37 42 a2 93 10 d0 67"

$tlscertificatename = "<i>$($cert.Issuer)<s>$($cert.Subject)"

If the TLS Certificate Name changes, you can use these commands to set the new TLS Cert Name on the Send/Receive Connectors

Get-ReceiveConnector | ? TlsCertificateName -ne $null | Set-ReceiveConnector -TlsCertificateName $tlscertificatename

Get-SendConnector | ? TlsCertificateName -ne $null | Set-SendConnector -TlsCertificateName $tlscertificatename

If Applicable:

Subscribe Exchange 2019/SE Mailbox server with new 2019/SE Edge servers

Required ports:

Port 50636 should be opened from mailbox toward edge

Port 25 should be opened both directions

I.        Export XML file from edge transport server:

New-EdgeSubscription -FileName "C:\Temp\Edge2019Subscription.xml"

 

II.      Copy XML file from Edge to Mailbox server and run the below command. Kindly ensure that path is correct where you have saved the XML file.

New-EdgeSubscription -CreateInboundSendConnector $false -CreateInternetSendConnector $false -FileData ([byte[]]$(Get-Content -Path "C:\Temp\EdgeSubscriptionInfo.xml" -Encoding Byte -ReadCount 0)) -Site "Enter the site name"

**Note** Change “Enter site name” with your AD site

III.    Restart the Microsoft Exchange EdgeSync service on mailbox server

 

IV.    Run the below command from a mailbox server.

Start-edgesynchronization

Test-edgesynchronization

Procedures for Edge Subscriptions | Microsoft Learn

Run HCW and mark 2019/SE servers as Hybrid Servers.

Download, install and run the HCW from one of the new Exchange 2019/SE servers.

Exchange Hybrid Configuration Wizard step by step guide

HCW Choose Exchange Hybrid Configuration feature | Microsoft Learn

Send a Test Email to Check Relay

  1. Open Exchange Management Shell (EMS):
    • Launch the EMS on your Exchange 2019/SE server.
  1. Use the Send-MailMessage Cmdlet:
    • The Send-MailMessage cmdlet can be used to send a test email. Here’s an example command:
    • Send-MailMessage -From "test@yourdomain.com" -To "recipient@externaldomain.com" -Subject "Test Email" -Body "This is a test email to check relay." -SmtpServer "your.smtp.server"

Replace the placeholders with appropriate values:

      • test@yourdomain.com: The email address you are sending from.
      • recipient@externaldomain.com: The email address you are sending to.
      • your.smtp.server: The SMTP server address of your Exchange 2019/SE server.
  1. Verify the Email:
    • Check the recipient’s inbox to ensure the test email was received.
    • Review the mail flow logs on your Exchange 2019/SE       server to confirm the email was relayed correctly.

 Run the Exchange Server Health Checker

Health Checker identifies potential critical issues. We strongly recommend running it and thoroughly review its findings. You should implement all recommendations to avoid potential outages or performance issues. Health Checker isn’t just for migration. It’s for ongoing management of Exchange Server. Keep it handy, as it is one of your most valued admin tools.

Helpful Documents.

Upgrading your organization from current versions to Exchange Server SE - Microsoft Community Hub

Exchange On-Premises Best Practices for Migrations from 2010 to 2016 - Microsoft Community Hub

Best Practices for Migrating from Exchange Server 2013 to Exchange Server 2019 - Microsoft Community Hub

Remove legacy Exchange versions

Decommissioning Exchange Server 2016 | Microsoft Community Hub

Decommissioning Exchange Server 2013 | Microsoft Community Hub