Script: Updating Recipients in Exchange Online (Office 365)

A very common request is to update recipients in Exchange Online and commonly many updates are required. Updating a few recipients in EXO can easily be done with the Office 365 Admin Portal or Exchange Admin Console (ECP), but due to human error can lead to mistakes. When more than a few recipients need updates, scripting is needed.


The script below will cover five key functions. 1) Connecting to Azure AD/EXO/O365, if not connected already, 2) Getting input from a CSV file, 3) Detecting the recipient type, 4) Updating the properties of an existing recipient, and 5) Creating a Contact when an existing recipient is not found.

1)    Connecting to Azure AD/EXO/O365

Before we can do anything in EXO/O365 we need to start a remote PowerShell session to Office 365. So at the very start of the script it will check to see if there is an existing open session to EXO. If one is not found, a new session will be created.

Before sessions can be created, the Azure AD PowerShell module must be installed on the system running this script. It can be downloaded from:

The first line below, uses the Get-PSSession cmdlet to get and then filter all session to return a count of those that are “Open” and have the ConfigurationName of “*Microsoft.Exchange*”. If this cmdlet, with the filters, returns no results, then the New-PSSession cmdlet is called. When the New-PSSession cmdlet is used, the credentials of a user, with the required rights, must be supplied. These are requested via a secure pop-up using the Get-Credential cmdlet and saved to the $UserCredential variable.

Using the $Session variable we store the session information, including credentials. This is then used by the Import-PSSession cmdlet to create the session. Finally, the session is connected to using Connect-msolservice.

If (((Get-PSSession | ?{$_.State -Like "*Open*" -And $_.ConfigurationName -Like "*Microsoft.Exchange*"}).Count) -Eq 0) {

$UserCredential= Get-Credential

$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri -Credential $UserCredential -Authentication Basic -AllowRedirection

Import-PSSession $Session

Connect-msolservice -credential $UserCredential

2)    Getting input from a CSV file

The five lines in the script example below, will read a Recipients.csv file and then process each line using a ForEach loop. In this loop, three fields are displayed: FirstName, LastName, and Email.

In this script, it is assume the CSV as the following fields in it: FirstName, LastName, Role, Email, Phone, MobilePhone, HomePhone, WorkPhone, Comments, Address, City, State, Zip, ID. These, except for ID, can map directly to existing attributes and the ID field will be stored CustomAttribute1 in the full script.

The first line uses the Import-Csv cmdlet to read in the file and then pass it, via the | (pipe), to the ForEach loop.

Import-Csv "Recipients.csv" |
ForEach {
$FullName = $_.FirstName + " " + $_.LastName
Write-Host “Full name: $FullName”
Write-Host "Looking for: $($_.Email)" -ForegroundColor Cyan   }

3)    Detecting if a recipient exists

Now that we have a PS session to O365 and the first line of the CSV ready, we need to see if the recipient existing and then based on its type use the correct cmdlets to update it.

Using the Get-Recipient cmdlet with the Email attribute from the CSV file we can query EXO for a matching recipient. By adding “-ErrorAction SilentlyContinue” we hide the error if one is not found, which makes for a cleaner running script. The $Recipient variable, which is first set to $Null to ensure that the previous value isn’t used, is set by the Get-Recipient cmdlet.

$Recipient = $Null
$Recipient = Get-Recipient $_.Email -ErrorAction SilentlyContinue
If ($Recipient -eq $Null) {
Write-Host "`tRecipient not found, creating" -ForegroundColor Yellow }
Else {
Write-Host `t"Found: $($Recipient.Name)" -ForegroundColor Green }

4)    Updating the properties of an existing recipient

Assuming the recipient exists, we 1st need to find out what type of recipient it is. To do this the script will check the RecipientType property.

There are three RecipientType value, at least supported by this script: MailContact, MailUser, & MailMailbox. A MailContact type is for objects that are mail enable, but do not have a logon and have an external email address. A MailUser is for objects that have been mail enable, with an external email address, but do have a login to O365. Last, MailMailbox is for objects that have an EXO mailbox and O365 login.

For the first two types, the Set-Contact cmdlet is used to set most of the attributes. For MailContact objects, the Set-MailContact cmdlet is used to set the CustomAttribute1 attribute and other Exchange only related attributes. For MailUser objects, Set-MailUser is needed to update the Exchange attributes. For MailMailbox objects, Set-MailMailbox is used to update the Exchange attributes.

Using three IF statements, checking the $Recipient.RecipientType value, the script calls the correct cmdlets to update the attribute. The  -WarningAction 0 parameter is used to hide the yellow warning message when the attribute value doesn’t change, since the value is the same.

If ($Recipient.RecipientType -eq "MailContact") {

Set-Contact $Properties.Email -LastName $Properties.lastname -FirstName $Properties.firstname -DisplayName $FullName -WarningAction 0
Set-MailContact $Properties.Email -CustomAttribute1 $Properties.ID -WarningAction 0

If ($Recipient.RecipientType -eq "MailUser") {

Set-User $Properties.Email -LastName $Properties.lastname -FirstName $Properties.firstname -DisplayName $FullName -WarningAction 0
Set-MailUser $Properties.Email -CustomAttribute1 $Properties.ID -WarningAction 0

If ($Recipient.RecipientType -eq "MailMailbox") {

Set-User $Properties.Email -LastName $Properties.lastname -FirstName $Properties.firstname -DisplayName $FullName -WarningAction 0

Set-MailMailbox $Properties.Email -CustomAttribute1 $Properties.ID -WarningAction 0

5)    Creating a Contact when an existing recipient is not found

The last key part of this script is to create a Contact when a recipient is not found. This is done with the New-MailContact cmdlet, this script only creates Contacts for CSV entries that aren’t found.

Inside a ForEach loop, the current object\entry being processed can be referred to using the $_ object. The cmdlet below sets five attributes to the value in the CSV for the current line being processed.

New-MailContact -Name $FullName -DisplayName $FullName -ExternalEmailAddress $_.Email -FirstName $_.FirstName -LastName $_.LastName

Putting the parts together

There are three major sections of the script, the 1st connects to Office 365, the 2nd is the UpdateRecipient function, and 3rd is the Import-Csv “Recipients.csv” and ForEach loop. The loop in the 3rd part calls the 2nd part for each line in the CSV file.

In the 3rd (Import-Csv “Recipients.csv”) part it queries EXO using the Get-Recipient cmdlet and if one is not found, the New-MailContact cmdlet is used to create a new contact. If the recipient is found or after the New-MailContact cmdlet, the existing or new object and the current value from the CSV file is passed to the UpdateRecipient function.

Below are the key lines from the 3rd section of the script:

Import-Csv "Recipients.csv" |

ForEach {

$Recipient = Get-Recipient $_.Email -ErrorAction SilentlyContinue

If ($Recipient -eq $Null) {

Write-Host "`tRecipient not found, creating" -ForegroundColor Yellow

$FullName = $_.Preferred + " " + $_.LastName

New-MailContact -Name $FullName -DisplayName $FullName -ExternalEmailAddress $_.Email -FirstName $_.FirstName -LastName $_.LastName

$Recipient = Get-Recipient $_.Email


Else {

Write-Host `t"Found: $($Recipient.Name)" -ForegroundColor Green


UpdateRecipient $Recipient $_



The 2nd part is the UpdateRecipient function, below is a partial copy of that code.

Function UpdateRecipient ($Recipient, $Properties) {

Write-Host `t"UpdateRecipient: $($Properties.Email)" -ForegroundColor Cyan

$FullName = $Properties.FirstName + " " + $Properties.LastName


If ($Recipient.RecipientType -eq "MailContact") {

Set-Contact …

Set-MailContact …


If ($Recipient.RecipientType -eq "MailUser") {

Set-User …

Set-MailUser …


If ($Recipient.RecipientType -eq "MailMailbox") {

Set-User …

Set-MailMailbox …




Full script

# CSV: FirstName, LastName, Role, Email, Phone, MobilePhone, HomePhone, WorkPhone, Comments, Address, City, State, Zip, ID


# CustomAttribute map [EXO = CSV]

# CustomAttribute1 = ID


# Connect to Office 365, if not connected

# Requires Azure AD PowerShell Module:

If (((Get-PSSession | ?{$_.State -Like "*Open*" -And $_.ConfigurationName -Like "*Microsoft.Exchange*"}).Count) -Eq 0) {

$UserCredential= Get-Credential

$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri -Credential $UserCredential -Authentication Basic -AllowRedirection

Import-PSSession $Session

connect-msolservice -credential $UserCredential



Function UpdateRecipient ($Recipient, $Properties) {

Write-Host `t"UpdateRecipient: $($Properties.Email)" -ForegroundColor Cyan

$FullName = $Properties.FirstName + " " + $Properties.LastName


If ($Recipient.RecipientType -eq "MailContact") {

Set-Contact $Properties.Email -LastName $Properties.lastname -FirstName $Properties.firstname -DisplayName $FullName -WarningAction 0

Set-Contact $Properties.Email -Phone $ -MobilePhone $Properties.mobilephone -HomePhone $Properties.homephone -OtherTelephone $Properties.workphone -WarningAction 0

Set-Contact $Properties.Email -StreetAddress $Properties.address -City $ -StateOrProvince $Properties.state -PostalCode $ -Title $Properties.Title -Notes $Properties.comments -WarningAction 0

Set-MailContact $Properties.Email -CustomAttribute1 $Properties.ID -WarningAction 0


If ($Recipient.RecipientType -eq "MailUser") {

Set-User $Properties.Email -LastName $Properties.lastname -FirstName $Properties.firstname -DisplayName $FullName -WarningAction 0

Set-User $Properties.Email -Phone $ -MobilePhone $Properties.mobilephone -HomePhone $Properties.homephone -OtherTelephone $Properties.workphone -WarningAction 0

Set-User $Properties.Email -StreetAddress $Properties.address -City $ -StateOrProvince $Properties.state -PostalCode $ -Title $Properties.role -Notes $Properties.comments -WarningAction 0

Set-MailUser $Properties.Email -CustomAttribute1 $Properties.ID -WarningAction 0


If ($Recipient.RecipientType -eq "MailMailbox") {

Set-User $Properties.Email -LastName $Properties.lastname -FirstName $Properties.firstname -DisplayName $FullName -WarningAction 0

Set-User $Properties.Email -Phone $ -MobilePhone $Properties.mobilephone -HomePhone $Properties.homephone -OtherTelephone $Properties.workphone -WarningAction 0

Set-User $Properties.Email -StreetAddress $Properties.address -City $ -StateOrProvince $Properties.state -PostalCode $ -Title $Properties.role -Notes $Properties.comments -WarningAction 0

Set-MailMailbox $Properties.Email -CustomAttribute1 $Properties.ID -WarningAction 0




Import-Csv "Recipients.csv" |

ForEach {

$Found = $False

$Recipient = $Null

Write-Host "Looking for: $($_.Email)" -ForegroundColor Cyan

$Recipient = Get-Recipient $_.Email -ErrorAction SilentlyContinue

If ($Recipient -eq $Null) {

Write-Host "`tRecipient not found, creating" -ForegroundColor Yellow

#Create new Contact

$FullName = $_.Preferred + " " + $_.LastName

New-MailContact -Name $FullName -DisplayName $FullName -ExternalEmailAddress $_.Email -FirstName $_.FirstName -LastName $_.LastName

$Recipient = Get-Recipient $_.Email


Else {

#Update Recipient

Write-Host `t"Found: $($Recipient.Name)" -ForegroundColor Green


UpdateRecipient $Recipient $_

Posted in Exchange, O365, Script, Technical | Tagged , , , | Leave a comment

Moderated Messages issues with Exchange Online (EXO) hybrid configurations

I’ve been using Exchange 2016 in a hybrid configuration for a few years now and have a few moderated groups I manage. An issue I ran into with this is that when messages come in for moderation I did not have Accept\Reject buttons coming up in Outlook or in OWA. After some research, I found that some changes were needed both in Exchange 2016 and EXO configs, all of which need to be done with PowerShell.

The issue stems from the approval messages coming in from SystemMailbox{} But the Hybrid configuration wizard only sets up a remote domain entry for, using the New-RemoteDomain cmdlet. This cmdlet registers and SMTP namespace so policies and mail-flow can be controlled for it specifically, via the Set-RemoteDomain cmdlet

You can check your current remote domain list by running: Get-RemoteDomain. This should return a Default\* entry and one for each SMTP domain you are using with EXO. By default these hybrid entries are named “Hybrid Domain – “, you should at least have one called “Hybrid Domain –” with a DomainName of

Note: The YourTenant value is your tenant’s short name setup in Office 365, mine is for example.

To fix the issue, so you can Accept\Reject moderated messages you need to do the following on-premises

  1. Create a new remote domain entry for, using the cmdlet below
New-RemoteDomain -Name "Hybrid Domain -" -DomainName
  1. Update the setting for these new RemoteDomain entry to make it a trusted domain
Set-RemoteDomain -Name "Hybrid Domain -" -IsInternal $true -TargetDeliveryDomain $true -AllowedOOFType InternalLegacy -MeetingForwardNotificationEnabled $true -TrustedMailOutboundEnabled $true -TrustedMailInboundEnabled $true -UseSimpleDisplayName $true -TNEFEnabled $true
  1. Add to Send Connector to Exchange online.
    Below my Send Connector is called “Send to EXO”, this name will vary. Run Get-SendConnector if needed to get the name.
Set-SendConnector "Send to EXO" -AddressSpaces: @{"}

Then you need to do the following to update Exchange Online:

  1. Connect to EXO, run the below cmdlet from any PowerShell prompt
$UserCredential= Get-Credential ; $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri -Credential $UserCredential -Authentication Basic -AllowRedirection ; Import-PSSession $Session
  1. Create a new RemoteDomain entry for
  2. Repeat the step below for each of your domains, replacing “” with the DNS domain name for each hybrid SMTP domain.
    Note: You should have a RemoteDomain entry for each of your SMTP domains, where messages need to be routed back to on-premises.
New-RemoteDomain -Name "Hybrid Domain" -DomainName



  1. Set this new remote domain as trusted, internal, and several other settings using the following cmlet
    Repeat this for the other remote domains to, or replace “” in the -Name parameter with just “*” to update them all at once.
Set-RemoteDomain -Name "Hybrid Domain" -IsInternal $true -TargetDeliveryDomain $true -AllowedOOFType InternalLegacy -MeetingForwardNotificationEnabled $true -TrustedMailOutboundEnabled $true -TrustedMailInboundEnabled $true -UseSimpleDisplayName $true -TNEFEnabled $true

Once the cmdlets are run above, moderated message approval should start working.

In my research, I found this blog post, that covers the same info I did above:

Posted in Exchange, Microsoft, O365, Technical | Tagged , | Leave a comment

Exchange Server 2016 “requires .NET Framework 4.5 or later” error

Recently I was trying to address and issue with a corrupted arbitration mailbox. The quick fix for this issue is to delete these mailboxes and the system accounts used by them and run “ /PrepareAD” to recreate them.

When I tried to run: setup /PrepareAD /IAcceptExchangeServerLicenseTerms I was getting this error:

Exchange Server 2016 requires .NET Framework 4.5 or later. Download it from

I carried out several steps to make sure .NET was installed and working correctly. The system had 4.5.2, 4.6, 4.6.1, & 4.6.2 on it. But Exchange was running 2016 CU3 on Windows 2012 R2, which only supports 4.6.1.

FIX: For the.NET 4.5 error was to upgrade Exchange 2016 to CU4. After upgrading the servers, the SETUP command now runs fine.

.NET 4.6.1 was not supported on Exchange 2013/2016 until CU13/2, article on the lack of support initially. The June 2016 updates officially added support for 4.6.1. The September 2016 updates, CU14/3, did add support for 4.6.2, but ONLY on Windows 2016. The December 2016 updates, CU 15/4, finally add support for 4.6.2 on Windows 2012 R2.


Posted in Microsoft, Technical | Tagged , | 2 Comments

Microsoft Teams is out

Tony Redmond just posted this great write-up on his views and details on Microsoft Teams, which is Office 365’s answer to Slack. I’ve been using Slack for over a year and a half and look forward to working with MS Teams and comparing the two soon.

Note: Microsoft Teams is in preview release only at this point, general availability is expected in early 2017.

For now, see Tony’s post here:

Microsoft’s site about it:

Posted in MS Teams, O365, Technical | Tagged | Leave a comment

URGENT: Hold off on deploying Exchange 2016 on Windows 2016

Exchange 2016 CU3 was the 1st version of Exchange supported on Windows 2016 when it was released on 9/20/2016 [Exchange 2013 & 2016 quarterly updates out]. This was a minor update, biggest changes are the support for Windows 2016 and indexing from passive copies.

Today officially Microsoft posted:

Microsoft recommends that customers delay deploying Exchange Server on Windows Server 2016 until this update is made available.


The issue surfaces as an issue with IIS, that causes W3WP.exe to crash, which leads to “instability” on that Exchange server and the DAG. The issues does not seem to appear on stand-alone servers. You will see event IDs 1003, 4999, & 5011 in the application logs relating to IIS issues. Per Tony’s post, see below, this issue is not directly related to IIS, but IIS crashes as a result of it. This it only appears on DAG members, which use Windows Clusters, so it makes sense it is related to clustering.

Tony Redmond goes into more detail here: Exchange 2016 to Windows 2016: No Thanks – At Least, Not for the Moment (11/4/2016)

One of the first public post about the issues was on the TechNet forums on 10/19/2016: Exchange Server 2016 & Windows Server 2016 Datacenter IIS AppPool’s constantly crashing. ECP/Powershell inaccessible.

The release notes for Windows 2016 have also now been updated with:

If you attempt to run Microsoft Exchange 2016 CU3 on Windows Server 2016, you will experience errors in the IIS host process W3WP.exe. There is no workaround at this time. You should postpone deployment of Exchange 2016 CU3 on Windows Server 2016 until a supported fix is available.

I will update this post as more info comes in or when a fix is posted.


Posted in Exchange, Technical | Tagged , | 1 Comment

Don’t let your O365 tenant get hijacked! An example of this happening…

I recently helped a small non-profit recover from a disgruntled director hijacking their O365 tenant and domains, they were able to do this since they were a Global Admin for their tenant. This post is about what happened, from a O365\domain stand point only, and as a warning to other orgs out there.

When I was contacted by the interim director of the org I was asked to see what the ex-director had done. I quickly found out I was unable to login to their O365 tenant. I then tried a couple of other, test, accounts I had and none of them worked. My next step was to try to login to their GoDaddy account, but that also failed.

So I called Microsoft O365 support… And waited… And waited… When I got someone I described the issue to them, was transferred and repeated this to a few different people, before getting to the data protection team. As suspected, they wanted to verify who I was and the only method they would use was a domain validation (DNS record), since my account and any other account I had access to no longer existed. Because I didn’t have access to the GoDaddy account, where DNS was hosted, I had no way to validate I was the actual admin\owner of the domain. In addition, their policies require that they contact all Global Admins before doing anything else. Since the disgruntled ex-employee was the only Global Admin left I knew this wouldn’t help.

So I called GoDaddy. Since I didn’t have the password or call in PIN #, which were changed by the ex-employee, they initially said they couldn’t help. But I then got transferred to a manager and we went over the issue further. He said if we could get the domain record owner, the domain register contact, to send in a copy of their photo ID and a letter on the non-profit their letterhead he would reset the password. The problem with this is that the domain contact was a person who setup the domain years ago. The contact number for him was the number for the non-profit. So I sent him an email explaining the situation and asked him to contact me for more details, which I never heard back on.

So after spending a few hours with O365 and GoDaddy support I was dead in the water with this issue.

I called back the interim director and told her that currently I was stuck and asked about getting the passwords from the ex-employee. She let me know that they had already had a lawyer write up a letter to the ex-employee and it was to be delivered that day by certified carrier. She had until noon the next day to turn over several items she took and any passwords. If she did not the org was going to file charges against her for theft and other offences. So we decided to wait it out to see if she would turn over the passwords. The only other alternatives we had was to call back O365 & GoDaddy support and beg them to help us further and hope that the person that registered the domain years ago would help also.

Luckily, the ex-employee turned over the passwords to GoDaddy & her O365 account the next day.

With the new passwords, I logged into GoDaddy and did the following:

  1. Change the password on the account
  2. Change the contact info to my name, as the technical lead for the org
  3. Setup delegated access to my primary GoDaddy account
  4. Changed the contact info on the domain to me as primary & technical contact and the interim director for the administrative contact.
  5. Setup two-factor authentication to my cell phone
  6. Setup a new PIN
  7. I then shared the new login info and PIN with the interim director


On O365, I setup an account for me, recovered the other accounts this person has deleted and granted the interim director Global Admin rights also. The last I did just in case I got hit by a bus.


  1. Make sure your domain(s) contact info is current and multiple trusted parties are listed on it
  2. Set the password on your domain account to something complex and save it in a safe place
    • A safe place could be a piece of paper in a safe that only a few people have access to or better a password saving tool, like LastPass, that only a VERY few people have access to.
  3. Setup alerts, if possible, so key people are notified when the password is changed on the domain hosting account and when settings are changed on the DNS and\or hosting account
  4. Have at least two accounts with Global Admin rights that admin have access to and another with a password that is stored in a safe place, that management can access if needed

With access recovered I then recovered the deleted accounts & mailboxes, which just took a few click at by going to Users\Deleted Users and click the users and clicking Restore. By default, O365 will keep deleted users and mailbox for 30 days.

With the deleted mailboxes recovered I then delegated access to the ex-director’s mailbox to the interim director and ask her to look it over. She got back to me and told me there were very few emails in it. So I assumed the ex-director deleted email. But since this org was using the free O365 for Non-profit E1 we had no access to the eDiscovery tools to search or put mailboxes on retention old. So with authorization, I upgraded the ex-director’s account to an E3 license, which includes eDiscovery support.

After searching her mailbox, I found many hard deleted items. I placed the mailbox on retention hold, exported the search results to the Discovery mailbox and exported her entire mailbox, including deleted items to a PST file. The PST file provided a better format since it kept folder structure, even deleted ones in tack. With the Discovery mailbox all results are dumped into a single folder.

I plan on making a post that goes into more details on the eDiscovery processes I used above soon.

Posted in O365, Technical | Tagged , | Leave a comment

Exchange 2013 & 2016 quarterly updates out

11/7/16 Added a “Known & Possible Issues” list
11/4/16 Urgent Update: Do NOT deploy Exchange 2016 CU3 (or earlier) on Windows 2016 at this time. See URGENT: Hold off on deploying Exchange 2016 on Windows 2016 for more details.
Today, 9/20/2016 Microsoft release CU14 for Exchange 2013 and CU3 for 2016.

EHLO Blog post: Released: September 2016 Quarterly Exchange Updates

Exchange 2013 | Download CU14 | KB3177670

Exchange 2016 | Download CU3 | KB3152589

Exchange 2016 Key Updates

  • Windows Server 2016 Support
    • Also includes .Net 4.6.2 support, on Windows 2016 Only currently
    • .Net 4.6.2 support will be required by March 2017, which will be supported on Windows 2008 R2 and higher by then
  • Local indexing for search or “Read from Passive” support – Starting with CU3 the local DB, passive or active, will be used to index the content in the DB for search. Before CU3 servers hosting passive copies had to query the active copy of the DB to create the local search index. This change may result in up to a 40% reduction in bandwidth. This may also speed up failovers since the passive copy no longer need to query the active copy and make local updates before being made active. Lagged copies will still need to communicate coordinate with the active copy.
  • A data loss scenario was addressed with Public Folder migration (KB3161916)
  • AD scheme is updated with CU3
  • Pre-requisite install behavior changed so server is not placed in off-line monitoring state at the start of the install, now this is only done once the pre-requisite checks are done and the install of binaries is started

There are no key updates in Exchange 2013 CU14.

PS: Exchange 2007 End of Life (EOL) is now only seven months away, 4/11/2017, after this date 2007 will not longer be covered under extended support. Read more here. If you are still on 2007 you need to be migrating off of it NOW!

Known & Possible Issues

Posted in Exchange, Microsoft, Technical | Tagged , | 1 Comment

Calendar Sharing across Devices w/ Exchange or EXO/O365

Starting in Exchange 2010 you have been able to share your calendar, if enabled at the organization level, with anyone on the Internet anonymously. This can still be done today with Exchange 2016 and Office 365 in the ICS (iCAL), which can be used by most email\calendar clients, and HTML formats.

Personally, I use this support so I can view my wife and two teenager’s calendars and they can view my personal and work calendar. From a professional standpoint, this would be great for a manager to view his team member’s calendars or an admin assist to be able to view their manager’s calendar, from their mobile devices. The ActiveSync protocol, used by most mobile devices, does not support delegated mailbox or calendar access. So access team member or other people’s calendars from mobile devices has to be setup manually.

For this to work your Exchange or EXO instance must be setup for Internet calendar publishing. This can be done via PowerShell or EAC. See the steps in this TechNet article for EMS and ECP directions, ECP steps below. This support can also be enabled between Exchange\O365 orgs and to certain domains if you want to limit the support to partners, for example. In my case I have an Anonymous sharing rule so anyone, that I give the URL to can access our calendars.

To Enable individual calendar sharing, in ECP for Exchange 2013+ or EXO, at the org level

  1. Open ECP and goto the organization menu
  2. Under “Individual Sharing” click the + (plus) button
  3. Give the sharing policy a name, like Anonymous calendar sharing
  4. Under “Define sharing rules for this policy” click the + (plus) to create a new Sharing Rule
    1. Choose Sharing with a specific domain
    2. Enter “Anonymous” for the domain name, to allow anyone to be able to access the calendars shared by user
      Note: This rule only allows users to share their calendars but does not share them for them automatically, this is still an action the users have to take.

      1. Check Share your calendar folder
      2. Then choose the level of calendar info to share, for anonymous I recommend free/busy with time only or free/busy with time, subject, and location.
        Note: If you choose All attachment and details in the meeting body will also be shared
  5. Click Save

Now that calendar sharing is enabled, users will need to Opt-In if they want someone to be able to access their calendar anonymous from the Internet.

Enabling sharing for a user and get their ICS URL for the calendar

These steps are for Exchange 2016; this can also be done in Exchange 2010 & 2013 but the steps vary.

These steps should be carried out by the end user.

  1. Open the OWA Calendar options
    1. In OWA, goto the Setting “gear” and choose Options
    2. In Outlook 2013+, goto your calendar and right click on it and choose Publish This Calendar
  2. Once in OWA, expand Calendar and choose Publish calendar
  3. Under “Select permissions” choose Limited details or Availability only then click Save
  4. Note: Users can only share details up to the level set at the org policy level earlier
  5. After clicking Save two URLs will be displayed
    1. The HTML one will use OWA to display a read-only version of that person’s calendar
    2. ICS is the iCAL format that can be shared with others so they can view the calendar in their email clients
  6. Email\share the ICS URL with those you want to be able to view your calendar in their email client

Adding a shared calendar to an iOS device

Give the long URL, you will want to email the ICS URL to those that you want to share it with. In my environment I setup custom short URL for ours, like, and one for my wife and each of our teenagers.

  1. Copy the ICS URL on the iOS device that are setting up access to another calendar on
  2. Goto Settings\Mail, Contacts, Calendars
  3. Click Add Account
  4. Choose Other
  5. Choose Add Subscribed Calendar
  6. Paste the ICS URL and click Next
  7. Enter a friendly name for the calendar when prompted, the other fields can be left blank
  8. This calendar should now show up in the Calendars app

Adding a shared calendar to in Outlook

These steps are only needed for calendars outside of the Exchange org your mailbox is in, also not required if there is an org sharing relationship setup between your org and theirs.

  1. Goto Outlook Calendar
  2. Right click on Other Calendars and choose Add Calendar\From Internet…
  3. Paste in the ICS URL

Adding a shared calendar to in Google Calendar

  1. Goto
  2. Next to “Other calendars” click the down arrow and choose Add by URL
  3. Paste in the ICS URL, which should have been sent to the Gmail user via email

Using the steps above, you have enabled anonymous calendar sharing and given users the ability to share their calendar with anyone on the Internet. Furthermore, I covered how to add a calendar shared from Exchange\OWA to iOS, Outlook, and Gmail.

Posted in Exchange, Microsoft, O365, Technical | Tagged | Leave a comment

Exchange 2016 CU2 and 2013 CU13 are out

Microsoft released the latest update to Exchange 2013 and 2016 on 6/21/2016. No major changes, but the DAG auto rebalancing and finally .NET 4.6.1 support are much desired changes. The new DAG feature is important to larger environments and the .NET support is important since 4.6.1 is an automatically suggested update and will cause issues on Exchange, unless you update to CU2\CU13 before install it.

EHLO Blog Post on this:

Tony Redmon’s post:

Key Changes

Other Changes

  • 2016: Get-ExchangeServer cmdlet updated to include rule definitions. Not a big deal, since 2016 servers will either be a Mailbox of Edge server
  • 2016: Self-signed certificates will now use SHA-2

2016 CU2 Key Fixes

2013 CU13 Key Fixes

Posted in Exchange, Microsoft, Technical | Tagged , | Leave a comment

20 Years on Exchange and 30 years on email

I don’t recall the exact date, but when I worked at Digital Equipment Corporation (DEC), 1994-1996, I got a beta version of Exchange 4.0 in late 1995. Having worked with MS:Mail, VMS Mail (ALL-IN-1), and several other email systems I really wanted to see the new mail server from Microsoft.

So using my DEC AXP150, running a true 64-bit processor in 1995!, test box running Windows NT 3.51 64-bit. I installed my 1st version of Exchange 4.0 beta (64-bit also) and started down the path of being a Microsoft Exchange consultant and expert.

Initially, this was just a test system but our team of ~30 quickly moved it for our primary communications. We were on the Microsoft PC Apps support team and really didn’t like the character only messaging systems we had been using. Within a few months, it expanded to the PC hardware support and other teams and when I left DEC, in early 1996, we had over 200 mailboxes on two different Alpha based Exchange 4.0 beta servers. When I was leaving DEC I chatted with our corporate IT staff about their planned roll-out of Exchange, which had Tony Redmond (fellow Exchange MVP) on the team. It wasn’t until around early 2000s that Tony and I reconnected and realized we had talked about Exchange about 10 years earlier when we were both at DEC.

At home, where I ran a 12 node BBS and mini ISP, I continued to run NetScape Mail until Exchange 5.0 came out with OWA support in 1997.

My 1st email system I ever setup was actually about 30 years ago, in 1986 when I was sysop on multiple  WWIV BBSs that were integrated into the FidoNet. Back in those days you could still send an email around the county or world, but it would take many days to get to some locations. We were limited to 300 baud, or about 300 characters per sec, and long distance phone calls were expense, but the rates dropped in the middle of the night. So Fido net would store and queue up message during the day, then at a schedule time when long distance was cheaper it would call a BBS in another city. This process would repeat until your message got from your BBS to the user on the target BBS. It was common for a message to take days to get across the county initially, but hubs were setup and Delphia & Compuserve start providing quicker routing in the early 90s.

Today, we commonly see single emails that were many times the size of our entire mailboxes in Exchange 4.0. We started out with 10MB mailbox I believe on Exchange 4.0 at DEC, now I host mailbox for friends and family on Exchange 2016 out of my house that have a 10GB limit by default 🙂

Posted in Exchange, Technical | Tagged | 2 Comments