Skip to content

Commit 75a3128

Browse files
committed
Combined GalSync scripts into one module.
1 parent 89bf8a4 commit 75a3128

File tree

3 files changed

+277
-244
lines changed

3 files changed

+277
-244
lines changed

Exchange/GalSync.psm1

+277
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
function Sync-Gal {
2+
<#
3+
.DESCRIPTION
4+
Does a one-way sync between two Exchange Online Gal's.
5+
6+
.PARAMETER PrimaryTenantCreds
7+
Credentials to login to the primary tenant.
8+
9+
.PARAMETER SecondaryTenantCreds
10+
Credentials to login to the secondary tenant.
11+
12+
.PARAMETER AsJob
13+
Enables the updates to be run as PS Jobs.
14+
15+
.PARAMETER Jobs
16+
The number of PS Jobs to create to run the updates. Limited to 3 because of an O365 default max concurrent connection.
17+
18+
.PARAMETER ContactLimit
19+
Number of contacts to sync. Default is Unlimited. Useful for doing quick tests.
20+
21+
.EXAMPLE
22+
./Sync-Gal.ps1 -PrimaryTenantCreds (Get-Credential) -SecondaryTenantCreds (Get-Credential) -AsJob -Jobs 3
23+
24+
.EXAMPLE
25+
./Sync-Gal.ps1
26+
27+
.NOTES
28+
Created by Nick Rodriguez
29+
Syncs the Gal of Exchange Online across two Office 365 tenants
30+
Note that this will break the creation of external user objects until MS addresses a known issue:
31+
https://products.office.com/en-us/business/office-365-roadmap?filters=&featureid=72273
32+
#>
33+
[CmdletBinding(
34+
SupportsShouldProcess = $true,
35+
DefaultParameterSetName = 'Synchronous'
36+
)]
37+
Param(
38+
[Parameter(Mandatory = $true)]
39+
[PSCredential]
40+
$PrimaryTenantCreds,
41+
42+
[Parameter(Mandatory = $true)]
43+
[PSCredential]
44+
$SecondaryTenantCreds,
45+
46+
[Parameter(
47+
Mandatory = $false,
48+
ParameterSetName = 'Asynchronous'
49+
)]
50+
[Switch]
51+
$AsJob,
52+
53+
[Parameter(
54+
Mandatory = $false,
55+
ParameterSetName = 'Asynchronous'
56+
)]
57+
[ValidateRange(1, 3)]
58+
[Int]
59+
$Jobs = 2,
60+
61+
[Parameter(Mandatory = $false)]
62+
[ValidateRange(0, [Int]::MaxValue)]
63+
[Int]
64+
$ContactLimit = 0
65+
)
66+
67+
begin {
68+
# Log everything
69+
$LogDirectory = (New-Item -ItemType Directory "C:\powershell-scripts\Exchange Online\Logs" -Force).FullName
70+
$Date = (Get-Date).ToString('yyyyMMdd-HHmm')
71+
Start-Transcript -Path "$LogDirectory\$($MyInvocation.MyCommand.Name)-$Date.log"
72+
}
73+
74+
process {
75+
# Create Exchange Online PowerShell session for primary tenant
76+
$PrimaryTenantEOSession = New-EOSession -Credential $PrimaryTenantCreds
77+
78+
# Enter session on primary tenant
79+
Import-PSSession $PrimaryTenantEOSession
80+
81+
# Get all Gal recipients using the primary filter
82+
$GalFilter = (Get-GlobalAddressList).RecipientFilter
83+
$ResultSizeLimit = if ($ContactLimit -eq 0) { 'Unlimited' } else { $ContactLimit }
84+
$Gal = Get-Recipient -ResultSize $ResultSizeLimit -Filter $GalFilter
85+
86+
# Export Gal to Csv file
87+
$Gal | Export-Csv -Path "$LogDirectory\Gal.csv" -NoTypeInformation -Force
88+
89+
# Remove session on primary tenant
90+
Remove-PSSession -Session $PrimaryTenantEOSession
91+
92+
# Create/Update contact for each Gal entry
93+
# If Jobs param specified, break up the list into smaller lists that can be started as jobs
94+
if ($AsJob) {
95+
$ContactLists = @{}
96+
$Count = 0
97+
98+
# Separate the contacts into smaller lists
99+
$Gal | ForEach-Object {
100+
$ContactLists[$Count % $Jobs] += @($_)
101+
$Count++
102+
}
103+
104+
# Create a job for each sublist of contacts
105+
foreach ($ContactList in $ContactLists.Values) {
106+
Start-Job -ArgumentList $SecondaryTenantCreds, $ContactList -ScriptBlock {
107+
# Create Exchange Online PS session
108+
$SecondaryTenantEOSession = New-EOSession -Credential $args[0]
109+
110+
# Enter session on secondary tenant
111+
Import-PSSession $SecondaryTenantEOSession
112+
113+
Update-GalContact -Gal $args[1]
114+
115+
# Remove session on secondary tenant
116+
Remove-PSSession -Session $SecondaryTenantEOSession
117+
} -InitializationScript { Import-Module 'C:\powershell-scripts\Exchange Online\GalSync.psm1' }
118+
}
119+
120+
# Wait for all jobs to finish then receive and remove the jobs
121+
Get-Job | Wait-Job | Receive-Job
122+
Get-Job | Remove-Job
123+
} else {
124+
# Create Exchange Online PS session
125+
$SecondaryTenantEOSession = New-EOSession -Credential $SecondaryTenantCreds
126+
127+
# Enter session on secondary tenant
128+
Import-PSSession $SecondaryTenantEOSession
129+
130+
Update-GalContact -Gal $Gal
131+
132+
# Remove session on secondary tenant
133+
Remove-PSSession -Session $SecondaryTenantEOSession
134+
}
135+
}
136+
137+
end {
138+
# Remove any lingering PSSessions and stop the logging
139+
Get-PSSession | Remove-PSSession
140+
Stop-Transcript
141+
}
142+
}
143+
144+
function New-EOSession {
145+
<#
146+
.DESCRIPTION
147+
Create a new Exchange Online PowerShell session
148+
149+
.PARAMETER Credential
150+
The credentials to use for the session.
151+
152+
.EXAMPLE
153+
New-EOSession -Credential $creds
154+
155+
.NOTES
156+
Created by Nick Rodriguez
157+
#>
158+
[CmdletBinding(SupportsShouldProcess = $true)]
159+
Param(
160+
[Parameter(Mandatory = $true)]
161+
[PSCredential]
162+
$Credential
163+
)
164+
165+
New-PSSession -ConfigurationName Microsoft.Exchange -Authentication Basic -AllowRedirection `
166+
-ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $Credential
167+
}
168+
169+
function Update-GalContact {
170+
<#
171+
.DESCRIPTION
172+
Takes an array of recipients and updates the Gal.
173+
174+
.PARAMETER Gal
175+
The recipient or list of reipients to update.
176+
177+
.EXAMPLE
178+
Update-GalContact -Gal $ExternalGal
179+
180+
.NOTES
181+
Created by Nick Rodriguez
182+
#>
183+
[CmdletBinding(SupportsShouldProcess = $true)]
184+
Param(
185+
[PSObject[]]$Gal
186+
)
187+
188+
foreach ($Recipient in $Gal) {
189+
# Create a new contact if one doesn't exist
190+
if (Get-MailContact -Identity $Recipient.Name) {
191+
Write-Host "Contact $($Recipient.Name) already exists."
192+
} else {
193+
try {
194+
New-MailContact `
195+
-ExternalEmailAddress $Recipient.PrimarySmtpAddress `
196+
-Name $Recipient.Name `
197+
-FirstName $Recipient.FirstName `
198+
-LastName $Recipient.LastName `
199+
-DisplayName $Recipient.DisplayName `
200+
-Alias $Recipient.Alias
201+
Write-Host "New contact created for $($Recipient.Name)"
202+
} catch {
203+
Write-Host "Error creating new contact for $($Recipient.Name): $_"
204+
}
205+
}
206+
207+
try {
208+
# Update mail contact properties
209+
Set-MailContact `
210+
-Identity $Recipient.Name `
211+
-ExternalEmailAddress $Recipient.PrimarySmtpAddress `
212+
-Name $Recipient.Name `
213+
-DisplayName $Recipient.DisplayName `
214+
-Alias $Recipient.Alias `
215+
-CustomAttribute1 $Recipient.CustomAttribute1 `
216+
-CustomAttribute2 $Recipient.CustomAttribute2 `
217+
-CustomAttribute3 $Recipient.CustomAttribute3 `
218+
-CustomAttribute4 $Recipient.CustomAttribute4 `
219+
-CustomAttribute5 $Recipient.CustomAttribute5 `
220+
-CustomAttribute6 $Recipient.CustomAttribute6 `
221+
-CustomAttribute7 $Recipient.CustomAttribute7 `
222+
-CustomAttribute8 $Recipient.CustomAttribute8 `
223+
-CustomAttribute9 $Recipient.CustomAttribute9 `
224+
-CustomAttribute10 $Recipient.CustomAttribute10 `
225+
-CustomAttribute11 $Recipient.CustomAttribute11 `
226+
-CustomAttribute12 $Recipient.CustomAttribute12 `
227+
-CustomAttribute13 $Recipient.CustomAttribute13 `
228+
-CustomAttribute14 $Recipient.CustomAttribute14 `
229+
-CustomAttribute15 $Recipient.CustomAttribute15 `
230+
-ExtensionCustomAttribute1 $Recipient.ExtensionCustomAttribute1 `
231+
-ExtensionCustomAttribute2 $Recipient.ExtensionCustomAttribute2 `
232+
-ExtensionCustomAttribute3 $Recipient.ExtensionCustomAttribute3 `
233+
-ExtensionCustomAttribute4 $Recipient.ExtensionCustomAttribute4 `
234+
-ExtensionCustomAttribute5 $Recipient.ExtensionCustomAttribute5 `
235+
| Out-Null
236+
237+
# Update Windows Email Address only if it's populated
238+
if ($Recipient.WindowsLiveID -ne '') {
239+
Set-MailContact -Identity $Recipient.Name -WindowsEmailAddress $Recipient.WindowsLiveID | Out-Null
240+
}
241+
} catch {
242+
Write-Host "Error updating mail contact info for $($Recipient.Name): $_"
243+
}
244+
245+
try {
246+
# Update contact properties
247+
Set-Contact `
248+
-Identity $Recipient.Name `
249+
-FirstName $Recipient.FirstName `
250+
-LastName $Recipient.LastName `
251+
-Department $Recipient.Department `
252+
-Company $Recipient.Company `
253+
-Phone $Recipient.Phone `
254+
-HomePhone $Recipient.HomePhone `
255+
-OtherHomePhone $Recipient.OtherHomePhone `
256+
-MobilePhone $Recipient.MobilePhone `
257+
-OtherTelephone $Recipient.OtherTelephone `
258+
-Pager $Recipient.Pager `
259+
-Fax $Recipient.Fax `
260+
-OtherFax $Recipient.OtherFax `
261+
-Office $Recipient.Office `
262+
-CountryOrRegion $Recipient.UsageLocation `
263+
-StreetAddress $Recipient.StreetAddress `
264+
-City $Recipient.City `
265+
-StateOrProvince $Recipient.StateOrProvince `
266+
-PostalCode $Recipient.PostalCode `
267+
-PostOfficeBox $Recipient.PostOfficeBox `
268+
-Title $Recipient.Title `
269+
-Manager $Recipient.Manager `
270+
-AssistantName $Recipient.AssistantName `
271+
-Notes $Recipient.Notes `
272+
| Out-Null
273+
} catch {
274+
Write-Host "Error updating contact info for $($Recipient.Name): $_"
275+
}
276+
}
277+
}

0 commit comments

Comments
 (0)