Wednesday, July 8, 2020

Restoring Deleted Items from Exchange as an Admin

The Get/Restore-RecoverableItems PowerShell cmdlets have been available for some time now in Exchange Server 2016/2019 and Exchange Online environments. These allow admins to restore deleted items from user mailboxes such as in the following example:

Restore-RecoverableItems nuno@domain.com -FilterItemType IPM.Note -SubjectContains “New Joiner – John Doe” -FilterStartTime “20/06/2020 12:00:00 AM” -FilterEndTime “21/06/2020 11:59:59 PM”

Items are restored to the original folder location if the information is available for the item. If the information cannot be found, the item is restored to the default folder for the item type (Inbox for messages, Calendar for meetings and appointments, and so on).

What this post is mainly about, however, is the fact that admins can now do the same from the preview version of Exchange Admin Center (with the new UI)!

Important: please note that in order to use these cmdlets and the method described below, you need to be assigned the Mailbox Import / Export permission which, by default, no one is assigned.

To get started, go to the Exchange Admin Center and click on Try it now to access the preview version:

Alternatively, you can use the following link to get to it directly: https://admin.exchange.microsoft.com/#/mailboxes

Next, select the user you want to recover deleted items for and then look for the Recover deleted items link on the user property page:

The new Recover deleted items UI will show up and automatically list the latest 50 recoverable items:

We can easily search for the items we are interested in by subject, type, or folder type:

After clicking Apply filter, our results are reduced to recoverable items containing the subject line “azure” which were deleted within the past 30 days. Once we find the item(s) we want to recover, we simply select it/them and click on Recover deleted items:

Once recovered, a green banner will appear indicating all items have been successfully recovered:

All done!   😊

Friday, June 26, 2020

Exchange LUN disappeared after reboot

After rebooting an Exchange 2013 server to complete the installation of a security update, 2 LUNs just disappeared:


Looking in Disk Management their Status was Failed:


The LUNs were offline, and after manually bringing them online everything was back to normal.

There had been some previous work done on these LUNs in order to expand them and, for some reason, the default Windows policy to make SAN disks offline was applied (it assumes VMware disks are SAN disks).

After changing the policy to make “new” disks online by default, the issue didn’t happen again:

How to set OWA Language and Time zone using PowerShell

When a user first logs on to Outlook Web App (OWA), he/she gets prompted to set their language and time zone:

 

Can we, as administrators, pre-set these for users or change them after they have been set, for example? Of course! To do this, we use the Get/Set-MailboxRegionalConfiguration cmdlet:


The two parameters we are interested for this scenario are:

  • Language: specifies the language setting, such as en-us, that would apply for the mailbox;
  • TimeZone: specifies the time zone, such as Pacific Standard Time, that the mailbox in the specified region uses. The default value is the time zone setting on the server.

Let us say, for example, that we want to set everyone’s mailbox to American English and Pacific time. To do this, all we need to run is:

Get-Mailbox –ResultSize Unlimited –Filter {RecipientTypeDetails –eq "UserMailbox"} | Set-MailboxRegionalConfiguration –Language en-US –TimeZone "Pacific Standard
Time"

How to Change Exchange EAC Language

When we first login to the Exchange Admin Center (EAC), we are greeted with the language selection screen and time zone. But how do we change EAC’s language after that? The answer is to use Outlook Web App (OWA). Once you have logged into OWA, select the cog icon in the top right hand corner and then click Options:


Next, go to settings in the options menu, and on the right hand menu select regional. Now edit the language and time zone settings to what you want as these settings also apply to the EAC:


But what about for environments where administrators do not have mailboxes? How do they change EAC’s language if they do not have access to OWA? In this case, you can specify the language you want to use in the URL itself. For example, for American English, add ?mkt=EN-us to the EAC’s URL: https://mail.domain.com/ecp?mkt=EN-us

This works for both on-premises Exchange 2013 and above, and Office 365.

Thursday, June 25, 2020

How to Disable Microsoft Stream

As an Office 365 Global Admin, you can prevent some or all users in your organisation from being able to use Stream. This is typically done in cases where employees are already using Office 365 Video or if Stream has not been approved by all parts of your organisation.

When a first user from any organisation signs-up for Microsoft Stream using their corporate credentials, Microsoft Stream service is registered as an application in the organisation's Office 365 tenant. The license is automatically assigned to the user who signed up. There are additional licenses available for the IT admin to assign to other users in the organization without them going through the sign up process.

Important: as an admin, you can unlicense users from the Office 365 admin center. However, even though you might have removed Microsoft Stream license from a user, they have the option to sign-up via a free trial and get access to your organisation's Stream portal.

 

To turn off Microsoft Stream for everyone, follow these steps:

  1. As an Office 365 Global Admin, log in to the Azure portal;
  2. In the Azure Active Directory (AAD) section, click Enterprise Application, and then in the Manage section, click All Applications;
  3. Change the filters to Show All Applications with All Application Status, and then click Apply;
  4. Search for Microsoft Stream Service from the list of applications, or search by the GUID 2634dd23-5e5a-431c-81ca-11710d9079f4 for Microsoft Stream Service:
  1. Click the Microsoft Stream Service application, and then under Manage, click Properties;
  2. Set Enabled for users to sign-in flag to No;
  3. Click Save:

 

You have now blocked access to all users for Stream. When a user now tries to access Stream, they will receive the following error message:

Wednesday, June 24, 2020

RecipientNotFoundPermanentException when migrating a mailbox to Exchange Online

The other when migrating a mailbox from an on-prem Exchange 2013 environment to Exchange Online, I was faced with a RecipientNotFoundPermanentException error with the message “Error: Cannot find a recipient that has mailbox GUID '39c970e4-4869-47ee-b9af-f6fd6264ee0a'.”

 

These were the details for the mail user in Exchange Online (notice the ExchangeGuid attribute):


 And these were the details for the mailbox on-premises:

  

As you can see, the ExchangeGUID was the same! And where was that '39c970e4-4869-47ee-b9af-f6fd6264ee0a' GUID coming from?!

After much troubleshooting, I ended up logging a support ticket with Microsoft. According to the engineer, “this issue is caused due to multiple mailbox shards”. Unfortunately, he wouldn’t go into details as apparently it is all secret stuff... But he ran some diagnostics to resolve the issue, and after a few minutes all was well with the world again!

Block emails delivered directly to user@domain.onmicrosoft.com or user@domain.mail.onmicrosoft.com (MX Bypass)

Let’s assume I have my mailbox in Exchange Online with an email address of nuno@domain.com. External users can send emails directly to nuno@domain.onmicrosft.com (the tenant’s default domain) or to nuno@domain.mail.onmicrosoft.com (typically used in Hybrid environments) and they will be delivered to my mailbox. This is because these two managed domains are fully managed by Microsoft and have internet-routable MX records:


Not a big problem for “pure” Office 365 organizations as these emails are still subject to Exchange Online Protection (EOP) policies such as anti-spam and anti-malware. However, this is a problem for organizations that use a 3rd-party service for message hygiene. In this scenario, these emails would completely bypass the 3rd-party and all the security measures it enforces.

There are a few solutions out there that rely on mail flow (transport) rules to either reject or redirect these emails back to the 3rd-party provider, but these never quite worked for me... The main reason is that transport rules are processed post onresolveMessage event and when this happens the email address for the recipient changes to the PrimarySMTPAddress which has the domain.com suffix.

The solution I ended up implementing was slightly different. I created an Inbound Partner Connector, set the sender domain address space to *, and restricted it to only accept emails if the sender presents the certificate I have configured on my on-prem Hybrid servers (you can also restrict it to your on-prem or 3rd-party IP addresses). The following procedure assumes that we have a setup in which we have executed the Hybrid Configuration Wizard (HCW) successfully (which would have created the required connectors for mail flow).

First, we run the following command to get a list of inbound connectors configured by HCW. If you have Hybrid across multiple Exchange Organizations, then you’ll see more than one entry.

$onpremorg = Get-OnPremisesOrganization | Select OrganizationGuid, InboundConnector | Where {$_.InboundConnector -ne $null}

$onpremorg


If the Inbound Connector was configured by the HCW, then the attribute TlsSenderCertificateName will map either to the domain name included in the subject or subject alternate name attribute of the 3rd-party certificate used, or it will contain the details of the certificate.

Next, we create the inbound partner connector using the following command:

New-InboundConnector -Name “Block Direct Delivery” -ConnectorType Partner -SenderDomains * -TlsSenderCertificateName (Get-InboundConnector $onpremorg[0].InboundConnector).TlsSenderCertificateName -RestrictDomainsToCertificate $True -RequireTls $True


Once this connector is in place, any external senders that try to email nuno@domain.onmicrosft.com or nuno@domain.mail.onmicrosoft.com will receive the following NDR:


Emails sent to Exchange Online mailboxes routed through the on-prem Exchange servers, will always use the Inbound connector created by the HCW, while service emails like notifications from Teams, SharePoint Online, etc., will use the default connectors.

If you use a 3rd-party service for message hygiene but your emails flow directly from the 3rd-party to Exchange Online, you can still use this method, but instead of restricting the connector to a certificate using the RestrictDomainsToCertificate parameter, you can restrict it to the IP(s) of your 3rd-party service by using the RestrictDomainsToIPAddresses parameter.

Monday, June 22, 2020

Azure AD Connect “An error occurred while connecting to the state store: attempted to perform an authorized operation”

The other day, while trying to enable an optional feature on a staging Azure AD Connect server, I came across the following error:


The Trace log, located at C:\ProgramData\AADConnect, had the following:
[06:42:08.604] [  1] [INFO ] MicrosoftOnlinePersistedStateProvider.Save: saving the persisted state file
[06:42:08.605] [  1] [INFO ] MicrosoftOnlinePersistedStateProvider.UpdateFileProtection: updating file protection from the persisted state file: C:\ProgramData\AADConnect\PersistedState.xml, isAddProtection: False
[06:42:08.607] [  1] [ERROR] PerformConfigurationPageViewModel: Caught exception when connecting to persisted state store.
Exception Data (Raw): System.UnauthorizedAccessException: Attempted to perform an unauthorized operation.
   at System.Security.AccessControl.Win32.SetSecurityInfo(ResourceType type, String name, SafeHandle handle, SecurityInfos securityInformation, SecurityIdentifier owner, SecurityIdentifier group, GenericAcl sacl, GenericAcl dacl)
   at System.Security.AccessControl.NativeObjectSecurity.Persist(String name, SafeHandle handle, AccessControlSections includeSections, Object exceptionContext)
   at System.Security.AccessControl.FileSystemSecurity.Persist(String fullPath)
   at Microsoft.Online.Deployment.Types.PersistedState.MicrosoftOnlinePersistedStateProvider.UpdateFileProtection(String fileName, Boolean isAddProtection)
   at Microsoft.Online.Deployment.Types.PersistedState.MicrosoftOnlinePersistedStateProvider.Save(PersistedStateContainer state)
   at Microsoft.Online.Deployment.Types.PersistedState.MicrosoftOnlinePersistedStateProvider.SetStateElements(IEnumerable`1 elements)
   at Microsoft.Online.Deployment.OneADWizard.UI.WizardPages.PerformConfigurationPageViewModel.SavePersistedState()

Looking at the properties of the PersistedState.xml file, located in the same directory, I noticed it was set to Read-Only:



And that Everyone only had read access (the special permissions only block deletion) with no other users specified:


Comparing this to a “healthy” server, the configuration was the same. Nonetheless, I temporarily gave the service account full access to the file, and it worked!

Tuesday, June 2, 2020

Collecting Microsoft Teams Logs

Microsoft Teams has three different types of logs, which get stored on different locations.

Web Logs
Consists of most Teams client activity.

  • Windows: to capture logs, open Teams and press CTRL+ALT+SHIFT+1 in the client, and the logs will be downloaded to %downloads%\MSTeams Diagnostics Log.txt
  • MAC: press Command+Option+SHIFT+1 in client to download logs to Downloads\MSTeams Diagnostics Log.txt


Desktop Logs
This log has most information about framework and bootstrapping information, app bootstrap process, plugin initialization, update management and SSO/ADAL Sign in information.

  • Windows: %appdata%\Microsoft\Teams\logs.txt
  • MAC: ~/Library/Application Support/Microsoft/Teams/logs.txt


Media Stack Logs
This log has media connectivity related information.

  • Windows: %appdata%\Microsoft\Teams\media-stack
  • MAC: ~/Library/Application Support/Microsoft/Teams/media-stack


Microsoft Teams Mobile client (iOS):
To collect log on Teams mobile client, open Microsoft Teams app -> Settings -> Report an issue. A new email will open with log.txt attached.

Sunday, March 1, 2020

Gather Microsoft Teams Statistics using Graph API

This script uses Graph API to gather statistics regarding Microsoft Teams.


IMPORTANT:
  • You will need to have, or create, an 'app registration' in Azure and create a 'client secret';
  • The app registration will need the following API permissions to Graph API: 'Group.Read.All' and 'User.Read.All', both of type 'Application'.


The script gathers and exports the following stats for each team:

  • Team: the display name of the team;
  • Description: the team's description as set by its owner;
  • Email: the team's email address;
  • CreatedOn: the date and time the team was created;
  • RenewedOn: the date and time when the team was renewed;
  • Visibility: if the team is 'Private' or 'Public';
  • Owners: the number of owners;
  • OwnerNames: the display name of all the team's owners;
  • Members: the number of members;
  • Channels: the number of channels for the team;
  • ChannelNames: the display name of all the team's channels.

To call the script, simply run:
.\GetTeamsDetails.ps1

Exchange Online Meeting Room Statistics - Graph API

A few years ago I wrote an article named "Exchange Meeting Room Statistics" about a script to gather statistics regarding Exchange meeting room usage for MSExchange.org (now techgenix.com). For this script to work, we have to give ourselves FullAccess to the meeting rooms’ mailbox, add them into our Outlook profile, and then use an Outlook COM Object to connect to Outlook and gather this information. Far from ideal, especially when trying to analyse dozens of rooms!

A couple of years after, I wrote a new version that uses Exchange Web Services to gather the same information, plus some further stats. However, this script wasn't very reliable with Exchange Online as it would work in some environments, but not in other.

I have finally written this newer version, specifically targeted at Exchange Online only, this time using Graph API!


IMPORTANT:
  • To analyze a particular meeting room, specify one or more primary SMTP addresses in the format: "room1@domain.com, room2@domain.com". Alternatively, analyze all meeting rooms by using the "-All" switch;
  • You will need to have, or create, an 'app registration' in Azure and user digital certificate for authentication (you can update the script to use 'client secret' instead);
  • The app registration will need the following API permissions to Graph API: 'User.Read.All', 'Calendars.Read', and 'Place.Read.All', all of type 'Application';
  • Maximum range to search is 1825 days (5 years);
  • You can enter the dates in the format "22/02/2020", "22/02/2020 15:00", or in ISO 8601 format such as "2020-02-22T15:00:00", or even "2020-02-22T15:00:00-08:00" to specify an offset to UTC (time zone).


The most basic way of running the script is as follows (more examples in the script itself):
.\Get-MeetingRoomStats_GraphAPI.ps1 -All -From "01/01/2020" -To "01/02/2020"


The script gathers and exports the following stats for each meeting room for the given date range:
  • RoomName: the display name of the meeting room (when using -All). When using -RoomListSMTP, this will be the room's SMTP address;
  • RoomSMTP: the SMTP address of the meeting room;
  • From: the start of the date range to search the calendar;
  • To: the end of the date range to search the calendar;
  • totalMeetings: the total number of meetings;
  • totalDuration: the total number of minutes for all meetings;
  • totalAttendees: the total number of attendees invited across all meetings;
  • totalUniqueOrganizers: the number of unique meeting organizers;
  • totalUniqueAttendees: the number of unique attendees;
  • totalReqAttendees: the total number of required attendees;
  • totalOptAttendees: the total number of optional attendees;
  • Top5Organizers: the email address of the top 5 meeting organizers, and how many meetings each scheduled;
  • Top5Attendees: the email address of the top 5 meeting attendees, and how many meetings each attended;
  • totalAllDay: the total number of 'all-day' meetings;
  • totalAM: the total number of meetings that started in the morning (this excludes all-day meetings);
  • totalPM: the total number of meetings that started in the afternoon;
  • totalRecurring: the total number of recurring meetings;
  • totalSingle: the total number of non-recurring meetings (single instance/occurrence).

Thursday, November 21, 2019

How to disable Office 365 self-service purchase

I am sure by now you have heard or read about Microsoft plans to allow Office 365 users to make self-service purchases for the Power Platform. This created such a storm of critics and complaints that Microsoft postponed the introduction of this feature to January 14, 2020. Additionally, it has finally released a way for admins to block such feature.

“Beginning January 14, 2020, self-service purchase, subscription, and license management capabilities for Power Platform products (Power BI, Power Apps, and Power Automate) will be available for commercial cloud customers in the United States. Self-service purchase gives users a chance to try out new technologies and lets them develop solutions that will ultimately benefit their larger organizations. This capability will not be available to tenants in the US that are government, nonprofit, or education, at this time. Central procurement and IT teams will have visibility to all users buying and deploying self-service purchase solutions through the Microsoft 365 admin center, and will be able to turn off self-service purchasing on a per product basis via PowerShell.”

On November 19, Microsoft updated their self-service FAQ and stated that “Admins can also control whether users in their organization can make self-service purchases. For more information see Use AllowSelfServicePurchase for the MSCommerce PowerShell module.”

The MSCommerce PowerShell module is now available on PowerShell Gallery. The module includes a PolicyID parameter value for AllowSelfServicePurchase that lets you control whether users in your organization can make self-service purchases.

You can use the MSCommerce PowerShell module to:
• View the default state of the AllowSelfServicePurchase parameter value — whether it's enabled or disabled;
• View a list of applicable products and whether self-service purchase is enabled or disabled;
• View or modify the current setting for a specific product to either enable or disable it.

To use the MSCommerce PowerShell module, you need:
• A Windows 10 device;
• Administrator permission for the device;
• Global or Billing Admin role for your tenant.

Install the MSCommerce PowerShell module
Download the MSCommerce PowerShell module from the PowerShell Gallery. To install the MSCommerce PowerShell module with PowerShellGet, run the following command:
Install-Module -Name MSCommerce

Import MSCommerce into the PowerShell session
After you install the module, import it into the PowerShell session by running the following command:
Import-Module -Name MSCommerce

Connect to MSCommerce
Finally, connect to the PowerShell module with your credentials. This command connects the current PowerShell session to an Azure Active Directory tenant. The command prompts you for a username and password for the tenant you want to connect to. If multi-factor authentication is enabled for your credentials, you use the interactive option to log in.
Connect-MSCommerce

View details for AllowSelfServicePurchase
To view a description of the AllowSelfServicePurchase parameter value and the default status, based on your organization, run the following command:
Get-MSCommercePolicy -PolicyId AllowSelfServicePurchase | FL



View a list of self-service purchase products and their status
To view a list of all available self-service purchase products and the status of each, run the following command:
Get-MSCommerceProductPolicies -PolicyId AllowSelfServicePurchase





View or set the status for AllowSelfServicePurchase
After you view the list of products available for self-service purchase, you can view or modify the setting for a specific product. To get the policy setting for a specific product, run the following command:
Get-MSCommerceProductPolicy -PolicyId AllowSelfServicePurchase -ProductId CFQ7TTC0KP0N



To enable the policy setting for a specific product, run the following command:
Update-MSCommerceProductPolicy -PolicyId AllowSelfServicePurchase -ProductId CFQ7TTC0KP0N -Enabled $True

To disable the policy setting for a specific product, basically preventing users from purchasing licenses themselves, run the following command:
Update-MSCommerceProductPolicy -PolicyId AllowSelfServicePurchase -ProductId CFQ7TTC0KP0N -Enabled $False



To disable all three at the same time, you can use the following command:
Get-MSCommerceProductPolicies -PolicyId AllowSelfServicePurchase | ForEach {Update-MSCommerceProductPolicy -PolicyId $_.PolicyID -ProductId $_.ProductID -Enabled $False}

Sunday, September 15, 2019

MapiExceptionMessagePerFolderCountQuotaExceeded

The other day during a mailbox migration of an on-prem Exchange 2013 mailbox to Exchange Online, I came across the following error:
Error: QuotaExceededException/MapiExceptionMessagePerFolderCountQuotaExceeded: Error: Cannot save changes made to an item to store. --> MapiExceptionMessagePerFolderCountQuotaExceeded: Unable to save changes.

The message was due to a folder in the user’s mailbox which had almost 1,500,000 items in it. The problem is that Exchange Online enforces a maximum number of 1M messages per mailbox folder limit (across all Exchange Online plans). New messages can’t be delivered or saved in a folder when this limit is reached.


There is also a warning for number of messages per mailbox folder. Once a folder in Exchange Online reaches 900,000 items, Exchange Online sends a warning message to the mailbox owner. When this quota is reached, warning messages are sent once a day.


To check which folder(s) in a user’s mailbox is causing this, you can use the Get-MailboxFolderStatistics cmdlet:
Get-MailboxFolderStatistics "user" | % {If ($_.ItemsInFolder -ge 1000000) {Write-Host "Folder with $("{0:N0}" -f $($_.ItemsInFolder)) items: '$($_.FolderPath)'" -ForegroundColor Red}}

It is also crucial to check other limits before starting to sync mailboxes, like mailbox size limit, folder limit, and so on.

Hope it helps!

Sunday, March 31, 2019

The onboarding move could not be created because user ‎is already being moved

When migrating mailboxes to Exchange Online in a Hybrid environment, you might encounter the following error:
PS C:\> Get-MigrationUser user@domain.com | FL

Identity            : user@domain.com
BatchId             : Pilot Migration 1
MailboxEmailAddress : user@domain.com
RecipientType       : MailboxOrMailuser
Status              : Failed
StatusSummary       : Failed
MigrationType       : ExchangeRemoteMove
State               : Failed
WorkflowStep        : DataMigration
WorkflowStage       : Injection
ErrorSummary        : The onboarding move could not be created because user 'User' is already being moved.

If you have already checked all your migration batches (Get-MigrationBatch) and/or all your individual move requests (Get-MoveRequest “user”) and there is absolutely no move request associated with this user, then it is very likely you still have a move request for the user in your on-premises Exchange environment.

So, go to your on-premises environment and remove the local move request for that particular user. This should clear the msExchMailboxMoveRemoteHostName AD attribute. Other attributes related to move requests that you can also check are:
  • msExchMailboxMoveBatchName
  • msExchMailboxMoveFlags
  • msExchMailboxMoveRemoteHostName
  • msExchMailboxMoveSourceMDBLink
  • msExchMailboxMoveStatus
  • msExchMailboxMoveTargetMDBLink

Wednesday, February 6, 2019

How to Update Office 365 Groups Primary Email Address

The other day I got asked if it was possible to update the primary SMTP address of multiple Office 365 Groups in one go. It turns out that, for this particular tenant, the default domain was still the tenantname.onmicrosoft.com one, meaning all the groups created until then had an email address of group@tenantname.onmicrosoft.com instead of group@domain.com.

To do this, we have to use PowerShell as it is not possible to change the email address of such group using the EAC.


First, we connect to Exchange Online PowerShell, and then we need to use the *-UnifiedGroup cmdlet.

To retrieve the list of groups that need to be updated, we use the following code:
Get-UnifiedGroup -ResultSize Unlimited | Where {$_.PrimarySmtpAddress -like "*tenantname.onmicrosoft.com"}


To update the primary SMTP address of an Office 365 Group, we use the Set-UnifiedGroup cmdlet with the PrimarySmtpAddress parameter:
Set-UnifiedGroup “Test Group” -PrimarySmtpAddress “test.group@nunomota.pt”

Below you can see that the existing primary email address becomes an alias of the group, so no alias/email addresses are lost:



To do this is a large scale, we can use a script like the following:
Get-UnifiedGroup -ResultSize Unlimited | ? {$_.PrimarySmtpAddress -like "*tenantname.onmicrosoft.com"} | % {
  $smtp = $_.PrimarySmtpAddress
  $newSmtp = $smtp.Split("@")[0] + "@domain.com"
  
  # If all the groups’ email addresses are in the alias@ format, then we can simply use the following instead
  # $newSmtp = "$($_.Alias)@domain.com"

  Write-Host "Updating ""$($_.DisplayName)"" from $smtp to $newSmtp"
  Set-UnifiedGroup $_.Identity -PrimarySmtpAddress $newSmtp
}

Saturday, August 4, 2018

Installing Exchange Server 2019 Preview on Windows Server 2019 (Core and GUI)

Amongst other big changes, such as the lack of the Unified Messaging role from Exchange Server 2019, the biggest announcement has to be the fact that this is the first version of Exchange that can be installed on a Windows Server Core (either 2016 or 2019)!

So, without further ado, let’s look at how we install the Preview version of Exchange 2019 on a Windows Server 2019 Core as well as on a Windows Server 2019 with a GUI (Desktop Experience).

Preparing Windows for Exchange
After downloading the ISO or Hyper-V image from the Windows Insider Preview website, install the OS as you would normally. In this case, I am using Hyper-V server on a Windows 10 machine. After the OS is installed, the first step is to change the default Administrator’s account password:


Once this is done, we can either use SConfig.cmd or PowerShell to configure the server’s name, domain, network and other settings:

To use PowerShell instead, run start powershell from the command line, which will open a PowerShell window:

Using PowerShell, we can use the Get-NetIPAddress cmdlet to retrieve the network adapters on the server, and then New-NetIPAddress and Set-DNSClientServerAddress to configure it. For example:
New-NetIPAddress -InterfaceIndex 5 -IPAddress 192.168.1.10 -PrefixLength 24 -DefaultGateway 192.168.1.1
Set-DNSClientServerAddress -InterfaceIndex 5 -ServerAddress “192.168.1.2”

To rename and join the server to the nunomota.pt domain, we could use the following cmdlet:
Add-Computer -DomainName nunomota.pt -NewName EX1 -DomainCredential nunomota\admin

Note that these PowerShell cmdlets can be used to configure both the Core and on the Desktop Experience versions of Windows Server.

Windows Features and Additional Software
We will let Exchange Setup configure the required Windows Features it needs, but there are two we have to install beforehand as they are required for Microsoft UCMA 4.0 and to prepare Active Directory:
Install-WindowsFeature Server-Media-Foundation, RSAT-ADDS

Next, we need to enable file sharing on our Core server, so we can copy some required files to the server. This can be done using the following cmdlet:
netsh advfirewall firewall set rule group="File and Printer Sharing" new enable=yes

Next, from another workstation, download the following software and copy it over to the server:

To install Visual C++, navigate to the location where you copied the file to, and simply run vcredist_x64.exe:


Now we need to install UCMA (Microsoft Unified Communications Managed API 4.0), which comes in the Exchange 2019 media itself. On the Core server, start by mounting the Exchange ISO file:
Mount-DiskImage C:\full_path\ExchangeServer2019.iso

Once this is done, go to the UCMARedist folder, and run Setup.exe to start the installation:

Finally, restart the server:
Restart-Computer -Force


Exchange Installation (Core)
After restarting the server, re-mount the Exchange ISO image and run an unattended installation as you normally would. We can either prepare Active Directory and the Domains separately or let the Setup do everything. In this case, let’s do everything separately.

First we use the /PrepareSchema switch to extend the Active Directory schema:

Next, the /PrepareAD switch to prepare Active Directory:

Then we use /PrepareAllDomains to prepare all the Active Directory domains:

Finally, we can install Exchange, and all the required OS components, using the same method we are used to with previous versions:
.\Setup.exe /Mode:Install /Roles:Mailbox /IAcceptExchangeServerLicenseTerms /InstallWindowsComponents

Once the installation completes, we can launch the Exchange Management Shell using LaunchEMS command from the command line:

Done! From here, we can manage this server using a variety of methods such as the Exchange Management Shell, Exchange Admin Center, remote PowerShell, and all the Windows management tools.


Exchange Installation (GUI)
As for the Desktop Experience version of Windows, we can obviously install Exchange unattended, or follow the installation wizard, which is pretty much identical to Exchange 2016:

Once installation has complete, we have the usual links in the Start menu:

We have our Exchange Management Shell:

And the Exchange Admin Center:

From a user perspective, they get the “new” Outlook in the Web :)