Building On What’s Gone Before

Imitation is the sincerest form of flattery that mediocrity can pay to greatness,” said Oscar Wilde. Leaving any notion of mediocrity aside, I liked the idea Sean McAvinue explained in his article about creating organizational contacts in user mailboxes using the Microsoft Graph API. It’s a simple concept: all organizations have contacts, like HR, the Travel Department, and Security, which users should be able to contact in case of emergency or other reasons. They can always look up the GAL, but creating personal contacts in user mailboxes means that mobile devices can synchronize the information too. All in all, a nice idea, and even better, Sean’s PowerShell code works.

Leveraging PowerShell

The beauty of PowerShell is that it’s easy to take an idea developed by someone else and evolve and improve it. That’s where imitation comes into play, and the idea that a script can be taken and enhanced is exactly what we want to encourage at As I looked at the code, I thought of three improvements:

  • Identify mailboxes created in the last month and create personal contacts in these mailboxes.
  • Allow any mail-enabled recipient in Exchange Online to be used to create personal contacts.
  • Check if personal contacts is present in a mailbox before attempting to create it.

These are relatively simple changes. Here’s what I did to create the code.

Find Target Mailboxes

To find mailboxes created in the last month, run the Get-ExoMailbox cmdlet with a server-side filter against the WhenMailboxCreated property. Date comparison can be tricky with the mailbox cmdlets, but the following code worked for me:

$LastMonth = (Get-Date).AddDays(-30)
$Mailboxes = Get-ExoMailbox -Filter "WhenMailboxCreated -gt '$LastMonth'" -RecipientTypeDetails UserMailbox | Select ExternalDirectoryObjectId, DisplayName, UserPrincipalName

Find the Source for Personal Contacts

The idea is to allow any mail-enabled recipient to be a source for personal contacts. The Get-Recipient cmdlet can find any objects across all recipient types, but we need to identify which recipients should be used as a source. To do this, we mark the recipients to use by writing a value into a custom attribute. All mail-enabled objects support custom attributes, so we can use user mailboxes, shared mailboxes, Microsoft 365 Groups, distribution lists, mail users, and mail contacts. Assuming that the chosen recipients are updated with the value, we can find the set of source contacts with a command like:

$OrgContacts = Get-Recipient -Filter {CustomAttribute4 -eq "OrgContact"}

Updating Personal Contacts

The original script does the heavy lifting to create a JSON structure defining each personal contact to add to user mailboxes. My version does some parsing to handle each recipient type. The other change that’s implemented is to check if a personal contact exists in a mailbox before attempting to add it. The check uses a filter to check that a personal contact with the same email address exists. If one is not found, we go ahead and add it. Here’s the code for the check:

$Uri = "$Mailbox/contacts?`$filter=emailAddresses/any(a:a/address eq '$Email')"
$Results = Invoke-RestMethod -Headers $Headers -Uri $Uri -UseBasicParsing -Method "GET" -ContentType "application/json"

Figure 1 shows how the organizational contacts appear in a user mailbox. In this case, the origins for the contacts include a mail contact, distribution list, shared mailbox, Microsoft 365 group, and user mailbox.

Organizational contacts created in a user mailbox
Figure 1: Organizational contacts created in a user mailbox

The Next Step

Like any PowerShell script, I’m sure that this code (which you can download from GitHub) can be improved. For one thing, my scripts come with minimal error handling because I try to illustrate principles instead of writing off-the-shelf code. In any case, every organization has their own way of dealing with errors, logging issues, and so on. It’s best if you take the idea explained here and implement it in your own way.

Let your imagination run riot and see what you can do. All enhancements are welcome. No idea is a bad one. And when you’re done, be sure to share what you’ve built. We’ll all appreciate it.

About the Author

Tony Redmond

Tony Redmond has written thousands of articles about Microsoft technology since 1996. He is the lead author for the Office 365 for IT Pros eBook, the only book covering Office 365 that is updated monthly to keep pace with change in the cloud. Apart from contributing to, Tony also writes at to support the development of the eBook. He has been a Microsoft MVP since 2004.


  1. Simon

    Is there a way to update the Contact, if the Phone number changes? For example a User is already added as contact, however his Phone number changes.
    could you also specifically delete a contact if it does not have the the Attribute anymore?

  2. Markus

    Hi Tony,

    THANKS for this great script. This helped me alot!
    I have tried to add the address to my contact but failed. Do you have an idea or tip how to do that?


    1. Avatar photo
      Tony Redmond

      Do you have the necessary Graph permissions to run the command? Did you get an error message? Can you run the code step by step to find where the problem lies?

  3. Adriano

    Hi ,
    thx for sharing the code.

    How can I try the code for a specific user ?

    1. Avatar photo
      Tony Redmond

      replace the line:

      $Mailboxes = Get-ExoMailbox

      with $Mailboxes = the user principal names of the mailboxes to process…. Like [array]$mailboxes = “”, “”

      1. Adriano

        thank you, but when I try to execute the code again, something going wrong

        “Invoke-RestMethod : Error on remote server: (400) Request not valid.
        Error creating contact for Error on remote server: (405) Method not allowed..

        I’ve already done the graph authorization on API request, there’s something missing?

        1. Avatar photo
          Tony Redmond

          I don’t know how you have set things up, but I bet that the loop ForEach ($Mbx in $Mailboxes) is not working because the input array doesn’t contain all the information used to figure out mailbox details etc.
          If $Mbx contains a UPN, then it will probably be sufficient to add a line:

          $Mbx = Get-Mailbox -Identity $Mbx

          Just under the ForEach line. That command will populate the $MBX variable with the information needed later on.

          Have you stepped through the code to find out what is being passed in the Invoke-RestMethod call to see what’s failing? This is PowerShell, so it’s relatively straightforward to debug.

          1. Adriano

            It Works fine!

            It was really this, $Mbx = Get-Mailbox -Identity $Mbx

            Thank you so much!

    2. Ben

      Hello Adriano, could you tell me which line you adjusted in the code?

      1. Ben

        Because the checking if the Contact is existing fails, it just goes on to create it, even though a contact with existing mail adress exists.

  4. Sameer

    I’m highly interested in applying the solution. Do you have a Youtube video that explains the process?

    1. Avatar photo
      Tony Redmond

      Nope. The process is fully explained in the article. I don’t see how YouTube would add any value to the discussion.

Leave a Reply