Entra / Microsoft 365 · Exchange Online
Populate org contacts
A script to write organization contacts to user mailboxes.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
# Review required modules and connection steps before running.# Connect to Microsoft Graph or Exchange Online as needed for this script.
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
param([string] $AppId = "",[int] $LookbackDays = 30)$Modules = Get-ModuleIf ("ExchangeOnlineManagement" -notin $Modules.Name) {Write-Host "Please connect to Exchange Online Management before continuing...";break}# Find the set of organization contacts - marked as such by having OrgContact in CustomAttribute4 of their object properties. This attribute is chosen at random. You can use# whatever other way you want to find the set of contacts, including having a CSV file.[array]$OrgContacts = Get-ExoRecipient -Filter {CustomAttribute4 -eq "OrgContact"} -Properties CustomAttribute4, ExternalEmailAddress -RecipientTypeDetails MailContactIf (!($OrgContacts)) {Write-Host "No organization contacts found - exiting" ; break }Write-Host ("Found {0} organization contacts - continuing..." -f $OrgContacts.count)# Look for target mailboxes. In this example, we get the mailboxed created in the last month[datetime]$LastMonth = (Get-Date).AddDays(-$LookbackDays)[array]$Mailboxes = Get-ExoMailbox -Filter "WhenMailboxCreated -gt '$LastMonth'" -RecipientTypeDetails UserMailbox | Select ExternalDirectoryObjectId, DisplayName, UserPrincipalNameIf (!($Mailboxes)) { Write-Host "No mailboxes found to process - exiting" ; break }Write-Host ("Found {0} mailboxes to process - continuing..." -f $Mailboxes.count)$Now = Get-Date -format D# Get Graph access token - change these values for the app you use.$AppSecret = "~C2U~tDO74Tj-w2glcIWqa_BtCZ46KmVA."$Tenantid = "b762313f-14fc-43a2-9a7a-d2e27f4f3478"# Construct URI and body needed for authentication$uri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"$body = @{client_id = $AppIdscope = "https://graph.microsoft.com/.default"client_secret = $AppSecretgrant_type = "client_credentials"}$tokenRequest = Invoke-WebRequest -Method Post -Uri $uri -ContentType "application/x-www-form-urlencoded" -Body $body -UseBasicParsing# Unpack Access Token$token = ($tokenRequest.Content | ConvertFrom-Json).access_token$Headers = @{'Content-Type' = "application\json"'Authorization' = "Bearer $Token" }$OrgNotes = "Organization contact created " + $NowForEach ($Mbx in $Mailboxes) {# Populate email addresses for existing contacts$Uri = "https://graph.microsoft.com/v1.0/users/" + $Mbx.ExternalDirectoryObjectId + "/contacts"[array]$ContactsInMbx = Invoke-RestMethod -Headers $Headers -Uri $Uri -UseBasicParsing -Method "GET"# Build hash table of contacts that exist in the mailboxIf ($ContactsInMbx.Value.Count -gt 0) { $ExistingContacts = $ContactsInMbx.Value | Select-Object -ExpandProperty emailaddresses | Sort-Object -Unique }$CheckTable = @{}ForEach ($C in $ExistingContacts) { $CheckTable.Add($C.Address.toString(), $C.Name.toString()) }Write-Host "Processing mailbox" $Mbx.DisplayName$ApiUri = "https://graph.microsoft.com/v1.0/users/$mailbox/contacts"ForEach ($Contact in $OrgContacts) {Write-Host "Processing contact" $Contact.DisplayName$Assistant = $Null$Phone = $Contact.Phone$HomePage = $Null$Company = $Contact.Company$Department = $Contact.Department$DisplayName = $Contact.DisplayName$Title = $Contact.Title$First = $Contact.FirstName$Last = $Contact.LastName$Middle = $Null$Nickname = $Contact.Alias$Notes = $OrgNotesSwitch ($Contact.RecipientTypeDetails) { # Populate the contact details depending on the type of mail recipient object"MailContact" { # Mail contacts$Email = $Contact.ExternalEmailAddress.Split(":")[1]$Profession = "Contact"}"MailUniversalDistributionGroup" { # Distribution lists$Email = $Contact.PrimarySmtpAddress$Profession = "Distribution list"}"GroupMailbox" { # Microsoft 365 Groups$Email = $Contact.PrimarySmtpAddress$Profession = "Microsoft 365 Group"}"UserMailbox" { # User mailboxes$Email = $Contact.PrimarySmtpAddress$Profession = "User Mailbox"}"SharedMailbox" { # User mailboxes$Email = $Contact.PrimarySmtpAddress$Profession = "Shared Mailbox"}} # End Switch# Check if the contact is already there. If not, we go ahead and add the contactIf ($CheckTable[$Email]) {Write-Host ("Contact record for {0} is already present in the mailbox" -f $Email) }Else {Write-Host "Proceeding..."# Build the contact object$ContactObject = @"{"assistantName": "$($Assistant)","businessHomePage": "$($HomePage)","businessPhones": ["$($Phone)"],"companyName": "$($Company)","department": "$($Department)","displayName": "$($DisplayName)","emailAddresses": [{"address": "$($Email)","name": "$($Displayname)"}],"givenName": "$($First)","jobTitle": "$($Title)","middleName": "$($Middle)","nickName": "$($Nickname)","profession": "$($Profession)","personalNotes": "$($OrgNotes)","surname": "$($Last)","title": "$($Saluation)"}"@# And add the new contactTry {$NewContact = (Invoke-RestMethod -Headers @{Authorization = "Bearer $($Token)" } -ContentType 'application/json' -Body $ContactObject -Uri $Uri -Method Post)}catch {throw "Error creating contact $($contact.emailaddress) for $mailbox $($_.Exception.Message)"break}} #End Else} #End ForEach OrgContacts} #End ForEach Mailboxes
Parameters
ParameterDefaultNotes
-AppId""Application (client) ID for the app registration used to connect.-LookbackDays30Number of days of contact data to process when populating organization contacts.Attribution
Author
Office365itpros