Reading Tim McMichael’s blog post here reminded me of a script I’ve been meaning to tidy up and share. This script is used for bulk adding SMTP addresses to Office 365 mailboxes for a new SMTP domain.
This scenario applies to cloud-only Exchange Online scenarios. If you have an on-premises Exchange organization, or are in a Hybrid configuration, or otherwise are using using directory synchronization then this script should not be used, and instead you should use email address policies to make the changes.
The reason I’m sharing my own script instead of just pointing you at Tim’s (which seems fine) or any of the other code samples out there for this task (of which there are many) is that I wanted a script that had a few extra controls, and also one that wrote a log file so I could see what changes are made to the mailboxes it touches.
Here’s how it works. First of all, the script must be run from a PowerShell session that has already been connected to Exchange Online. Use my Connect-EXOnline function for this if you have not already got your own script or function for it.
Next, if you try to add SMTP addresses for a domain name that is not already configured as an accepted domain for your Office 365 tenant, the script will throw an error.
PS C:\Scripts> .\Add-SMTPAddresses.ps1 -Domain office365bootcamp.com The operation couldn't be performed because object 'office365bootcamp.com' couldn't be found on 'HKXPR04A001DC06.APCPR04A001.prod.outlook.com'. + CategoryInfo : NotSpecified: (:) [Get-AcceptedDomain], ManagementObjectNotFoundException + FullyQualifiedErrorId : [Server=SIXPR04MB048,RequestId=f8357ccc-bb99-46e4-b9d9-dda2207d7b28,TimeStamp=14/05/2015 1:10:13 PM] [FailureCategory=Cmdlet-ManagementObjectNotFoundException] BE34E051,Microsoft.Exchange.Management.Sys temConfigurationTasks.GetAcceptedDomain + PSComputerName : pod51055psh.outlook.com
There are three parameters used with the script:
- Domain – the domain name you wish to add (note: new email addresses are added as alias@domain)
- MakePrimary – specifies that the new address should be added as the primary email address. Otherwise the new address is added as a secondary email address by default.
- Commit – specifies that you want to commit the changes. Without this switch the script will not make any changes, but will still log the changes that would have been made to the log file so you can evaluate the impact of the script.
Here’s a few examples.
.\Add-SMTPAddresses.ps1 -Domain office365bootcamp.com
This will perform a test pass for adding the new alias@office365bootcamp.com as a secondary email address to all mailboxes. Use the log file to evaluate the outcome before you re-run with the -Commit switch.
.\Add-SMTPAddresses.ps1 -Domain office365bootcamp.com -MakePrimary
This will perform a test pass for adding the new alias@office365bootcamp.com as a primary email address to all mailboxes. Use the log file to evaluate the outcome before you re-run with the -Commit switch.
.\Add-SMTPAddresses.ps1 -Domain office365bootcamp.com -MakePrimary -Commit
This will add the new alias@office365bootcamp.com as a primary email address to all mailboxes.
The log file records the existing email addresses for the mailboxes, the email address that is being added, and then the full set of email addresses being committed to the mailbox (or not, if the -Commit switch wasn’t specified).
Important Note: You should always test PowerShell scripts before you run them against your production systems. If you do not have a test Office 365 tenant available and want to test this script against a single mailbox you can simply modify this Get-Mailbox command in the script.
#Get the list of mailboxes in the Office 365 tenant $Mailboxes = @(Get-Mailbox -ResultSize Unlimited)
For example, this will make the script only run against the “Vik.Kirby” mailbox.
#Get the list of mailboxes in the Office 365 tenant $Mailboxes = @(Get-Mailbox Vik.Kirby)
Add-SMTPAddresses.ps1 can be downloaded from the TechNet Script Gallery or Github. If you have any feedback or issues please leave a comment below or raise an issue on Github.
Thanks – I made a slight change as we wanted email addresses like mary.smith@acme.com – however I didn’t quite get it right as it didn’t add a second address for shared mailboxes as they don’t have a first and last name – any suggestions?
$NewAddress = $null
#If -MakePrimary is used the new address is made the primary SMTP address.
#Otherwise it is only added as a secondary email address.
if ($MakePrimary)
{
$NewAddress = “SMTP:” + (Get-User $Mailbox).FirstName + “.” + (Get-User $Mailbox).LastName + “@$Domain”
}
else
{
$NewAddress = “smtp:” + (Get-User $Mailbox).FirstName + “.” + (Get-User $Mailbox).LastName + “@$Domain”
}
For shared mailboxes, I would use the mailbox alias for the aaddress.
Possibly this might be a better script to base things on given that things have changed a lot since the article was written: https://office365itpros.com/2022/10/18/update-user-email-upns/
Absolutely brilliant, slightly tweaked to my needs. This has saved me so much time, thank you so much.
I am getting when trying to run this against a single test account.
Get-AcceptedDomain : The term ‘Get-AcceptedDomain’ is not recognized as the name of a cmdlet
This is an M365 system only, no on-prem AD or directory sync.
Never Mind.
It had to do with authenticating to Exchange Online using Powershell 5.1. I installed Powershell 7 and it ran flawlessly.
Thanks Paul!
Paul, can this be run against a list of mailboxes, perhaps a list in a csv file?
Perfect script. Does what it should with minimal instruction. Thank you for this. I can’t say enough about it.
Leaving a thank you. I used your script earlier and it worked with no issues.
Is there a way to just have this target O365 mailboxes and distribution groups, and not try and change those that are synced/tied to AD objects?
I modified the script on the below link to be using Distribution Groups. However if you have AD Sync then it will use that instead.
https://github.com/dbeato/scripts/blob/master/Office%20365/ADD-SMTPAddress-DG.ps1
I am able to see the changes in log file before the running the script with -commit switch. However, when I am running the script with -commit switch, I am getting a warning for all the DLs stating – “WARNING: No changes made due to -Commit switch not being specified.” Please assist.
Thanks a lot, really helpful. I you want to update it for distribution lists and unified groups that would be great. I ran into a few issues multiple matching aliases and aliases with utf8 characters, so a little more error checking would probably be helpful to people with larger tenants than mine.
Hi Paul,
Fantastic script as usual, really helpful.
Many thanks,
Nathan
PS C:\saj> .\Add-SMTPAddressesDB.ps1 -Domain name.com -Commit
******* Processing: me1
The operation on mailbox “me1” failed because it’s out of the current user’s write scope. The action ‘Set-Mailbox’, ‘EmailAddresses’, can’t be performed on the object ‘me’
because the object is being synchronized from your on-premises organization. This action should be performed on the object in your on-premises organization.
+ CategoryInfo : InvalidOperation: (me:ADObjectId) [Set-Mailbox], InvalidOperationException
Good Morning Paul, I wonder if you could assist, I need to use set-aduser to add a proxy email address then make that an alias and add another as primary, please can you help??
Hi Paul,
Great script for mailboxes! It worked a treat!
I tried updating the script to create a distribution list version, but it crashes. It looks like when reading the EmailAddresses data it isn’t an array of Microsoft.Exchange.Data.ProxyAddressCollection elements and the command $addresses += $NewAddress is just creating a longer string with the two email addresses appended together?
The updated script is basically replacing Set-Mailbox with Set-DistributionGroup.
Updating Set-Mailbox with Set-DistributionGroup works if the distribution group already has more than 1 email address in EmailAddresses data.
I found that I also had to change the instance of get-mailbox to get-distributiongroup
sorry,
this is the error i am getting:
PS C:\Users\AviB\Downloads> .\Add-SMTPAddresses.ps1 -Domain airobotics.co.il
Get-AcceptedDomain : The term ‘Get-AcceptedDomain’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check
the spelling of the name, or if a path was included, verify that the path is correct and try again.
At C:\Users\AviB\Downloads\Add-SMTPAddresses.ps1:88 char:11
+ $chkdom = Get-AcceptedDomain $domain
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Get-AcceptedDomain:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
WARNING: You must add the new domain name to your Office 365 tenant first.
Hi
i tried the script, from a domain controller in order to add a secondary smtp address to all users.
i run the script and i get this eroor:
PS C:\Users\AviB\Downloads> .\Add-SMTPAddresses.ps1 -Domain airobotics.co.il
Get-Mailbox : The term ‘Get-Mailbox’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of
the name, or if a path was included, verify that the path is correct and try again.
At C:\Users\AviB\Downloads\Add-SMTPAddresses.ps1:97 char:16
+ $Mailboxes = @(Get-Mailbox -ResultSize Unlimited)
+ ~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Get-Mailbox:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
WARNING: You must add the new domain name to your Office 365 tenant first.
what am i doing wrong?
As the instructions say, the script must be run from a PowerShell session that is connected to Exchange Online.
If a mailbox has only one SMTP, it will fail as it adds as one line.
For example, a mailbox SMTP:abc@abc.com, and adding abc@def.com. If the mailbox only has one SMTP and that is it, it combines as:
SMTP:abc@abc.comsmtp:abc@def.com
This issue has been fixed on the GitHub version of the script
https://github.com/cunninghamp/Add-SMTPAddresses.ps1
Hi Paul,
Thanks for creating this, it’ll help to save a lot of time.
Is there a way to modify this so that it only updates for one particular domain. Is that what Julio the changes Julio mentioned above. For instance, you have Domain A, B, C and you only want to update addresses for Domain A? If you could point me where to change that and how that would be amazing and save me even more time. Thanks!
You can narrow the scope of the script by modifying the Get-Mailbox command so that it only returns the mailboxes you want to modify.
Hello Paul,
Deeply Thank’s for sharing your knowledge about O365!
I’m configuring a platform O365 Education for my school in Belgium.
At this time, i’m able to bulk create users accounts, passwords, manage licence,… (script + csv).
With help of your article, i’m too able to bulk add multiple aliases for all my users (connect to Exchange – script – – CSV).
All runs fine.
My users have primary addresses like firstname.lastname@mydomain.be
But, for compatibility with an other platform, i need allow my users to connect to O365 using one of their aliases (like E1900452@mydomain.be) but it looks impossible in Office 365. Is there not a policy in Office365 to authorize it?
If i change their primary address with E1900452@mydomain.be, il will cause a problem when they will create an email: the field ‘from’ will be E1900452@mydomain.be. (It looks not very ‘professionnal’ )
Any idea to help me? ;o)
Michel
Hey Paul,
Thanks for sharing this content, it’s very usefull!!
Please consider my tiny change once that we host mail services on a single exchange cluster.
We needed to impose theses changes targeting a specific domain
We edited the part;
#Get the list of mailboxes in the Office 365 tenant
$Mailboxes = @(Get-Mailbox -ResultSize unlimited | where {$_.PrimarySMTPaddress -like ‘*@myclientdomain’})
Hi Paul, this is great and my team will be able to use this script for our customers.
Is there a way to change this script to perform the same actions but on distrogroups?
In my customers case we only need to add an smtp alias NOT switch the primary SMTP. We also need to add these smtp aliases to the distrogroups though.
Thanks for the great script and any advice on the group part!
Sure. You could look at changing the mailbox related cmdlets in the script to distribution group cmdlets instead.
We are migrating to Office 365 and we need to add a secondary email address to mailboxes with onmicrosoft.com address. All our employees has staff ID as alias. Most of the employees follow firstname.lastname@company.com but some not. So we are looking for a script that will prompt alias, then retrieve emailaddress@company.com then add secondary SMTP address as emailaddress@company.mail.onmicrosoft.com.
Thanks in advance
is it possible to exclude some users fro this script , such as admin, administrator, or list of users from CSV file not to change any email address aliases to domain name ( e.g. contoso.com ) as primary will be same as @contoso.onmicrosoft.com for excluded users accotuns)
At the end of the blog post I demonstrate which part of the code you can adjust to suit your needs. You just need to work out what Get-Mailbox command will get you the results you’re needing.
Another request for clarification re creation of duplicates please. I would like to add a secondary SMTP address for an existing domain to those accounts that don’t already have one (many already do) and don’t want to break things by running the script without checking first.
Hi Paul,
New to PowerShell.
I have had a look at your script, and it does what I am after (I think).
I would however like to be able to specify a single set of users and not all users to add the new domain too.
For example:
I have a list of user who start with 16xxxx@xxxx.xxx.xx
I also have another lot of users who start with 17xxxx@xxxx.xxxx.xx
etc
I would like to (maybe via a prompt) have all 16* changed their default email domain and default domain to the new one 365.xxxxx.xxx.xxx.xx
I would also like to be able to do this for staff and not just students.
I can get a list of staff out of O365 and would like to pass their current email address testa@xxx to the new testa@staff etc.
Any help is greatly appreciated.
Thanks heaps Paul
Just change the line
$Mailboxes = @(Get-Mailbox -ResultSize Unlimited)
….so that Get-Mailbox returns whatever mailboxes you’re interested in modifying.
Also for folks who don’t have on-prem AD, the -MakePrimary switch changes the primary SMTP address but it doesn’t change the username. So if your users are used to logging into everything with their e-mail address, you will still need to run
Set-MsolUserPrincipalName -UserPrincipalName old address -NewUserPrincipalName new address
Okay, so I ended up adding a few things to the script to deal with my non-AD situation.
Ended up adding two new variables:
$OldUPN = $Mailbox.Alias + “@mydomain”
$NewUPN = $Mailbox.Alias + “@$Domain”
And then this lines to the $Commit switch:
#Changes the UPN so that it matches the new e-mail address
Set-MsolUserPrincipalName -UserPrincipalName $OldUPN -NewUserPrincipalName
$NewUPN
Worked like a charm.
Did it create duplicates? I have a few that already have the SMTP address.
Does anyone know what would happen if the new SMTP address already exists for some users? I did a couple of users manually in the O365 dash just to test some application behaviors.
Once I do it for everyone, will the entire process fail when trying to commit an e-mail address that already exists? The logfile just adds the address in there again, so there are duplicate addresses in the list. I have a sneaking suspicion that when it comes time to commit, the server won’t have anything to do with that! 🙂
If it fails on that particular one, I don’t care. I just don’t want the entire process to bomb because of it.
Any update on this? did the script still work or bomb if the address already existed?
I cannot see this script?
never mind, found it 😀
Hi Paul. This script would be ideal for a project I’m working on at the moment… but we’re an on-premises setup with AD password sync. When I try to run the script, it errors out as I’m sure you’d expect – “can’t be performed because the object is being sychronized from your on-premises organization”.
Is there any way round this or do I manually need to update all of my users in AD?
When dirsync is implemented the on-premises AD is the source of authority, so the changes need to be made on-prem. The script will work, but an email address policy in Exchange could be simpler.
even tried this…
{
$NewAddress = “SMTP:” + $Mailbox.FirstName + “@$Domain”
}
else
{
$NewAddress = “smtp:” + $Mailbox.Alias + “@$Domain”
}
to no avail 🙁
Hey I’m trying to use just the ‘FirstName’ for the primary SMTP
I’ve tried :-
$NewAddress = “SMTP:” + $_.FirstName + “@$Domain”
}
else
{
$NewAddress = “smtp:” + $Mailbox.Alias + “@$Domain”
else
{
$NewAddress = “smtp:” + $Mailbox.Alias + “@$Domain”
And I keep getting this error
Error: “The address
‘SMTP:@wearefuterra.com’ is invalid: Specified argument was out of the range of valid values.
I’m doing something stupid I know i am….
In the script the $mailbox variable represents what you would get if you ran Get-Mailbox (line 111) of the script.
In the output of Get-Mailbox there is no FirstName attribute. So your modification is trying to create an email address of *nothing* @domain.com, which is not valid.
There is a FirstName attribute returned by Get-User. So in your case, this should work:
$NewAddress = “SMTP:” + (Get-User $Mailbox).FirstName + “@$Domain”
Rock n Roll!!!
I knew i was being stupid…
Hi,
I always get this Answer. Whats wrong:
Cannot process argument transformation on parameter ‘Identity’. Cannot convert the “Thomas Barbara” value of type
“Deserialized.Microsoft.Exchange.Data.Directory.Management.Mailbox” to type
“Microsoft.Exchange.Configuration.Tasks.UserIdParameter”.
+ CategoryInfo : InvalidData: (:) [Get-User], ParameterBindin…mationException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError,Get-User
+ PSComputerName : outlook.office365.com
I receive the following error when trying to change the primary address in Office365 using your script. I already ran the script and it added all the alias’s under the new domain name then went back to run it again for the MakePrimary method
Method invocation failed because [System.Object[]] doesn’t contain a method named ‘Replace’.
At C:usersmparkerDocumentsPowerShellSMTP.ps1:149 char:40
+ $addresses = $addresses.Replace <<<< ("SMTP","smtp")
+ CategoryInfo : InvalidOperation: (Replace:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
The script isn’t designed to work that way. If you’ve already added the address as a secondary email address, the -MakePrimary switch won’t change it to a primary. It’s designed to add the new address as the primary address *at the time the address is being added*.
Brilliant! Thanks!
Hi Paul –
Nice treatment here with the variable params and the logfile… I have some issues, tho.. We are using Office365 as opposed to Exchange Online. Either they have some different commands or MS has changed things since your article. When run, your script gives me an error that Get-AcceptedDomain is not recognized.
I can get a list of verified domains with MSOnlineExtendedGet-MsolDomain
Is this a different command set for O365 or am I not connected correctly?
If it is different, it seems it is newer and easier to confuse what each help page is actually supporting. If there is a separate set of commands for O365 do you know if there is a reference for them (as opposed to Exhange Online)?
Thanks regardless, the script was informative / educational.
Exchange Online is part of Office 365. In the article I explain how to connect to Exchange Online to run the script.
A bit terse, but made me go back and look at the page where I got this idea.. https://technet.microsoft.com/en-us/library/dn568015.aspx – they detail different access methods for Exchange online and Office 365.
I mistakenly took that to mean O365 in its entirety, but apparently even there Exchange Online is it’s own entity.
So you are right, I never connected to it at all – assuming the MsolService connection was sufficient.
But really – thanks for replying at all; old thread and I’m sure you have better things to do.
WJ
Hello,
Can your script be adjusted to enable specific OU’s to be set to run the scripts against?
Is this an on-premises Exchange organization?
I found what seems like a bug in this script. In the commit logic you use $mailbox.alias to reference the mailbox being changes. This works fine as long as their aren’t two mailboxes with the same alias prefix. I just processed this change for an organization which had two mailboxes for many of their users and both had the same mailbox alias with different domains. This caused the $Mailbox.alias reference to fail with the following error. Changing the reference to $Mailbox.UserPrincipalName resolved the issue. Since UserPrincipalName is unique it seems like that how it should have been written initially. Please let me know your thoughts.
The operation couldn’t be performed because ‘XXXXXXX’ matches multiple entries.
+ CategoryInfo : NotSpecified: (:) [Set-Mailbox], ManagementObjectAmbiguousException
+ FullyQualifiedErrorId : [Server=BN1PR03MB156,RequestId=287617cd-22a8-4d2f-9826-968acf6dd09f,TimeStamp=7/27/2015
7:35:15 PM] [FailureCategory=Cmdlet-ManagementObjectAmbiguousException] 4A3BD071,Microsoft.Exchange.Management.Rec
ipientTasks.SetMailbox
+ PSComputerName : pod51030psh.outlook.com
Changing to UPN is a good idea. Thanks for sharing.
I would add a statement around this can only be used if you are using cloud identities with Office 365. If your user accounts are listed as “Sync’d with Active Directory” this script and any other attempt to make changes to the user accounts in Office 365 will fail with something along the lines of “not allowed with in this write scope”
Companies that use dirsync to create and maintain accounts in Office 365 will need to make these types of changes to the on premises active directory user account. Then allow time for them to sync to Office 365. The specific AD attribute to alter is the proxyaddresses in order to make those changes in that type of environment.