Michel de Rooij, with over 25 years of mixed consulting and automation experience with Exchange and related technologies, is a consultant for Rapid Circle. He assists organizations in their journey to and using Microsoft 365, primarily focusing on Exchange and associated technologies and automating processes using PowerShell or Graph. Michel's authorship of several Exchange books and role in the Office 365 for IT Pros author team are a testament to his knowledge. Besides writing for Practical365.com, he maintains a blog on eightwone.com with supporting scripts on GitHub. Michel has been a Microsoft MVP since 2013.
When processing strings, you may sometimes need to split those strings into several parts based on criteria. For example, you may need to split the ActiveSync DeviceUserAgent string, which can be used to identify the model and version of a device, for example:
Apple-iPhone4C1/1002.142
Apple-iPad2C1/1002.141
Apple-iPhone4C1/1001.523
Now let’s assume you want to take this string apart in the device model and the version, using the slash as a marker. When asking 10 people, a majority will come up with something along the following kind of structure:
Of course, this works but you need to understand what’s going on and additional code is required the handle situations when there’s no “/” present. Perhaps a more elegant and readable way, especially if you want to split the string in more than 2 parts, is using regular expressions to perform pattern matching. When used in combination with named groups, you will get easily referable parts using a name.
For the purpose of demonstrating it’s power, we’ll start by assigning some user agent strings to a variable. Note that you could use the DeviceUserAgent property of a collection of ActiveSync devices as well.
The pattern to look for in each user agent string is it starts with some characters, followed by a slash, followed by a number, followed by a dot and ending in a number. Translated to regular expression, the pattern would be:
^(.*)/(\d+)\.(\d+)$
where:
^ marks the start of the subject;
(.*) marks a sub pattern of any character, the * indicates it’s present zero, one or more times;
The slash is marked by (surprise!) a /;
(\d+) marks a sub pattern of decimal digit, the + indicates it’s used one or more times;
\. will match the dot (escaping it with “\” makes sure next character is used literally as ‘dot’ normally means any character);
$ marks the end of the subject.
When matching a pattern against a string you can use –match, e.g. “string” –match “pattern”. When this results in True, a match is found; when the result is False, the format of the string is invalid (of course requires properly defined pattern). After performing a match, the predefined variable $matches will contain an array containing the results, where element [0] contains the complete matching string and [1] .. [n] each matching (sub) pattern.
You see that each match returns True (match found) and $matches[1] for example contains the first sub pattern for each match, i.e. the device. This is nice and perhaps neater than splitting strings as mentioned in the start of this article, but wouldn’t it be even cooler if you can refer to those parts using names, e.g. device?
Here’s where named groups come into play and you can compare it to PowerShell’s calculated properties (select @{Name=”KB”; Expression={$_.Size/1kb}}) or column aliases in SQL (SELECT ColA as Name). To use named groups, put “?<name>” at the start of the (sub) pattern. For example, to use the name “device” for the first sub pattern “(.*)”, the expression would become “(?<device>.*)”. The complete pattern using named groups would then become something like:
Note that I’ve added casting (i.e. converting a variable to a different type) the output of “-match” to [void] so the match results (True or False) won’t be part of the output.
Having named groups now allows us to use easily match individual parts, filter or group information, e.g.:
Of course it’s a matter of taste, but having this information available as $matches.device instead of $matches[1] makes introducing changes more easy (no need to renumber when inserting/removing a sub pattern) and results in more readable code.
Somebody must be happy with my recent contributions to the Exchange community.
This week I received an e-mail from Matt Gervais of TechTarget who mentioned I was nominated for their “Profiling the best Exchange Server professionals” award and if I’d like to accept the honor as their February 2013 recipient.
For those who don’t know TechTarget, they provide information aimed at IT professionals. Exchange people might know them from their Exchange-related searchexchange.techtarget.com site. This site publishes articles from Exchange MVPs like The UC Architects’ Steve Goodman, Andry Grogan or J. Peter Bruzzese.
Recognition from the community is always nice and is a great motivator, so to whoever nominated me: a big thank you. As part of the related article, Matt also asked me some quick questions on how I became involved with Exchange and why I love doing my job. You can read the short interview here.
A quick heads-up for the ones which didn’t receive the notification via e-mail or social media: Microsoft announced the next Microsoft Exchange Conference is going to be held in April 2014:
So block your agendas and plan those budgets or submit requests to management for 2014 early!
I’m pleased to announce the availability of Install-Exchange15.ps1, a PowerShell script to perform a fully automated unattended setup of Exchange Server 2013, Exchange Server 2016, or Exchange Server 2019 (Desktop and Core) is supported).
The script takes care of:
Installing requires Windows Server features
Install Exchange Server prerequisites, e.g., .NET Framework 4.5.2/4.6.1/4.6.2/4.7.1/4.7.2/4.8/4.8.1 and Visual C++ Runtime 2012 or 2013, depending on roles, OS, and Exchange version to install.
Install additional prerequisites and prepare Active Directory.
Optionally install Exchange Server 2013 / 2016 / 2019.
Optionally, install required fixes and perform post configuration, like setting your Power Plan to High Performance, reconfiguring the pagefile to best practices (memory + 10MB with a maximum of 32GB+10MB) if it is system managed, and performing .NET framework optimizations. Custom post-configuration is possible by modifying the script.
On Windows Server 2016 and later, it will configure Windows Defender exclusions when present.
For Exchange 2016 CU22 and Exchange 2019 CU11 and later, will install the required URL Rewrite 2 module.
Finally, the script will clean things up, like removing the state file and setting the startup of Transport Service back to Automatic.
Usage This script version requires a domain-joined Windows Server, an account to perform the installation (and optionally prepare Active Directory), and the location where the Exchange Server 2013/2016/2019 installation files are stored (e.g., a UNC path).
Organization (optional): Specifies the name of the Exchange organization to create. When omitted, the step to prepare Active Directory (PrepareAD) will be skipped.
InstallMailbox: Specifies you want to install the Mailbox server role. This applies to Exchange 2013 as well as Exchange 2016.
InstallCAS: Specifies you want to install the CAS role. This applies to Exchange 2013 only, ignored when installing Exchange 2016.
InstallMultiRole: Specifies you want to install both Mailbox server and CAS roles. Applies to Exchange 2013 only.
InstallEdge: Specifies to install the Edge Transport rule (Exchange 2013/2016).
MDBName (optional): Specifies the name of the initially created database.
MDBDBPath (optional): Specifies the database path of the initially created database (requires MDBName).
MDBLogPath (optional): Specifies the log path of the initially created database (requires MDBName).
InstallPath (optional): Specifies (temporary) location of where to locate – and when downloaded store – prerequisite files, the state file, and log files. The default location is C:\Install. You can also use a UNC path to use a central location, given the credentials have sufficient permissions to write at this location. This is ideal when you want the script to use previously downloaded hotfix files, for example, as some required hotfixes are quite large (e.g. KB3206632 for WS2016 ~ 1GB, KB2919355 for WS2012R2 ~ 700MB).
NoSetup (optional): Specifies you only want to install prerequisites (and optionally prepare the Exchange organization), Exchange setup and post-configuration steps are not performed. You still need to specify SourcePath because the Exchange version will determine the prerequisites to install.
Recover: Specifies you want to install this server in Recovery mode. The script will check if an Exchange server object is already defined.
SourcePath: Specifies the location of the Exchange 2013 installation files. This can point to the location of setup.exe, or you can specify the Exchange ISO file.
TargetPath: Specifies the location where to install the Exchange 2013.
AutoPilot (switch): Specifies you want to automatically restart, log on using the credentials specified, and continue the installation. When not specified, you will need to restart, log on, and start the script manually each time (without parameters).
Credentials (optional): Specifies credentials to use for automatic logon. Use DOMAIN\User or user@domain. When not specified, you will be prompted to enter credentials.
IncludeFixes (optional): Depending on the operating system and detected Exchange version to install, will download and install recommended hotfixes.
DiagnosticData (optional): This switch determines the initial Data Collection mode for deploying Exchange 2019 CU11, Exchange 2016 CU22, or later builds.
DoNotEnableEP Do not enable Extended Protection on Exchange 2019 CU14+
DoNotEnableEP_FEEWS Do not enable Extended Protection on the Front-End EWS virtual directory on Exchange 2019 CU14+
SCP (optional) allows you to reconfigure the Service Connection Point record for Autodiscover after the Exchange setup has finished. Specify the full URI, e.g. https://autodiscover.contoso.com/autodiscover/autodiscover.xml. Use ‘-‘ to clear the SCP entries of the server.
Lock (optional) locks the system when running script.
NoNet481 (optional) prevents installing .NET Framework 4.8.1 and uses 4.8 when deploying Exchange 2019 CU14+
NoNet48 (optional) to use .NET Framework 4.7.2, even when installing an Exchange version that is supported with .NET Framework 4.8.
NoNET471 (optional) to use .NET Framework 4.6.2, even when installing an Exchange version which is supported with .NET Framework 4.7.1.
NoNET472 (optional) to use .NET Framework 4.7.1, even when installing an Exchange version which is supported with .NET Framework 4.7.2.
NoNET461 (optional) to use .NET Framework 4.5.2, even when installing an Exchange version which is supported with .NET Framework 4.6.1 or higher.
DisableSSL3 (optional) to disable SSL3 protocol as per KB187498.
DisableRC4 (optional) to disable RC4 cipher as per KB2868725.
SkipRolesCheck (optional) to bypass membership checks for Schema Admin and Enterprise Admin roles.
EdgeDNSSuffix specifies the DNS suffix to configure on the primary NIC.
Note that the script uses an XML file to store the (original) parameters used to start the script but also to keep track of the the process. Of course, if required, you can use predefined XML files to run the script without parameters.
Note that when not present, the script will try to download the prerequisites from the internet. When that isn’t possible or to save bandwidth, you can put them in the location defined by InstallPath and the script will detect and use them.
The post-configuration is currently adding IFilters for OneNote and Publisher (Mailbox) only. There are comments in the script where to add your own additional post-configuration steps.
For example, assume we want to start a fully unattended install of an Exchange Server 2013 Client Access server, using a network location for the Exchange Server 2013 source files. After setting the Execution Policy to Unrestricted and storing the script locally, we start the script using:
The script will perform some checks and since AutoPilot was specified without using the Credentials parameter, the script will ask for credentials.
After entering the credentials, the required features will be installed. Since OrganizationName wasn’t specified, Active Directory preparation will be skipped.
After rebooting, the system will automatically log on using the credentials specified earlier and start the script (RunOnce registry key is utilized for this purpose). It will read the last known state from the XML file and will continue with the next phase, which is downloading (when not present) and installing the Exchange prerequisites.
Next, after rebooting and the automatic logon, Exchange will be installed from the source location.
When done, the system will perform post-configuration and finalization steps.
When running in AutoPilot mode, the system will automatically perform reboots and logons between the steps. Note that it may seem like a lot of reboots, but rebooting after installing Windows features and Exchange prerequisites is required anyway, so I also put reboots after the other milestones.
Customization If you want to perform post-setup configuration of Exchange running Exchange cmdlets from the script, you need to tailor it to your needs. Locate the line which reads:
#Load-ExchangeModule
Uncomment this line so a proper Exchange Management Shell session will be set up to the local Exchange server. You can insert Exchange-related cmdlets after the Load-ExchangeModule line to configure your server. Be advised that you need to port modifications to new versions of the installation script.
Recovery The script also supports recovery mode (/mode:RecoverServer). After checking the Exchange server object is present in Active Directory, installation will proceed as normal, with the exception of running setup in recovery mode. For example:
Update The script also supports update mode (/mode:Update). After checking the Exchange server object is present in Active Directory, and checking for presence of Exchange installation, installation will proceed as normal, with the exception of running setup in Update mode.
Feedback Feedback is welcomed through the comments. If you have scripting suggestions or questions, do not hesitate to use the contact form.
Download You can download Install-Exchange15.ps1 from TechNet or GitHub.
With the release of Exchange 2010 SP3, you can now control where sent items are stored when Send-As or Send-on-Behalf permissions are configured and you’re using Outlook Web Access. In Outlook we already had the possibility to control where sent items are stored this using registry keys, but now we can do so from Outlook Web Access or using the Exchange Management Shell. Unlike Outlook, you unfortunately can’t control where deleted items are stored.
As a quick reminder, to configure permissions for user Francis to send e-mail messages as Philip or on behalf of Philip, you would use:
Add-ADPermission Philip -User Francis -Extendedrights "Send As"Set-Mailbox Philip -GrantSendOnBehalfTo Francis
Sent Items Management from Outlook Web Access To access the Sent Items Management option, use OWA to open the (shared) mailbox and navigate to Settings > Sent Items
You can configure the following options for Send-As or Send-on-Behalf e-mail:
From mailbox: The sent item will be stored in the Sent Items folder of the (shared) mailbox;
Sender and From mailboxes: The sent item will be stored in the Sent Items folder of the (shared) mailbox and actual sender;
Sender mailbox: The sent item will be stored in the Sent Items folder of the actual sender (default).
Sent Items Management using EMS
To inspect where sent items are stored, use Get-MailboxSentItemsConfiguration, e.g.
Get-MailboxSentItemsConfiguration Philip
To configure the settings, use Set-MailboxSentItemsConfiguration in conjunction with SendAsItemsCopiedTo or SendOnBehalfOfItemsCopiedTo specifying Sender, From or SenderAndFrom. For example, to configure the items sent as Philip to be stored in the mailbox of both Philip and the actual sender, use the following cmdlet:
Set-MailboxSentItemsConfiguration Philip –SenderAndFrom
It would be nice if Outlook would honor these settings, just like the signatures, so you configure this centrally and have consistent behavior regardless of the client setting. It shouldn’t be difficult, using Exchange Web Services as a vehicle to store and retrieve this information.