PS: SH-SOFT GAL Sync - Exchange <-> Exchange

This is the most basic version of the script. It can be used to synchronise mail enabled users from a source organisation to a target organisation and backwards.
It has some included filters to limit the number of accounts to be synchronised by the script:

  • Multiple OUs for source mail enabled users in the source and in the target organisation
  • It only synchronises visible users (so users hidden from the address list are not processes)

The script is used to run it from one exchange organisation. So this organisation can do all the work and provide guidelines for what to sync. As a typical situation where to use the script I see a company acquisition for example.
The password required to access the target environment can be stored using Power Shell please use the helper script provided >> here.
At the moment an AD trust is required between the two exchange organisations and for the login process also the domain controllers in the target environment need to be accessible from the server / client running the script.


Content to be syncronised

The following attributes are synchronised:

  • Display Name
  • Name
  • Lastname
  • Firstname
  • Department
  • Title
  • Company
  • Office
  • Phone
  • Fax
  • MobilePhone
  • City
  • PostalCode
  • StreetAddress
  • StateOrProvince
  • CountryOrRegion
  • PrimarySMTPAddress
  • Legacy ExchangeDN (as X500 Address) (it is only written once and not kept up to date)
  • Alias (with prefix is required)

The script populates the AD extension attributes 14 and 15 with sha1 hashed GUIDs either of the source user or of the scripts GUID to allow multiple instances of the script to coexist.


SETUP

This in short describes how to configure the script for a first time use:

After Download you should verify the integrity of the script downloaded with the included hashes. After this the following variables inside the script have to be configured:

  • TargetExchangeServer
    FQDN server name to connect for the remote Exchange management shell.
  • TargetSearchScopes
    Comma separated list of OUs to search for mail enabled users in the target organisation.
  • TargetContactOU
    OU in which the contacts should be created (this one needs to be writable for the Sync account in the target organisation).
  • TargetTransport
    Can be used to switch between http and https as transport. MS Exchange default is http for the Power Shell directory.
  • TargetCredentialPasswordFileName
    Filename for the password file that should be created.
  • TargetCredentialUserName
    Username for the remote access.
  • TargetAliasPrefix
    A prefix that is placed in front of the alias to avoid duplicate aliases.
  • SourceExchangeServer
    FQDN server name to connect for the source Exchange management shell.
  • SourceSearchScopes
    Comma separated list of OUs to search for mail enabled users in the source organisation.
  • SourceContactOU
    OU in which the contacts should be created (this one needs to be writable for the Sync account in the source organisation).
  • SourceAliasPrefix
    A prefix that is placed in front of the alias to avoid duplicate aliases.
  • LogFilePath
    By default this points to the script execution directory. But it can be changed to a different location if this is required.
  • LogFileBaseName
    Basic file name of the log file. The current Date is prepended to that filename.
  • LogFileDateTime
    Date and time syntax inside the log file.
  • DebugLogging
    It is recommended to keep this active until a full check has verified the script operates in a normal way…
  • DebugToScreen
    This is only required if you need to see the debug output in the shell window. This should be disabled to increase overall script performance.
  • ThisScriptGUIDFileBaseName
    Filename to the script GUI hash. The hash itself is being created automatically if no guid file is found at startup. Then it will result in problems because all contacts loose sync. If the file or the content of the file is lost take a look to extensionAttribut14 of already created contacts and store that string to the file specified here.
  • UpdateDeleteContactsOnlyCreatedByThisScript
    A very important switch please always use it set to true! It ensures the script only updates and deletes contacts created by itself. If this switch is not set the script WILL DELTE contacts in the Contacts OU if they do not match to a source mail enabled user!

You need to set up the environment:

  1. Open firewall from the Computer / Server running the script to the source and target Exchange and directory services.
  2. Add an account in the target environment with appropriate permissions in the target environment.
  3. Add an account in the source environment with appropriate permissions (Also the logon as Batch Job right is required on the machine running the script as a scheduled task).
  4. Store the target accounts password as encrypted password file with the account the script is running as.

Version History

  • 1.0.0: Initial release
  • 1.0.1: Some typo fixes
    fixed problem with updating contacts when script guid check was enabled
  • 1.0.2: Add MIT licence
    fixed issue that no cantact gets deleted when source user list was completely empty

Download the script:

>> Version 1.0.2 (current)
(MD5: 77a332832710e83c21fa96995349d0e4)
(SHA1: 3c3e464ee23388b48f055e58cb007561fe953aae)


Script code:

	
##------------------------------------------------------------------------------------------------
##
##  SH-SOFT-GALSync-ExchExch.ps1
##
##   Version 1.0.2
##
##
##   Copyright (c) 2016 Martin Mueller - www.sh-soft.com
##   
##   Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
##   and associated documentation files (the "Software"), to deal in the Software without 
##   restriction, including without limitation the rights to use, copy, modify, merge, publish, 
##   distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
##   Software is furnished to do so, subject to the following conditions:
##   
##   The above copyright notice and this permission notice shall be included in all copies or 
##   substantial portions of the Software.
##   
##   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
##   BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
##   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
##   DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
##   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
##   (The MIT License (MIT))
##
##------------------------------------------------------------------------------------------------
<#
.SYNOPSIS
please visit https://www.sh-soft.com/en/scripting/ps/sh-galsync/ps-sh-soft-gal-sync-exchange-exchange.html for instructions about the script. Thanks.

This script version can synchronize users between two Exchange organisations when there is a direct contact (http / https) possible between the organisations. 

You can find a complete list of available versions here: 
https://www.sh-soft.com/en/scripting/ps/sh-galsync

Otherwise please contact me to see if there are possible inofficial versions available for your special purpose :-)


#>

##------------------------------------------------------------------------------------------------
## Configuration parameters for the script
##------------------------------------------------------------------------------------------------

#------------------------------------------------------------------------------------------------
## Target Exchange environment
#
#   this variables contain connection information to you partner Exchange server
#
#------------------------------------------------------------------------------------------------
# FQDN server name of the partners exchange environment
$TargetExchangeServer = "exchange.sh-soft.com" 

# Search scopes to look for mail enabled users
$TargetSearchScopes = @("OU=Purchasing,OU=ADUsers,DC=sh-soft,DC=com", `
						"OU=Operating,OU=ADUsers,DC=sh-soft,DC=com", `
						"OU=Department1,OU=Moonbase,DC=sh-soft,DC=com")

# You have to verfiy this one is not within the SearchScopes!
$TargetContactOU = "OU=SchnitzelContacts,DC=sh-soft,DC=com" 

# connect to Exchange shell over http / https protocol (default on Exchange is http)
$TargetTransport = "http" 

# Alias prefix that is attached infront of the original alias when they get created / updated in the source directory
$TargetAliasPrefix = "" 

# Username to connect to the target environment (use NetBiosDomainName\SAMaccountName)
$TargetCredentialUserName = "SH-SOFT\svc_galsyncexchange" 

# Password for the above account stored encrypted
# see https://www.sh-soft.com/en/scripting/ps/ps-store-credential.html
$TargetCredentialPasswordFileName = "svc_galsyncexchange.cred" 



#------------------------------------------------------------------------------------------------
## Source Exchange environment
#
#   this variables contain connection infromation of your Exchange server
#
#------------------------------------------------------------------------------------------------
# FQDN server name of the local exchange environment
$SourceExchangeServer = "exchange.schnitzelbroetchen.info"

# Search scopes to look for mail enabled users
$SourceSearchScopes = @("OU=Schnitzel,OU=Benutzer,DC=schnitzelbroetchen,DC=info", `
						"OU=Pommes,OU=Benutzer,DC=schnitzelbroetchen,DC=info", `
						"OU=Salat,OU=Benutzer,DC=schnitzelbroetchen,DC=info")

# You have to verfiy this one is not within the SearchScopes!
$SourceContactOU = "OU=SH-SOFT-CONTACTS,DC=schnitzelbroetchen,DC=info"

# Alias prefix that is attached infront of the original alias when they get created / updated in the target directory
$SourceAliasPrefix = "" 


#------------------------------------------------------------------------------------------------
# Logging
#------------------------------------------------------------------------------------------------
$LogFilePath = (Split-Path -Parent -Path $MyInvocation.MyCommand.Definition) #default points to the script directory
$LogFileBaseName = "_SH-SOFT-GALSync-ExchExch.log"
$LogFileDateTime = "yyyyMMdd.HHmmss"

# Enable or disable debug (detailed) logging
$DebugLogging = $true 

# Enable or disable debugging to screen -> Should be set to $false in production because it reduces the script performance.
$DebugToScreen = $false 

#Monitoring files (Not yet implemented in a perfect way... )
$CreateMonitoringFiles = $false
$OverallStatusFileName = "somefilenaemewithpath" #


#------------------------------------------------------------------------------------------------
# GUID file of the script
#------------------------------------------------------------------------------------------------
$ThisScriptGUIDFileBaseName = "ThisScript.guid"


#------------------------------------------------------------------------------------------------
# To process only contacts created by this script leave this option enabled ($true)
# 
#  This ensures no otherwise created contacts get updated or removed!
#  The script creates a GUID if none exists. The GUID is stored as sha1 hash in a file defined in the variable $ThisScriptGUIDFileBaseName
#   the default name is ThisScript.guid
#  Every contact created by the script gets ExtensionAttribute14 provisioned with this hashed GUID (regardless if this option is enabled or not) 
#------------------------------------------------------------------------------------------------
$UpdateDeleteContactsOnlyCreatedByThisScript = $true 





###================================================================================================###
###																								   ###
### MAIN SCRIPT PART - no change should be required  from here on...							   ###
###																								   ###
###================================================================================================###



##------------------------------------------------------------------------------------------------
## Internal variables
##------------------------------------------------------------------------------------------------
# Logfile DateTime prefix
$Date = Get-Date -Format $LogFileDateTime
#Hashing algorithm (sha1 as default)
$CProvider = New-Object System.Security.Cryptography.SHA1CryptoServiceProvider
#Overall status variable
$ScriptOverallStatus = "OK"
#GUID filename handling
$ThisScriptGUIDFileName = ""
$ThisScriptGUIDFileName = Join-Path -Path (Split-Path -Parent -Path $MyInvocation.MyCommand.Definition) -ChildPath ($ThisScriptGUIDFileBaseName) -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
$ThisScriptGUID = $null
#Password filename hdanling
$TargetCredentialPasswordFile = ""
$TargetCredentialPasswordFile = Join-Path -Path (Split-Path -Parent -Path $MyInvocation.MyCommand.Definition) -ChildPath ($TargetCredentialPasswordFileName) -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
$TargetCredentialPasswordFileName = $null



##------------------------------------------------------------------------------------------------
## Internal functions
##------------------------------------------------------------------------------------------------


#------------------------------------------------------------------------------------------------
# Log writer
function Write-Log ($Entry, $DebugEntry) {
	$DateTime = Get-Date -Format "yyyyMMdd;HHmmss;"
	$FileName = Join-Path -Path $LogFilePath -ChildPath ($Date + $LogFileBaseName)
	## USER INTERFACE
	if ($DebugToScreen) {
		if ($Entry) {
			Write-Host -ForegroundColor Cyan ($DateTime+$Entry)
		}
		if ($DebugEntry) {
			Write-Host -ForegroundColor DarkMagenta ($DateTime+"[DEBUG]") -nonewline
			Write-Host -ForegroundColor Cyan $DebugEntry
		}
	}
	if ($LogFilePath.Length -eq $null) {
		$FileName = ($Date + "-" + $LogFileBaseName)
	}
	if ($Entry) {
		Add-Content -Path $FileName -Value ($DateTime + $Entry)
	}
	if ($DebugLogging -and $DebugEntry) {
		Add-Content -Path $FileName -Value ($DateTime +" [DEBUG] "+ $DebugEntry)
	}
}


#------------------------------------------------------------------------------------------------
# WaitForContactCreation
function WaitForContactCreation ($ContactMail) {
	Write-Log -DebugEntry "Check if new contact is created... UserID: $ContactMail"
	$WAIT = $true
	$Counter = 0
	while ($WAIT) { 
		if (Get-MailContact -Identity "$ContactMail" -WarningAction SilentlyContinue -ErrorAction SilentlyContinue) {
			$WAIT = $false
		}
		else {
			$Counter++
			Start-Sleep -Seconds 1
		}
	}
	if ($Counter -eq 0) {
		Write-Log -DebugEntry "UserID: $ContactMail is present"
		return $true
	}
	else {
		Write-Log -DebugEntry "UserID: $ContactMail was present after $Counter seconds"
		return $true
	}

}


#------------------------------------------------------------------------------------------------
# Connect source Exchange server
function Connect-SourceExchangeShell {
	Write-Log -Entry ("[INFO] Connecting to source Exchange shell") -DebugEntry $SourceExchangeServer
	$Session = New-PSSession -ConfigurationName Microsoft.Exchange `
								-ConnectionUri http://$SourceExchangeServer/PowerShell/ `
								-Authentication Kerberos
	try {
		$null = Import-PSSession $Session -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -DisableNameChecking -OutVariable $null
		Write-Log -DebugEntry "[CONNECT-SOURCEEXCHANGESHELL] Exchange connection to $SourceExchangeServer established!"
	}
	catch {
		Write-Log -Entry "[ERROR] Connecting to target Exchange server"
		Update-StatusFiles -OverallStatusFile "ERROR"
		exit
	}
}


#------------------------------------------------------------------------------------------------
# Connect target Exchange server
function Connect-TargetExchangeShell {
	Write-Log -Entry ("[INFO] Connecting to target Exchange shell") -DebugEntry ("Server: $TargetExchangeServer; Transport: $TargetTransport")
	$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri ($TargetTransport+"://$TargetExchangeServer/PowerShell/") -Credential $TargetCredential -Authentication Kerberos
	Write-Log -DebugEntry "Session Created"
	try {
		$null = Import-PSSession $Session -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -DisableNameChecking -OutVariable $null
		Write-Log -DebugEntry "[CONNECT-TARGETEXCHANGESHELL]Exchange connection to $TargetExchangeServer using protocol: $TargetTransport established!"
	}
	catch {
		Write-Log -Entry "[ERROR] Connecting to target Exchange server"
		Update-StatusFiles -OverallStatusFile "ERROR"
		exit
	}
}


#------------------------------------------------------------------------------------------------
# Disconnect shell
function Disconnect-ExchangeShell ($Selector) {
	$ServerToDisconnect = $null
	if ($Selector -eq "source") {
		$ServerToDisconnect = $SourceExchangeServer 
	}
	elseif ($Selector -eq "target") {
		$ServerToDisconnect = $TargetExchangeServer
	}
	# Disconnect all sessions to the server
	Write-Log -DebugEntry ("[DISCONNECT-EXCHANGESHELL]"+$Selector +" -- "+$ServerToDisconnect)
	$null = Get-PSSession | Where-Object {$_.ComputerName -eq $ServerToDisconnect} | Remove-PSSession
}


#------------------------------------------------------------------------------------------------
# Create hashes for strings
function Create-SHAHash ([string]$Value) { 
	if ($Value.Length -gt 0) {
		$encoding = [system.Text.Encoding]::UTF8 
		$ByteValue = $encoding.GetBytes($value) 
		$HASH = ([System.BitConverter]::ToString($CProvider.ComputeHash($ByteValue))).Replace("-", "").ToLower()
		Write-Log -DebugEntry ("[CREATE-SHAHASH] Input: `""+$Value+"`"  -- Output: `""+$HASH+"`"")
		return ($HASH) 
	}
	else {
		return "[ERROR-SOURCEWASEMPTY]"
	}
}


#------------------------------------------------------------------------------------------------
# Verify hashes
function Verfiy-SHAHash ([string]$Value, [string]$Hash) {
	$InnerHash = Create-SHAHash -Value $Value
	Write-Log -DebugEntry ("[VERIFY-SHAHASH] Value: `""+$Value+"`" (`""+$InnerHash+"`") -- Hash: `""+$Hash+"`"")
	if ($InnerHash -eq $Hash) {
		Write-Log -DebugEntry ("[VERIFY-SHAHASH] - Match")
		return $true
	}
	else {
		Write-Log -DebugEntry ("[VERIFY-SHAHASH] - No Match")
		return $false
	}
}


#------------------------------------------------------------------------------------------------
# Update Status File
function Update-StatusFiles ($OverallStatusFile) {
	if ($CreateMonitoringFiles) {
		Write-Log -DebugEntry "[UPDATE-STATUSFILES]"
		if ($OverallStatusFile.Length -gt 0) {
			$ScriptOverallStatus = $OverallStatusFile
		}

		try {
			# Overall file
			
			Set-Content -Path $OverallStatusFileName -Value $ScriptOverallStatus
			Add-Content -Path $OverallStatusFileName -Value (Get-Date -Format "yyyyMMdd-HHmmss")
		}
		catch {
			Write-Log -Entry "[ERROR] failed updating status file!"
		}		 
	}
}



##------------------------------------------------------------------------------------------------
## early initialization
##------------------------------------------------------------------------------------------------


#------------------------------------------------------------------------------------------------
# GUID handling
if ($ThisScriptGUIDFileName.Length -eq 0) {
	$ThisScriptGUIDFileName = $ThisScriptGUIDFileBaseName
}
#Verify file exists then load 
if (Test-Path $ThisScriptGUIDFileName) {
	try {
		$ThisScriptGUID = Get-Content -Path $ThisScriptGUIDFileName
		Write-Log -DebugEntry "Reading GUID from file: $ThisScriptGUIDFileName - successfull"
	}
	catch {
		Write-Log -Entry "[ERROR] while reading GUID file" -DebugEntry "$ThisScriptGUIDFileName"
	}
}
#Otherwise create guid and save it to file
else {
	Write-Log -Entry "Creating new GUID for this script"
	$ThisScriptGUID = ([guid]::NewGuid()) | Select-Object -ExpandProperty GUID
	Write-Log -DebugEntry "Creating hash for new GUID: $ThisScriptGUID"
	$ThisScriptGUID = Create-SHAHash -Value $ThisScriptGUID
	Write-Log -DebugEntry "Hash for GUID is: $ThisScriptGUID"
	try {
		Set-Content -Path $ThisScriptGUIDFileName -Value $ThisScriptGUID -Encoding UTF8  
	}
	catch {
		Write-Log -Entry "[ERROR] while writing GUID to file" -DebugEntry $ThisScriptGUIDFileName
	}
}


#------------------------------------------------------------------------------------------------
# Log file initialize
Write-Log -DebugEntry ("[LOGGING] `""+$LogFilePath+"`"")
Write-Log -DebugEntry ("[LOGGING] `""+$LogFileBaseName+"`"")
Write-Log -DebugEntry ("[LOGGING] `""+$LogFileDateTime+"`"")
Write-Log -DebugEntry ("[STATUSFILE] `""+$OverallStatusFileName+"`"")
#Status monitoring
Update-StatusFiles
Write-log -Entry "Starting script execution"


#------------------------------------------------------------------------------------------------
# Build target credential
$TargetCredential = $null
if (Test-Path -Path $TargetCredentialPasswordFile) {
	try {
		$Password = Get-Content "$TargetCredentialPasswordFile" | ConvertTo-SecureString
		$TargetCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "$TargetCredentialUserName",$Password
		$Password = $null
	}
	catch {
		Write-Log -Entry "ERROR while reading target credential file" -DebugEntry $TargetCredentialPasswordFile
		$ScriptOverallStatus = "ERROR"
		$Password = $null
		exit
	}
}
else {
	Write-Log -Entry "ERROR target credential file not found please check whether file exists or not." -DebugEntry $TargetCredentialPasswordFile
	$ScriptOverallStatus = "ERROR"
	exit
}


#------------------------------------------------------------------------------------------------
# Test connection to source and target
Disconnect-ExchangeShell -Selector "target" #Just to ensure there is no open session at startup...
Disconnect-ExchangeShell -Selector "source" #Just to ensure there is no open session at startup...
Connect-TargetExchangeShell #Verify the target is accessible 
Disconnect-ExchangeShell -Selector "target"
Connect-SourceExchangeShell #Verify the source is accessible



##------------------------------------------------------------------------------------------------
## Get source elements
##------------------------------------------------------------------------------------------------
##Status monitoring
Update-StatusFiles


#------------------------------------------------------------------------------------------------
# Get source real users
#------------------------------------------------------------------------------------------------
$SourceElements = @()
foreach ($OU in $SourceSearchScopes) {
	$SourceElements += Get-Mailbox -OrganizationalUnit "$OU" -RecipientTypeDetail "UserMailbox" -ResultSize unlimited -Filter {HiddenFromAddressListsEnabled -eq $false} |`
							Select-Object -Property SHElementID, SHHashedGUID, SHStatus, SHSourceContactElementID, `
														DisplayName, Name, LastName, FirstName, `
														Department, Title, Company, Office, `
														Phone, Fax, MobilePhone, `
														City, PostalCode, StreetAddress, StateOrProvince, CountryOrRegion, `
														PrimarySmtpAddress, EmailAddresses, LegacyExchangeDN, Alias,  `
														CustomAttribute14, CustomAttribute15, DistinguishedName, Guid														
}


# Create Hashed GUID foreach recipient and collect additional attributes
$ElementID = 0
foreach ($SourceElement in $SourceElements) {
	$CurrentDN = $SourceElement.DistinguishedName
	Write-Log -DebugEntry ("[SOURCEUSERATTRIBUTE-COLLECTION] $CurrentDN")
	$User = Get-User -Identity "$CurrentDN" | `
				Select-Object -Property LastName, FirstName, `
										Department, Title, Company, Office, `
										Phone, Fax, MobilePhone, `
										City, PostalCode, StreetAddress, StateOrProvince, CountryOrRegion

	#Update new values to existing object
	$SourceElement.SHElementID = $ElementID	
	$SourceElement.SHHashedGUID = Create-SHAHash -Value $SourceElement.Guid.ToString()
	
	$SourceElement.LastName = $User.LastName
	$SourceElement.FirstName = $User.FirstName
	
	$SourceElement.Department = $User.Department
	$SourceElement.Title = $User.Title
	$SourceElement.Company = $User.Company
	$SourceElement.Office = $User.Office
	
	$SourceElement.Phone = $User.Phone
	$SourceElement.Fax = $User.Fax
	$SourceElement.MobilePhone = $User.MobilePhone
	
	$SourceElement.City = $User.City
	$SourceElement.PostalCode = $User.PostalCode
	$SourceElement.StreetAddress = $User.StreetAddress
	$SourceElement.StateOrProvince = $User.StateOrProvince
	$SourceElement.CountryOrRegion = $User.CountryOrRegion

	#Set Alias Prefix 
	$SourceElement.Alias = $SourceAliasPrefix+$SourceElement.Alias
	
	$ElementID++
}


#------------------------------------------------------------------------------------------------
# Get Contacts in SourceContacts OU (Partners Addresses in Source directory)
#------------------------------------------------------------------------------------------------
$TargetContactElements = @()
$TargetContactElements = Get-Recipient -OrganizationalUnit "$SourceContactOU" -RecipientTypeDetails "MailContact" -RecipientType "MailContact" -ResultSize unlimited | `
							Select-Object -Property SHElementID, SHHashedGUID, SHStatus, SHTargetElementID, `
														DisplayName, Name, LastName, FirstName, `
														Department, Title, Company, Office, `
														Phone, Fax, MobilePhone, `
														City, PostalCode, StreetAddress, StateOrProvince, CountryOrRegion, `
														PrimarySmtpAddress, EmailAddresses, LegacyExchangeDN, Alias, `
														CustomAttribute14, CustomAttribute15, DistinguishedName, Guid 

$ElementID = 0
if ($TargetContactElements.Count -gt 0) {
	foreach ($TargetContactElement in $TargetContactElements) {
	   $CurrentDN = $TargetContactElement.DistinguishedName
	   Write-Log -DebugEntry ("[TARGETCONTACTATTRIBUTE-COLLECTION] $CurrentDN")
	   $User = Get-Contact -Identity "$CurrentDN" | `
				Select-Object -Property Fax, MobilePhone, `
										StreetAddress

	   $TargetContactElement.SHElementID = $ElementID

	   $TargetContactElement.Fax = $User.Fax
	   $TargetContactElement.MobilePhone = $User.MobilePhone

	   $TargetContactElement.StreetAddress = $User.StreetAddress

	   $ElementID++
	}
}

# Disconnect source Exchange session
Disconnect-ExchangeShell -Selector "source"




##------------------------------------------------------------------------------------------------
## Get target elements
##------------------------------------------------------------------------------------------------
##Status monitoring
Update-StatusFiles
Connect-TargetExchangeShell


#------------------------------------------------------------------------------------------------
# Get Mailboxes from Target Domain
#------------------------------------------------------------------------------------------------
$TargetElements = @()
foreach ($OU in $TargetSearchScopes) {
	$TargetElements += Get-Mailbox -OrganizationalUnit "$OU" -RecipientTypeDetail "UserMailbox" -ResultSize unlimited -Filter {HiddenFromAddressListsEnabled -eq $false} | `
							Select-Object -Property SHElementID, SHHashedGUID, SHStatus, SHTargetContactElementID, `
														DisplayName, Name, LastName, FirstName, `
														Department, Title, Company, Office, `
														Phone, Fax, MobilePhone, `
														City, PostalCode, StreetAddress, StateOrProvince, CountryOrRegion, `
														PrimarySmtpAddress, EmailAddresses, LegacyExchangeDN, Alias,`
														CustomAttribute14, CustomAttribute15, DistinguishedName, Guid   
}
# Create Hased GUID foreach recipient
$ElementID = 0
foreach ($TargetElement in $TargetElements) {
	$CurrentDN = $TargetElement.DistinguishedName
	Write-Log -DebugEntry ("[TARGETUSERATTRIBUTE-COLLECTION] $CurrentDN")
	$User = Get-User -Identity "$CurrentDN" | `
				Select-Object -Property LastName, FirstName, `
										Department, Title, Company, Office, `
										Phone, Fax, MobilePhone, `
										City, PostalCode, StreetAddress, StateOrProvince, CountryOrRegion

	#Update new values to existing object
	$TargetElement.SHElementID = $ElementID	
	$TargetElement.SHHashedGUID = Create-SHAHash -Value $TargetElement.Guid.ToString()
	
	$TargetElement.LastName = $User.LastName
	$TargetElement.FirstName = $User.FirstName
	
	$TargetElement.Department = $User.Department
	$TargetElement.Title = $User.Title
	$TargetElement.Company = $User.Company
	$TargetElement.Office = $User.Office
	
	$TargetElement.Phone = $User.Phone
	$TargetElement.Fax = $User.Fax
	$TargetElement.MobilePhone = $User.MobilePhone
	
	$TargetElement.City = $User.City
	$TargetElement.PostalCode = $User.PostalCode
	$TargetElement.StreetAddress = $User.StreetAddress
	$TargetElement.StateOrProvince = $User.StateOrProvince
	$TargetElement.CountryOrRegion = $User.CountryOrRegion

	#Set Alias Prefix 
	$TargetElement.Alias = $TargetAliasPrefix+$TargetElement.Alias
	
	$ElementID++
}


#------------------------------------------------------------------------------------------------
# Get Contacts in TargetContacts OU (Your Addresses in target directory)
#------------------------------------------------------------------------------------------------
$SourceContactElements = @()
$SourceContactElements = Get-Recipient -OrganizationalUnit "$TargetContactOU" -RecipientTypeDetails "MailContact" -RecipientType "MailContact" -ResultSize unlimited | `
							Select-Object -Property SHElementID, SHHashedGUID, SHStatus, SHSourceElementID, `
														DisplayName, Name, LastName, FirstName, `
														Department, Title, Company, Office, `
														Phone, Fax, MobilePhone, `
														City, PostalCode, StreetAddress, StateOrProvince, CountryOrRegion, `
														PrimarySmtpAddress, EmailAddresses, LegacyExchangeDN, Alias, `
														CustomAttribute14, CustomAttribute15, DistinguishedName, Guid 

$ElementID = 0
if ($SourceContactElements.Count -gt 0) { # ==>
	foreach ($SourceContactElement in $SourceContactElements) {
	   $CurrentDN = $SourceContactElement.DistinguishedName
	   Write-Log -DebugEntry ("[SOURCECONTACTATTRIBUTE-COLLECTION] $CurrentDN")
	   $User = Get-Contact -Identity "$CurrentDN" | `
				Select-Object -Property Fax, MobilePhone, `
										StreetAddress

	   $SourceContactElement.SHElementID = $ElementID

	   $SourceContactElement.Fax = $User.Fax
	   $SourceContactElement.MobilePhone = $User.MobilePhone

	   $SourceContactElement.StreetAddress = $User.StreetAddress

	   $ElementID++
	}
}

# Disconnect target Exchange session
Disconnect-ExchangeShell -Selector "target"




##------------------------------------------------------------------------------------------------
## compare
##------------------------------------------------------------------------------------------------
##Status monitoring
Update-StatusFiles


#------------------------------------------------------------------------------------------------
# Check Source Elements (Compare your source elements with the contacts in your partners directory)
#------------------------------------------------------------------------------------------------
Write-Log -Entry "[COMPAREOBJECTS] SourceUser::RemoteContact"
if ($SourceElements.Count -ne 0 -or $SourceContactElements.count -ne 0) {
	# Check for updates an new created users in source system
	foreach ($SourceElement in $SourceElements) {
		$CurrentSourceElementHashGUID = $SourceElement.SHHashedGUID
		$SourceElement.SHStatus = "userNotFound"
		if ($SourceContactElements.Count -gt 0) {
			foreach ($SourceContactElement in $SourceContactElements) {
				if ($SourceContactElement.CustomAttribute15 -eq $CurrentSourceElementHashGUID) {
					$SourceElement.SHStatus = "userFound"
					$SourceElement.SHSourceContactElementID = $SourceContactElement.SHElementID
					$SourceContactElement.SHSourceElementID = $SourceElement.SHElementID
					break	
				}
			}
			
			if ($SourceElement.SHStatus -eq "userFound") {
				$SourceContactID = $SourceElement.SHSourceContactElementID
				# Name
				if ($SourceElement.DisplayName -ne $SourceContactElements[$SourceContactID].DisplayName) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[DisplayName.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].DisplayName +";New: "+$SourceElement.DisplayName)
				}
				if ($SourceElement.Name -ne $SourceContactElements[$SourceContactID].Name) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[Name.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].Name +";New: "+$SourceElement.Name)
				}
				if ($SourceElement.LastName -ne $SourceContactElements[$SourceContactID].LastName) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[LastName.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].LastName +";New: "+$SourceElement.LastName)
				}
				if ($SourceElement.FirstName -ne $SourceContactElements[$SourceContactID].FirstName) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[FirstName.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].FirstName +";New: "+$SourceElement.FirstName)
				}
				# Company
				if ($SourceElement.Department -ne $SourceContactElements[$SourceContactID].Department) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[Department.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].Department +";New: "+$SourceElement.Department)
				}
				if ($SourceElement.Title -ne $SourceContactElements[$SourceContactID].Title) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[Title.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].Title +";New: "+$SourceElement.Title)
				}
				if ($SourceElement.Company -ne $SourceContactElements[$SourceContactID].Company) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[Company.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].Company +";New: "+$SourceElement.Company)
				}
				if ($SourceElement.Office -ne $SourceContactElements[$SourceContactID].Office) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[Office.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].Office +";New: "+$SourceElement.Office)
				}
				# Phone
				if ($SourceElement.Phone -ne $SourceContactElements[$SourceContactID].Phone) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[Phone.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].Phone +";New: "+$SourceElement.Phone)
				}
				if ($SourceElement.Fax -ne $SourceContactElements[$SourceContactID].Fax) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[Fax.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].Fax +";New: "+$SourceElement.Fax)
				}
				if ($SourceElement.MobilePhone -ne $SourceContactElements[$SourceContactID].MobilePhone) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[MobilePhone.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].MobilePhone +";New: "+$SourceElement.MobilePhone)
				}
				# Address
				if ($SourceElement.City -ne $SourceContactElements[$SourceContactID].City) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[City.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].City +";New: "+$SourceElement.City)
				}
				if ($SourceElement.PostalCode -ne $SourceContactElements[$SourceContactID].PostalCode) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[PostalCode.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].PostalCode +";New: "+$SourceElement.PostalCode)
				}
				if ($SourceElement.StreetAddress -ne $SourceContactElements[$SourceContactID].StreetAddress) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[StreetAddress.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].StreetAddress +";New: "+$SourceElement.StreetAddress)
				}
				if ($SourceElement.StateOrProvince -ne $SourceContactElements[$SourceContactID].StateOrProvince) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[StateOrPrivonce.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].StateOrProvince +";New: "+$SourceElement.StateOrProvince)
				}
				if ($SourceElement.CountryOrRegion -ne $SourceContactElements[$SourceContactID].CountryOrRegion) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[CountryOrRegion.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].CountryOrRegion +";New: "+$SourceElement.CountryOrRegion)
				}
				# Mail
				if ($SourceElement.PrimarySmtpAddress -ne $SourceContactElements[$SourceContactID].PrimarySmtpAddress) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[PrimarySmtpAddress.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].PrimarySmtpAddress +";New: "+$SourceElement.PrimarySmtpAddress)
				}
				#NOTE: LegacyExchangeDN can be implemented but is at the moment not required to check for updates because source field is more or less a constant.
				if ($SourceElement.Alias -ne ($SourceContactElements[$SourceContactID].Alias)) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[Alias.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].Alias +";New: "+$SourceElement.Alias)
				}
				
			}
		}
	}
	# Check for deleted users in source system
	if ($SourceContactElements.Count -gt 0) {
		foreach ($SourceContactElement in $SourceContactElements) {
			foreach ($SourceElement in $SourceElements) {
				$SourceContactElement.SHStatus = "userRemoved"
				if ($SourceContactElement.CustomAttribute15 -eq $SourceElement.SHHashedGUID) {
					$SourceContactElement.SHStatus = "userExists"
					break	
				}
			}
		}
	}
}



#------------------------------------------------------------------------------------------------
# Check Target Elements (Compare your partners elements with the contacts created in your directory)
#------------------------------------------------------------------------------------------------
Write-Log -Entry "[COMPAREOBJECTS] RemoteUser::SourceContact"
if ($TargetElements.Count -ne 0 -or $TargetContactElements.count -ne 0) {
	# Check for updates an new created users in Target system
	foreach ($TargetElement in $TargetElements) {
		$CurrentTargetElementHashGUID = $TargetElement.SHHashedGUID
		$TargetElement.SHStatus = "userNotFound"
		if ($TargetContactElements.Count -gt 0) {
			foreach ($TargetContactElement in $TargetContactElements) {
				if ($TargetContactElement.CustomAttribute15 -eq $CurrentTargetElementHashGUID) {
					$TargetElement.SHStatus = "userFound"
					$TargetElement.SHTargetContactElementID = $TargetContactElement.SHElementID
					$TargetContactElement.SHTargetElementID = $TargetElement.SHElementID
					break	
				}
			}
			
			if ($TargetElement.SHStatus -eq "userFound") {
				$TargetContactID = $TargetElement.SHTargetContactElementID
				# Name
				if ($TargetElement.DisplayName -ne $TargetContactElements[$TargetContactID].DisplayName) {
					$TargetElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[DisplayName.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].DisplayName +";New: "+$TargetElement.DisplayName)
				}
				if ($TargetElement.Name -ne $TargetContactElements[$TargetContactID].Name) {
					$TargetElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[Name.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].Name +";New: "+$TargetElement.Name)
				}
				if ($TargetElement.LastName -ne $TargetContactElements[$TargetContactID].LastName) {
					$TargetElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[LastName.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].LastName +";New: "+$TargetElement.LastName)
				}
				if ($TargetElement.FirstName -ne $TargetContactElements[$TargetContactID].FirstName) {
					$TargetElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[FirstName.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].FirstName +";New: "+$TargetElement.FirstName)
				}
				# Company
				if ($TargetElement.Department -ne $TargetContactElements[$TargetContactID].Department) {
					$TargetElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[Department.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].Department +";New: "+$TargetElement.Department)
				}
				if ($TargetElement.Title -ne $TargetContactElements[$TargetContactID].Title) {
					$TargetElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[Title.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].Title +";New: "+$TargetElement.Title)
				}
				if ($TargetElement.Company -ne $TargetContactElements[$TargetContactID].Company) {
					$TargetElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[Company.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].Company +";New: "+$TargetElement.Company)
				}
				if ($TargetElement.Office -ne $TargetContactElements[$TargetContactID].Office) {
					$TargetElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[Office.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].Office +";New: "+$TargetElement.Office)
				}
				# Phone
				if ($TargetElement.Phone -ne $TargetContactElements[$TargetContactID].Phone) {
					$TargetElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[Phone.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].Phone +";New: "+$TargetElement.Phone)
				}
				if ($TargetElement.Fax -ne $TargetContactElements[$TargetContactID].Fax) {
					$TargetElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[Fax.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].Fax +";New: "+$TargetElement.Fax)
				}
				if ($TargetElement.MobilePhone -ne $TargetContactElements[$TargetContactID].MobilePhone) {
					$TargetElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[MobilePhone.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].MobilePhone +";New: "+$TargetElement.MobilePhone)
				}
				# Address
				if ($TargetElement.City -ne $TargetContactElements[$TargetContactID].City) {
					$TargetElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[City.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].City +";New: "+$TargetElement.City)
				}
				if ($TargetElement.PostalCode -ne $TargetContactElements[$TargetContactID].PostalCode) {
					$TargetElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[PostalCode.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].PostalCode +";New: "+$TargetElement.PostalCode)
				}
				if ($TargetElement.StreetAddress -ne $TargetContactElements[$TargetContactID].StreetAddress) {
					$TargetElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[StreetAddress.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].StreetAddress +";New: "+$TargetElement.StreetAddress)
				}
				if ($TargetElement.StateOrProvince -ne $TargetContactElements[$TargetContactID].StateOrProvince) {
					$TargetElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[StateOrPrivonce.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].StateOrProvince +";New: "+$TargetElement.StateOrProvince)
				}
				if ($TargetElement.CountryOrRegion -ne $TargetContactElements[$TargetContactID].CountryOrRegion) {
					$TargetElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[CountryOrRegion.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].CountryOrRegion +";New: "+$TargetElement.CountryOrRegion)
				}
				# Mail
				if ($TargetElement.PrimarySmtpAddress -ne $TargetContactElements[$TargetContactID].PrimarySmtpAddress) {
					$TargetElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[PrimarySmtpAddress.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].PrimarySmtpAddress +";New: "+$TargetElement.PrimarySmtpAddress)
				}
				#NOTE: LegacyExchangeDN can be implemented but is at the moment not required to check for updates because Target field is more or less a constant.
				if ($TargetElement.Alias -ne ($TargetContactElements[$TargetContactID].Alias)) {
					$TargetElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[Alias.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].Alias +";New: "+$TargetElement.Alias)
				}
				
			}
		}
	}
	# Check for deleted users in Target system
	if ($TargetContactElements.Count -gt 0) {
		foreach ($TargetContactElement in $TargetContactElements) {
			foreach ($TargetElement in $TargetElements) {
				$TargetContactElement.SHStatus = "userRemoved"
				if ($TargetContactElement.CustomAttribute15 -eq $TargetElement.SHHashedGUID) {
					$TargetContactElement.SHStatus = "userExists"
					break	
				}
			}
		}
	}
}




##------------------------------------------------------------------------------------------------
## Create, update / delete source elements (Your partners elements in your directory)
##------------------------------------------------------------------------------------------------
##Status monitoring
Update-StatusFiles
Connect-SourceExchangeShell



#------------------------------------------------------------------------------------------------
# delete elements  (Your partners users as your contacts)
#------------------------------------------------------------------------------------------------
$DeleteSourceContacts = @()
$DeleteSourceContacts = $TargetContactElements | Where-Object {$_.SHStatus -eq "userRemoved"} | Select-Object -Property DistinguishedName, CustomAttribute14
if ($DeleteSourceContacts) {
	foreach ($DeleteContact in $DeleteSourceContacts) {
		$doNotDelete = $true
		#Switch update only contacts created by script...
		if ($UpdateDeleteContactsOnlyCreatedByThisScript) {
			if ($DeleteContact.CustomAttribute14 -eq $ThisScriptGUID) {
				$doNotDelete = $false
			}
		} 
		else {
			$doNotDelete = $false
		}
		if (-not $doNotDelete) {
			$DeleteContactDN = $DeleteContact.DistinguishedName
			Write-Log -DebugEntry ("[DELETE-SOURCECONTACT] $DeleteContactDN")
			Remove-MailContact -Identity "$DeleteContactDN" -Confirm:$Y 
		}
	}
}
else {
	Write-Log -DebugEntry ("[DELETE-SOURCECONTACT] no contact to delete")
}



#------------------------------------------------------------------------------------------------
# update elements  (Your partners users as your contacts)
#------------------------------------------------------------------------------------------------
$UpdateSourceElements = @()
$UpdateSourceElements = $TargetElements | Where-Object {$_.SHStatus -eq "userUpdated"} | Select-Object -ExpandProperty SHTargetContactElementID
if ($UpdateSourceElements) {
	foreach ($UpdateContactID in $UpdateSourceElements) {
		$ContactDN = $TargetContactElements[$UpdateContactID].DistinguishedName
		$TargetElementID = $TargetContactElements[$UpdateContactID].SHTargetElementID
		$CustomAttribute14 = $TargetContactElements[$UpdateContactID].CustomAttribute14
		$doNotUpdate = $true
		#Switch update only contacts created by script...
		if ($UpdateDeleteContactsOnlyCreatedByThisScript) {
			if ($CustomAttribute14 -eq $ThisScriptGUID) {
				$doNotUpdate = $false
			}
		} 
		else {
			$doNotUpdate = $false
		}
		if (-not $doNotUpdate) {
			Write-Log -DebugEntry ("[UPDATE-SOURCECONTACT] $ContactDN")
			$null = Set-MailContact -Identity "$ContactDN" -Alias ($TargetElements[$TargetElementID]).Alias -ErrorAction SilentlyContinue -WarningAction SilentlyContinue `
													-DisplayName ($TargetElements[$TargetElementID]).DisplayName `
													-ExternalEmailAddress ($TargetElements[$TargetElementID]).PrimarySmtpAddress
		
			$null = Set-Contact  -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -WarningVariable $SetContactWarning -Identity "$ContactDN" `
										-Name  ($TargetElements[$TargetElementID]).Name `
										-LastName ($TargetElements[$TargetElementID]).LastName `
										-FirstName ($TargetElements[$TargetElementID]).FirstName `
										-Department ($TargetElements[$TargetElementID]).Department `
										-Title ($TargetElements[$TargetElementID]).Title `
										-Company ($TargetElements[$TargetElementID]).Company `
										-Office ($TargetElements[$TargetElementID]).Office `
										-Phone ($TargetElements[$TargetElementID]).Phone `
										-Fax ($TargetElements[$TargetElementID]).Fax `
										-MobilePhone ($TargetElements[$TargetElementID]).MobilePhone `
										-City ($TargetElements[$TargetElementID]).City `
										-PostalCode ($TargetElements[$TargetElementID]).PostalCode `
										-StreetAddress ($TargetElements[$TargetElementID]).StreetAddress `
										-StateOrProvince ($TargetElements[$TargetElementID]).StateOrProvince `
										-CountryOrRegion ($TargetElements[$TargetElementID]).CountryOrRegion
		}
	}
}
else {
	Write-Log -DebugEntry ("[UPDATE-SOURCECONTACT] no contact to update")
}



#------------------------------------------------------------------------------------------------
# create elements (Your partners users as your contacts)
#------------------------------------------------------------------------------------------------
$NewSourceElements = @()
$NewSourceElements = $TargetElements | Where-Object {$_.SHStatus -eq "userNotFound"} | Select-Object -ExpandProperty SHElementID
if ($NewSourceElements) {
	foreach ($TargetElementID in $NewSourceElements) {
		$NewMailContactWarning = $null
		$SetMailContactWarning = $null
		$SetContactWarning = $null
		Write-Log -DebugEntry ("[CREATE-SOURCECONTACT] "+($TargetElements[$TargetElementID]).DisplayName)
		$null = New-MailContact -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -WarningVariable $NewMailContactWarning -Alias ($TargetElements[$TargetElementID]).Alias `
								   -DisplayName ($TargetElements[$TargetElementID]).DisplayName `
								   -Name ($TargetElements[$TargetElementID]).Name `
								   -ExternalEmailAddress ($TargetElements[$TargetElementID]).PrimarySmtpAddress `
								   -OrganizationalUnit $SourceContactOU 
		
		#Check if contact was created in Target
		if (WaitForContactCreation -ContactMail ($TargetElements[$TargetElementID]).PrimarySmtpAddress) {
			#Create X500 version of the LegacyExchangeDN
			$LegacyDN = ("X500:"+($TargetElements[$TargetElementID]).LegacyExchangeDN)
			
			Write-Log -DebugEntry ("[CREATE-SOURCECONTACT] Add attributes (LegacyExchangeDN, Hashed Exchange GUID and Hashed Script GUID)")

			$null = Set-MailContact  -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -WarningVariable $SetMailContactWarning -Identity ($TargetElements[$TargetElementID]).PrimarySmtpAddress `
										-EmailAddresses @{add=$LegacyDN} `
										-CustomAttribute15 (($TargetElements[$TargetElementID]).SHHashedGUID) `
										-CustomAttribute14 $ThisScriptGUID
			
			Write-Log -DebugEntry ("[CREATE-SOURCECONTACT] Add attributes (FirstName, Title, LastName, Phone, City, Company, PostalCode, Office, Department)")
			$null = Set-Contact  -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -WarningVariable $SetContactWarning -Identity ($TargetElements[$TargetElementID]).PrimarySmtpAddress `
									-LastName ($TargetElements[$TargetElementID]).LastName `
									-FirstName ($TargetElements[$TargetElementID]).FirstName `
									-Department ($TargetElements[$TargetElementID]).Department `
									-Title ($TargetElements[$TargetElementID]).Title `
									-Company ($TargetElements[$TargetElementID]).Company `
									-Office ($TargetElements[$TargetElementID]).Office `
									-Phone ($TargetElements[$TargetElementID]).Phone `
									-Fax ($TargetElements[$TargetElementID]).Fax `
									-MobilePhone ($TargetElements[$TargetElementID]).MobilePhone `
									-City ($TargetElements[$TargetElementID]).City `
									-PostalCode ($TargetElements[$TargetElementID]).PostalCode `
									-StreetAddress ($TargetElements[$TargetElementID]).StreetAddress `
									-StateOrProvince ($TargetElements[$TargetElementID]).StateOrProvince `
									-CountryOrRegion ($TargetElements[$TargetElementID]).CountryOrRegion
		
			Write-Log -DebugEntry ("[CREATE-SOURCECONTACT] Finished creating contact "+($TargetElements[$TargetElementID]).DisplayName)
			
		}
		
		#Write possible errors to DEBUGLOG
		if ($NewMailContactWarning -or $SetMailContactWarning -or $SetContactWarning) {
			Write-Log -DebugEntry ("[CREATE-SOURCECONTACT] NewMailContactWarning: "+$NewMailContactWarning)
			Write-Log -DebugEntry ("[CREATE-SOURCECONTACT] SetMailContactWarning: "+$NewMailContactWarning)
			Write-Log -DebugEntry ("[CREATE-SOURCECONTACT] SetContactWarning: "+$NewMailContactWarning)
		}
	}
}
else {
	Write-Log -DebugEntry ("[CREATE-SOURCECONTACT] no contact to create")
}




##------------------------------------------------------------------------------------------------
## Create, update / delete target elements (Your elements in your partners directory)
##------------------------------------------------------------------------------------------------
##Status monitoring
Update-StatusFiles
Disconnect-ExchangeShell -Selector "source"
Connect-TargetExchangeShell



#------------------------------------------------------------------------------------------------
# delete elements  (Your Users as your Partners Contacts)
#------------------------------------------------------------------------------------------------
$DeleteTargetContacts = @()
$DeleteTargetContacts = $SourceContactElements | Where-Object {$_.SHStatus -eq "userRemoved"} | Select-Object -Property DistinguishedName, CustomAttribute14
if ($DeleteTargetContacts) {
	foreach ($DeleteContact in $DeleteTargetContacts) {
		$doNotDelete = $true
		#Switch update only contacts created by script...
		if ($UpdateDeleteContactsOnlyCreatedByThisScript) {
			if ($DeleteContact.CustomAttribute14 -eq $ThisScriptGUID) {
				$doNotDelete = $false
			}
		} 
		else {
			$doNotDelete = $false
		}
		if (-not $doNotDelete) {
			$DeleteContactDN = $DeleteContact.DistinguishedName
			Write-Log -DebugEntry ("[DELETE-TARGETCONTACT] $DeleteContactDN")
			Remove-MailContact -Identity "$DeleteContactDN" -Confirm:$Y 
		}
	}
}
else {
	Write-Log -DebugEntry ("[DELETE-TARGETCONTACT] no contact to delete")
}



#------------------------------------------------------------------------------------------------
# update elements  (Your Users as your Partners Contacts)
#------------------------------------------------------------------------------------------------
$UpdateElements = @()
$UpdateElements = $SourceElements | Where-Object {$_.SHStatus -eq "userUpdated"} | Select-Object -ExpandProperty SHSourceContactElementID
if ($UpdateElements) {
	foreach ($UpdateContactID in $UpdateElements) {
		$ContactDN = $SourceContactElements[$UpdateContactID].DistinguishedName
		$SourceElementID = $SourceContactElements[$UpdateContactID].SHSourceElementID
		$CustomAttribute14 = $SourceContactElements[$UpdateContactID].CustomAttribute14
		$doNotUpdate = $true
		#Switch update only contacts created by script...
		if ($UpdateDeleteContactsOnlyCreatedByThisScript) {
			if ($CustomAttribute14 -eq $ThisScriptGUID) {
				$doNotUpdate = $false
			}
		} 
		else {
			$doNotUpdate = $false
		}
		if (-not $doNotUpdate) {
			Write-Log -DebugEntry ("[UPDATE-TARGETCONTACT] $ContactDN")
			$null = Set-MailContact -Identity "$ContactDN" -Alias ($SourceElements[$SourceElementID]).Alias -ErrorAction SilentlyContinue -WarningAction SilentlyContinue `
													-DisplayName ($SourceElements[$SourceElementID]).DisplayName `
													-ExternalEmailAddress ($SourceElements[$SourceElementID]).PrimarySmtpAddress
		
			$null = Set-Contact  -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -WarningVariable $SetContactWarning -Identity "$ContactDN" `
										-Name  ($SourceElements[$SourceElementID]).Name `
										-LastName ($SourceElements[$SourceElementID]).LastName `
										-FirstName ($SourceElements[$SourceElementID]).FirstName `
										-Department ($SourceElements[$SourceElementID]).Department `
										-Title ($SourceElements[$SourceElementID]).Title `
										-Company ($SourceElements[$SourceElementID]).Company `
										-Office ($SourceElements[$SourceElementID]).Office `
										-Phone ($SourceElements[$SourceElementID]).Phone `
										-Fax ($SourceElements[$SourceElementID]).Fax `
										-MobilePhone ($SourceElements[$SourceElementID]).MobilePhone `
										-City ($SourceElements[$SourceElementID]).City `
										-PostalCode ($SourceElements[$SourceElementID]).PostalCode `
										-StreetAddress ($SourceElements[$SourceElementID]).StreetAddress `
										-StateOrProvince ($SourceElements[$SourceElementID]).StateOrProvince `
										-CountryOrRegion ($SourceElements[$SourceElementID]).CountryOrRegion
		}
	}
}
else {
	Write-Log -DebugEntry ("[UPDATE-TARGETCONTACT] no contact to update")
}



#------------------------------------------------------------------------------------------------
# create elements (Your Users as your Partners Contacts)
#------------------------------------------------------------------------------------------------
$NewElements = @()
$NewElements = $SourceElements | Where-Object {$_.SHStatus -eq "userNotFound"} | Select-Object -ExpandProperty SHElementID
if ($NewElements) {
	foreach ($SourceElementID in $NewElements) {
		$NewMailContactWarning = $null
		$SetMailContactWarning = $null
		$SetContactWarning = $null
		Write-Log -DebugEntry ("[CREATE-TARGETCONTACT] "+($SourceElements[$SourceElementID]).DisplayName)
		$null = New-MailContact -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -WarningVariable $NewMailContactWarning -Alias ($SourceElements[$SourceElementID]).Alias `
								   -DisplayName ($SourceElements[$SourceElementID]).DisplayName `
								   -Name ($SourceElements[$SourceElementID]).Name `
								   -ExternalEmailAddress ($SourceElements[$SourceElementID]).PrimarySmtpAddress `
								   -OrganizationalUnit $TargetContactOU 
		
		#Check if contact was created in Target
		if (WaitForContactCreation -ContactMail ($SourceElements[$SourceElementID]).PrimarySmtpAddress) {
			#Create X500 version of the LegacyExchangeDN
			$LegacyDN = ("X500:"+($SourceElements[$SourceElementID]).LegacyExchangeDN)
			
			Write-Log -DebugEntry ("[CREATE-TARGETCONTACT] Add attributes (LegacyExchangeDN, Hashed Exchange GUID and Hashed Script GUID)")

			$null = Set-MailContact  -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -WarningVariable $SetMailContactWarning -Identity ($SourceElements[$SourceElementID]).PrimarySmtpAddress `
										-EmailAddresses @{add=$LegacyDN} `
										-CustomAttribute15 (($SourceElements[$SourceElementID]).SHHashedGUID) `
										-CustomAttribute14 $ThisScriptGUID
			
			Write-Log -DebugEntry ("[CREATE-TARGETCONTACT] Add attributes (FirstName, Title, LastName, Phone, City, Company, PostalCode, Office, Department)")
			$null = Set-Contact  -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -WarningVariable $SetContactWarning -Identity ($SourceElements[$SourceElementID]).PrimarySmtpAddress `
									-LastName ($SourceElements[$SourceElementID]).LastName `
									-FirstName ($SourceElements[$SourceElementID]).FirstName `
									-Department ($SourceElements[$SourceElementID]).Department `
									-Title ($SourceElements[$SourceElementID]).Title `
									-Company ($SourceElements[$SourceElementID]).Company `
									-Office ($SourceElements[$SourceElementID]).Office `
									-Phone ($SourceElements[$SourceElementID]).Phone `
									-Fax ($SourceElements[$SourceElementID]).Fax `
									-MobilePhone ($SourceElements[$SourceElementID]).MobilePhone `
									-City ($SourceElements[$SourceElementID]).City `
									-PostalCode ($SourceElements[$SourceElementID]).PostalCode `
									-StreetAddress ($SourceElements[$SourceElementID]).StreetAddress `
									-StateOrProvince ($SourceElements[$SourceElementID]).StateOrProvince `
									-CountryOrRegion ($SourceElements[$SourceElementID]).CountryOrRegion
		
			Write-Log -DebugEntry ("[CREATE-TARGETCONTACT] Finished creating contact "+($SourceElements[$SourceElementID]).DisplayName)
			
		}
		
		#Write possible errors to DEBUGLOG
		if ($NewMailContactWarning -or $SetMailContactWarning -or $SetContactWarning) {
			Write-Log -DebugEntry ("[CREATE-TARGETCONTACT] NewMailContactWarning: "+$NewMailContactWarning)
			Write-Log -DebugEntry ("[CREATE-TARGETCONTACT] SetMailContactWarning: "+$NewMailContactWarning)
			Write-Log -DebugEntry ("[CREATE-TARGETCONTACT] SetContactWarning: "+$NewMailContactWarning)
		}
	}
}
else {
	Write-Log -DebugEntry ("[CREATE-TARGETCONTACT] no contact to create")
}


##------------------------------------------------------------------------------------------------
## Create, update / delete target elements (Your elements in your partners directory)
##------------------------------------------------------------------------------------------------
Disconnect-ExchangeShell -Selector "target"
$SourceElements = $null
$SourceContactElements  = $null
$TargetElements = $null
$TargetContactElements  = $null
Write-Log -Entry "Script execution is finished!"

	

>> syntax highlighting powered by highlight.js