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).

add-smtpaddresses-logfile

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.

About the Author

Paul Cunningham

Paul is a former Microsoft MVP for Office Apps and Services. He works as a consultant, writer, and trainer specializing in Office 365 and Exchange Server. Paul no longer writes for Practical365.com.

Comments

  1. boe

    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”
    }

    1. Avatar photo
      Tony Redmond

      For shared mailboxes, I would use the mailbox alias for the aaddress.

  2. Gary

    Absolutely brilliant, slightly tweaked to my needs. This has saved me so much time, thank you so much.

  3. Mike

    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.

    1. Mike

      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!

  4. Allan Hall

    Paul, can this be run against a list of mailboxes, perhaps a list in a csv file?

  5. Rick

    Perfect script. Does what it should with minimal instruction. Thank you for this. I can’t say enough about it.

  6. A tech human

    Leaving a thank you. I used your script earlier and it worked with no issues.

  7. John

    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?

  8. Shivam Dogra

    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.

  9. Fred

    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.

  10. Nathan Harris

    Hi Paul,

    Fantastic script as usual, really helpful.

    Many thanks,
    Nathan

  11. Saj

    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

    1. saj

      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??

  12. Anthony

    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.

    1. Anthony

      Updating Set-Mailbox with Set-DistributionGroup works if the distribution group already has more than 1 email address in EmailAddresses data.

      1. Andrew

        I found that I also had to change the instance of get-mailbox to get-distributiongroup

  13. Avi

    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.

  14. Avi

    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?

    1. Avatar photo
      Paul Cunningham

      As the instructions say, the script must be run from a PowerShell session that is connected to Exchange Online.

  15. Hugh

    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

  16. Nick

    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!

    1. Avatar photo
      Paul Cunningham

      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.

  17. Michel

    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

  18. Júlio Vaz Queiroz

    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’})

  19. Jordan Adams

    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!

    1. Avatar photo
      Paul Cunningham

      Sure. You could look at changing the mailbox related cmdlets in the script to distribution group cmdlets instead.

  20. Asanka Indunil

    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)

    1. Avatar photo
      Paul Cunningham

      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.

  21. mikel

    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.

  22. Anthony

    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

    1. Avatar photo
      Paul Cunningham

      Just change the line

      $Mailboxes = @(Get-Mailbox -ResultSize Unlimited)

      ….so that Get-Mailbox returns whatever mailboxes you’re interested in modifying.

  23. FHR

    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

    1. FHR

      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.

      1. Paul

        Did it create duplicates? I have a few that already have the SMTP address.

  24. FHR

    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.

    1. Matthew

      Any update on this? did the script still work or bomb if the address already existed?

  25. Frank

    I cannot see this script?

    1. Frank

      never mind, found it 😀

  26. Duncan Wraight

    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?

    1. Avatar photo
      Paul Cunningham

      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.

  27. Guy Thompson

    even tried this…

    {
    $NewAddress = “SMTP:” + $Mailbox.FirstName + “@$Domain”
    }
    else
    {
    $NewAddress = “smtp:” + $Mailbox.Alias + “@$Domain”
    }

    to no avail 🙁

  28. Guy Thompson

    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….

    1. Avatar photo
      Paul Cunningham

      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”

      1. Guy Thompson

        Rock n Roll!!!

        I knew i was being stupid…

      2. Thomas

        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

  29. Michael

    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

    1. Avatar photo
      Paul Cunningham

      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*.

  30. ProgentCT

    Brilliant! Thanks!

  31. Wes Jones

    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.

    1. Avatar photo
      Paul Cunningham

      Exchange Online is part of Office 365. In the article I explain how to connect to Exchange Online to run the script.

      1. Wes Jones

        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

  32. Matthew

    Hello,

    Can your script be adjusted to enable specific OU’s to be set to run the scripts against?

  33. Joshua Johnson

    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

  34. Ryan Fogle

    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.

Leave a Reply