Forum Discussion
Share address book from one to other tenants in O365
- Jan 29, 2019With that said, feel free to vote on the uservoice for sharing GAL between tenants! Quite a big one!
https://office365.uservoice.com/forums/273493-office-365-admin/suggestions/10087845-gal-federation-between-different-o365-tenants
Regards / Adam
Hello Techno1795,
I was searching for a solution a long time.
Here the scenario:
- company holding with 3 child companies
- all 4 companies have their own tenant
- holding is AD domain synced to O365, only cloud mailboxes
- 1 child is plain O365
- 2 childs are hybrid Exchange organizations
- organizations with local ADs are bidirectional trusted
Goal is, that all organizations see the contacts in the other ones.
I finally used Azure Automation Powershell scripts for that.
- I created multiple scripts that run once a day
- read the mailboxes from one Exchange organization or O365 tenant
- add a suffix to the display name like "(Organization One)" (for users belonging to multiple orgs.)
- use one attribute to identify the organization
- create a contact in each other organization, update if already exists
- run a second script or add a section to the first script that parses all contacts in the tenants and checks, if the mailbox still exists in the organization. If not, delete the contact. That cleans up your contacts. But be careful an check, if the connection to the organization is OK. Otherwise during connection problems, you'll lose all contacts till next run. 😉
Not nice, but it works.
- Sunil50Jul 17, 2020Copper Contributor
Bernd_SchneiderI have a similar issue that I'm dealing with and would appreciate if you could share a detailed insight on the script used and steps taken.
- Bernd_SchneiderJul 20, 2020Copper Contributor
Sunil50 Hi, that's the script.
It could be optimized, but it works:# Publish O365 Users as Contacts in another tenant
# BSSE GmbH, Bernd Schneider
#
# Note:
# The text “ (Source-Tenant)” is appended to every contact to mark the contacts
# of the source company.
# Use at own risk!
# Connect Source-Tenant
Write-Output "Connect Source-Tenant"
$credObject = Get-AutomationPSCredential -Name "Source-Tenant-Admin"
Connect-MsolService -Credential $credObject
# Get Source-Tenant Users Excluding Guest Accounts
Write-Output "Read Source-Tenant MSOLUser"
$mySourceUsers=Get-MSOLUser -all|Where-Object {($_.UserPrincipalName -LIKE "*@Source-Tenant.*") -AND (-NOT ($_.UserPrincipalName -LIKE ("*#EXT#*"))) }
Get-PSSession | Remove-PSSession
# Create AzureRunAsConnection
$connectionName = "AzureRunAsConnection"
try
{
# Get the connection "AzureRunAsConnection "
$servicePrincipalConnection=Get-AutomationConnection -Name $connectionName
Write-Output "Logging in to Azure..."
Add-AzureRmAccount `
-ServicePrincipal `
-TenantId $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
}
catch
{
if (!$servicePrincipalConnection)
{
$ErrorMessage = "Connection $connectionName not found."
throw $ErrorMessage
} else{
Write-Error -Message $_.Exception
throw $_.Exception
}
}
# Connect Target-Tenant
Write-Output "Connect Target-Tenant"
$credObject = Get-AutomationPSCredential -Name "Target-Tenant-Admin"
Connect-MsolService -Credential $credObject
# Function: Update-ExchangeOnlineContact
function Update-ExchangeOnlineContact {
param (
$User
)
$NameAndOrg=$User.DisplayName+" (Source-Tenant)"
Set-Mailcontact $User.UserPrincipalName -Name $NameAndOrg -DisplayName $NameAndOrg -FirstName $User.FirstName -LastName $User.Lastname `
-CustomAttribute9 "BSSE Office 365 Synchronisation"
}
# Function: Update-ExchangeOnlineContactEmail
function Update-ExchangeOnlineContactEmail {
param (
$User
)
$NameAndOrg=$User.DisplayName+" (Source-Tenant)"
Get-MailContact -resultsize 99999|where-object {$User.DisplayName -eq $NameAndOrg}| ´
Set-Mailcontact -ExternalEmailAddress $User.UserPrincipalName -CustomAttribute9 "BSSE Office 365 Synchronisation"
}
# Function: New-ExchangeOnlineContact
function New-ExchangeOnlineContact {
param (
$User
)
$NameAndOrg=$User.DisplayName+" (Source-Tenant)"
New-Mailcontact -Name $NameAndOrg -DisplayName $NameAndOrg -FirstName $User.FirstName -LastName $User.LastName `
-ExternalEmailAddress $User.UserPrincipalName
Set-Mailcontact -Identity $User.UserPrincipalName -CustomAttribute9 "BSSE Office 365 Synchronisation"
}
# Function: Connect to Exchange Online
function Connect-ExchangeOnline {
param (
$Creds
)
Write-Output "Connecting to Exchange Online"
Get-PSSession | Remove-PSSession
$Session = New-PSSession –ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $Creds -Authentication Basic -AllowRedirection
$Commands = @("Get-Recipient","New-Mailcontact","Set-Mailcontact","Set-Mailbox","Get-Mailbox","Get-Contact","Get-MailContact", "Remove-Mailcontact", "Get-MailUser")
Import-PSSession -Session $Session -DisableNameChecking:$true -AllowClobber:$true -CommandName $Commands | Out-Null
}
# Connect to Exchange Online
Write-Output "Connect Target-Tenant Exchange"
Connect-ExchangeOnline -Creds $credObject
# Load Data in Buffer
$SourceDisplay=Get-MailContact -resultsize 99999|where-object {$_.DisplayName -like "*(Source-Tenant)"}
$SourceContacts=Get-MailContact -resultsize 99999|where-object {$_.PrimarySMTPAddress -like "*Source-Tenant.*"}
$SourceExternal=Get-MailUser -resultsize 99999|where-object {$_.PrimarySMTPAddress -like "*Source-Tenant.*" -and $_.RecipientTypeDetails -eq "GuestMailUser"}
# Check if new Account using Email address
ForEach($User in $MySourceUsers) {
$TempContact = $SourceContacts|where {$SourceContacts.PrimarySMTPAddress -eq ($User.UserPrincipalName)}
if ($TempContact) {
Write-Output ("Existing Contact: "+$User.DisplayName)
# Check for Update using time last changed
if ($User.Value.WhenModified.addday -gt (get-date).adddays(-2)) {
Write-Output ("Update Contact: "+$User.DisplayName)
Update-ExchangeOnlineContact -User $User
}
}
else {
$TempContact = $SourceExternal|where {$SourceExternal.PrimarySMTPAddress -like $User.UserPrincipalName}
# Check if external contact, if yes, ignore
if ($TempContact) {
Write-Output ("External Contact: "+$User.UserPrincipalName)
}
else {
$TempDisplay = $SourceDisplay|where {$SourceDisplay.DisplayName -eq ($User.DisplayName + " (Source-Tenant)")}
# Check, if same Displayname exists
if ($TempDisplay) {
# Change Email-Address
Write-Output ("New Email Address: "+$User.DisplayName+", "+$User.UserPrincipalName)
Update-ExchangeOnlineContactEmail -User $User
}
else {
# Create new Contact
Write-Output ("New Contact: "+$User.DisplayName)
New-ExchangeOnlineContact -User $User
}
}
}
}
$MySourceUsers|ft UserPrincipalName, DisplayName
# Check Account deletion
ForEach($Contact in $SourceContacts) {
$TempUser = $MySourceUsers|where {($MySourceUsers.UserPrincipalName) -like $Contact.PrimarySMTPAddress}
if ($TempUser) {
Write-Output ("Do not delete: "+$Contact.identity)
}
else {
Write-Warning ("Delete Contact: "+$Contact.identity)
Write-Output ($Contact.PrimarySMTPAddress)
$contact|Remove-MailContact -confirm:$false
}
}
# Close Session
Get-PSSession | Remove-PSSession
Write-Output "Script Completed!"
- Bernd_SchneiderJul 20, 2020Copper ContributorI'seen that pasting the script looses some of the hash characters.
Hope you see whit is comment. 🙂