PS: SH-SOFT GAL Sync - ADDS (<)-> ADDS

This script version is designed to synchronize AD users / contscts with AD Power Shell commandlets between two active directory domains / forests. It can also be used to do a one way synchronization from a sour ce to a target environment.
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 password required to access the target environment can be stored using Power Shell please use the helper script provided >> here.


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:

  • TargetDCList
    A list to domaincontrollers to be used on target side.
  • TargetDomainNetBiosName
    This represents the NetBIOS name of the target environment. It is used to determine if the connection to the target DC is possible.
  • 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).
  • DoNotSearchWithinTarget
    If set to true the script runs in one way mode and does not write anything to the source environment.
  • 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.
  • TargetObjectClass
    Can be used to define an object class to search for in the target environemnt.
  • SourceDCList
    A list to domaincontrollers to be used on target side.
  • SourceDomainNetBiosName
    This represents the NetBIOS name of the source environment. It is used to determine if the connection to the source DC is possible.
  • 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.
  • SourceObjectClass
    Can be used to define an object class to search for in the target environemnt.
  • 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: Bug fixes: New contacts in Target environment have not been created

Download the script:

>> Version 1.0.1 (current)
(MD5: b0ad80f86334b0fe503f3ee8946e2d13)
(SHA1: 62e0d7e027a17f607469bad62bf3c430080dc86c)


Script code:

	
##------------------------------------------------------------------------------------------------
##
##  SH-SOFT-GALSync-ADDSADDS.ps1
##
##   Version 1.0.1
##
##
##   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-adds-adds.html for instructions about the script. Thanks.

This script version can synchronize users between two Active Directory forests.

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 environment
#
#   this variables contain connection information to your partners ADDS server
#
#------------------------------------------------------------------------------------------------
# FQDN server name of the partners exchange environment
$TargetDCList = @("TDC1.sh-soft.com", "TDC2.sh-soft.com")

#Domain name is used verify connectivity to target domaincontroller should be: "(Get-ADDomain -Server (Read-Host -Prompt "Enter server name") -Credential (Get-Credential)).NetBIOSName"
$TargetDomainNetBiosName = "SH-SOFT" 

# Search scopes to look for mail enabled users
$TargetSearchScopes = @("OU=SomeUsers,dc=sh-soft,dc=com", "OU=SomeMoreUsers,dc=sh-soft,dc=com")

# if you enable the following setting ist will do a one way sync from local to target environment
$DoNotSearchWithinTarget = $false


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


# 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\service_addssync" 

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

# Target object class (used in the query to find objects to be created on the source side)
$TargetObjectClass = "(ObjectClass -eq 'user')"


#------------------------------------------------------------------------------------------------
## Source environment
#
#   this variables contain connection infromation of your ADDS Environment
#
#------------------------------------------------------------------------------------------------
# FQDN server name of the local exchange environment
$SourceDCList = @("sdc1.schnitzelbroetchen.info", "sdc2.schnitzelbroetchen.info")

#Domain name is used verify connectivity to target domaincontroller should be: "(Get-ADDomain -Server (Read-Host -Prompt "Enter server name")).NetBIOSName"
$SourceDomainNetBiosName = "SB" 

# Search scopes to look for mail enabled users
$SourceSearchScopes = @("OU=MyUsers,DC=schnitzelbroetchen,DC=info")

# You have to verfiy this one is not within the SearchScopes!
$SourceContactOU = "OU=SH-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 = ""

# Source object class (used in the query to find objects to be created on the target side)
$SourceObjectClass = "(ObjectClass -eq 'User')"


#------------------------------------------------------------------------------------------------
# Logging
#------------------------------------------------------------------------------------------------
$LogFilePath = (Split-Path -Parent -Path $MyInvocation.MyCommand.Definition) #default points to the script directory
$LogFileBaseName = "_SH-SOFT-GALSync-ADDSADDS.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)
	}
}



#------------------------------------------------------------------------------------------------
# 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
}

#------------------------------------------------------------------------------------------------
#Load ADDS Power Shell module if this one is not already loaded...
if (-Not (Get-Module -Name ActiveDirectory)) {
	try {
		Write-Log -Entry "Importing ADDS module for powershell"
		Import-Module ActiveDirectory -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
		Write-Log -DebugEntry "done"
	}
	catch {
		Write-Log -Entry "[ERROR] failed to load AD module for power shell. please ensure it is installed."
		exit
	}	
}


#------------------------------------------------------------------------------------------------
# establish connection to target side (Test credential and connection)
$TargetDC = $null
foreach ($testDC in $TargetDCList) {
	if ((Get-ADDomain -Server $testDC -Credential $TargetCredential -ErrorAction SilentlyContinue -WarningAction SilentlyContinue).NetBIOSName -eq $TargetDomainNetBiosName) {
		Write-Log -Entry "[TESTCONNECTION] Connection to target DC: $testDC has been verified"
		$TargetDC = $testDC
		break
	}
	else {
		Write-Log -Entry "[TESTCONNECTION] A connection to a target domaincontroller in the server list could not be established. The server used was: $testDC"
	}
}
if ($TargetDC -eq $null) {
	Write-Log -Entry "[ERROR] None of the servers in the target server list was accessible or the credentials used did not match!"
	exit
}


#------------------------------------------------------------------------------------------------
# establish connection to source side (Test credential and connection)
$SourceDC = $null
foreach ($testDC in $SourceDCList) {
	if ((Get-ADDomain -Server $testDC -ErrorAction SilentlyContinue -WarningAction SilentlyContinue).NetBIOSName -eq $SourceDomainNetBiosName) {
		Write-Log -Entry "[TESTCONNECTION] Connection to source DC: $testDC has been verified"
		$SourceDC = $testDC
		break
	}
	else {
		Write-Log -Entry "[TESTCONNECTION] A connection to a source domaincontroller in the server list could not be established. The server used was: $testDC"
	}
}
if ($SourceDC -eq $null) {
	Write-Log -Entry "[ERROR] None of the servers in the source server list was accessible or the credentials used did not match!"
	exit
}



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


#------------------------------------------------------------------------------------------------
# Get source real users
#------------------------------------------------------------------------------------------------
$SourceElements = @()
$SourceFilter = 'mail -like "*" -and {0}' -f "$SourceObjectClass"
foreach ($OU in $SourceSearchScopes) {
	$SourceElements += Get-ADObject -SearchBase "$OU" -Filter "$SourceFilter" -Server $SourceDC -Properties DisplayName, Name, sn, givenName,msExchHideFromAddressLists, `
																							department, title, company, physicalDeliveryOfficeName, `
																							telephoneNumber, facsimileTelephoneNumber, mobile, `
																							l, postalCode, streetAddress, st, co, `
																							mail, legacyExchangeDN, mailNickname, `
																							extensionAttribute14, extensionAttribute15, DistinguishedName, objectGUID | `
							Where-Object {$_.msExchHideFromAddressLists -ne $true } | `
							Select-Object -Property SHElementID, SHHashedGUID, SHStatus, SHSourceContactElementID, `
													DisplayName, Name, sn, givenName,msExchHideFromAddressLists, `
													department, title, company, physicalDeliveryOfficeName, `
													telephoneNumber, facsimileTelephoneNumber, mobile, `
													l, postalCode, streetAddress, st, co, `
													mail, legacyExchangeDN, mailNickname, `
													extensionAttribute14, extensionAttribute15, DistinguishedName, objectGUID																												
}

# Create Hashed GUID foreach recipient and collect additional attributes
$ElementID = 0
foreach ($SourceElement in $SourceElements) {
	$CurrentDN = $SourceElement.DistinguishedName
	Write-Log -DebugEntry ("[SOURCEUSERATTRIBUTE-COLLECTION] $CurrentDN")
	
	#Update new values to existing object
	$SourceElement.SHElementID = $ElementID	
	$SourceElement.SHHashedGUID = Create-SHAHash -Value $SourceElement.objectGUID.ToString()
	
	#Set Alias Prefix 
	$SourceElement.mailNickname = $SourceAliasPrefix+$SourceElement.mailNickname
	
	$ElementID++
}


#------------------------------------------------------------------------------------------------
# Get Contacts in SourceContacts OU (Partners Addresses in Source directory)
#------------------------------------------------------------------------------------------------
if (-not $DoNotSearchWithinTarget) {
	$TargetContactElements = @()
	$TargetContactElements = Get-ADObject -SearchBase "$SourceContactOU" -Filter "ObjectClass -eq 'Contact'" -Server $SourceDC -Properties DisplayName, Name, sn, givenName,msExchHideFromAddressLists, `
																							department, title, company, physicalDeliveryOfficeName, `
																							telephoneNumber, facsimileTelephoneNumber, mobile, `
																							l, postalCode, streetAddress, st, co, `
																							mail, legacyExchangeDN, mailNickname, `
																							extensionAttribute14, extensionAttribute15, DistinguishedName, objectGUID | `
								Select-Object -Property SHElementID, SHHashedGUID, SHStatus, SHTargetElementID, `
														DisplayName, Name, sn, givenName,msExchHideFromAddressLists, `
														department, title, company, physicalDeliveryOfficeName, `
														telephoneNumber, facsimileTelephoneNumber, mobile, `
														l, postalCode, streetAddress, st, co, `
														mail, legacyExchangeDN, mailNickname, `
														extensionAttribute14, extensionAttribute15, DistinguishedName, objectGUID

	$ElementID = 0
	if ($TargetContactElements.Count -gt 0) {
		foreach ($TargetContactElement in $TargetContactElements) {
		   $CurrentDN = $TargetContactElement.DistinguishedName
		   Write-Log -DebugEntry ("[TARGETCONTACTATTRIBUTE-COLLECTION] $CurrentDN")
		   
		   $TargetContactElement.SHElementID = $ElementID

		   $ElementID++
		}
	}
}




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


#------------------------------------------------------------------------------------------------
# Get Elements from Target Domain
#------------------------------------------------------------------------------------------------
if (-not $DoNotSearchWithinTarget) {
	$TargetElements = @()
	$TargetFilter = 'mail -like "*" -and {0}' -f "$TargetObjectClass"
	foreach ($OU in $TargetSearchScopes) {
		$TargetElements += Get-ADObject -SearchBase "$OU" -Filter "$TargetFilter" -Server $TargetDC -Credential $TargetCredential -Properties DisplayName, Name, sn, givenName,msExchHideFromAddressLists, `
																							department, title, company, physicalDeliveryOfficeName, `
																							telephoneNumber, facsimileTelephoneNumber, mobile, `
																							l, postalCode, streetAddress, st, co, `
																							mail, legacyExchangeDN, mailNickname, `
																							extensionAttribute14, extensionAttribute15, DistinguishedName, objectGUID | `																				
											Where-Object {$_.msExchHideFromAddressLists -ne $true } | `
											Select-Object -Property SHElementID, SHHashedGUID, SHStatus, SHSourceContactElementID, `
													DisplayName, Name, sn, givenName,msExchHideFromAddressLists, `
													department, title, company, physicalDeliveryOfficeName, `
													telephoneNumber, facsimileTelephoneNumber, mobile, `
													l, postalCode, streetAddress, st, co, `
													mail, legacyExchangeDN, mailNickname, `
													extensionAttribute14, extensionAttribute15, DistinguishedName, objectGUID	   
	}
	# Create Hased GUID foreach recipient
	$ElementID = 0
	foreach ($TargetElement in $TargetElements) {
		$CurrentDN = $TargetElement.DistinguishedName
		Write-Log -DebugEntry ("[TARGETUSERATTRIBUTE-COLLECTION] $CurrentDN")
		
		#Update new values to existing object
		$TargetElement.SHElementID = $ElementID	
		$TargetElement.SHHashedGUID = Create-SHAHash -Value $TargetElement.objectGUID.ToString()
	
		#Set Alias Prefix 
		$TargetElement.mailNickname = $TargetAliasPrefix+$TargetElement.mailNickname
	
		$ElementID++
	}
}


#------------------------------------------------------------------------------------------------
# Get Contacts in TargetContacts OU (Your Addresses in target directory)
#------------------------------------------------------------------------------------------------
$SourceContactElements = @()
$SourceContactElements = Get-ADObject -SearchBase "$TargetContactOU" -Filter "ObjectClass -eq 'Contact'" -Server $TargetDC -Credential $TargetCredential -Properties DisplayName, Name, sn, givenName,msExchHideFromAddressLists, `
																						department, title, company, physicalDeliveryOfficeName, `
																						telephoneNumber, facsimileTelephoneNumber, mobile, `
																						l, postalCode, streetAddress, st, co, `
																						mail, legacyExchangeDN, mailNickname, `
																						extensionAttribute14, extensionAttribute15, DistinguishedName, objectGUID | `
							Select-Object -Property SHElementID, SHHashedGUID, SHStatus, SHSourceElementID, `
													DisplayName, Name, sn, givenName,msExchHideFromAddressLists, `
													department, title, company, physicalDeliveryOfficeName, `
													telephoneNumber, facsimileTelephoneNumber, mobile, `
													l, postalCode, streetAddress, st, co, `
													mail, legacyExchangeDN, mailNickname, `
													extensionAttribute14, extensionAttribute15, DistinguishedName, objectGUID

$ElementID = 0
if ($SourceContactElements.Count -gt 0) {
	foreach ($SourceContactElement in $SourceContactElements) {
		$CurrentDN = $SourceContactElement.DistinguishedName
		Write-Log -DebugEntry ("[SOURCECONTACTATTRIBUTE-COLLECTION] $CurrentDN")
		   
		$SourceContactElement.SHElementID = $ElementID

		$ElementID++
	}
}





##------------------------------------------------------------------------------------------------
## 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.extensionAttribute15 -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.sn -ne $SourceContactElements[$SourceContactID].sn) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[sn.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].sn +";New: "+$SourceElement.sn)
				}
				if ($SourceElement.GivenName -ne $SourceContactElements[$SourceContactID].GivenName) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[GivenName.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].GivenName +";New: "+$SourceElement.GivenName)
				}
				# 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.physicalDeliveryOfficeName -ne $SourceContactElements[$SourceContactID].physicalDeliveryOfficeName) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[physicalDeliveryOfficeName.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].physicalDeliveryOfficeName +";New: "+$SourceElement.physicalDeliveryOfficeName)
				}
				# telephoneNumber
				if ($SourceElement.telephoneNumber -ne $SourceContactElements[$SourceContactID].telephoneNumber) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[telephoneNumber.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].telephoneNumber +";New: "+$SourceElement.telephoneNumber)
				}
				if ($SourceElement.facsimileTelephoneNumber -ne $SourceContactElements[$SourceContactID].facsimileTelephoneNumber) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[facsimileTelephoneNumber.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].facsimileTelephoneNumber +";New: "+$SourceElement.facsimileTelephoneNumber)
				}
				if ($SourceElement.mobile -ne $SourceContactElements[$SourceContactID].mobile) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[mobile.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].mobile +";New: "+$SourceElement.mobile)
				}
				# Address
				if ($SourceElement.l -ne $SourceContactElements[$SourceContactID].l) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[l.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].l +";New: "+$SourceElement.l)
				}
				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.st -ne $SourceContactElements[$SourceContactID].st) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[StateOrPrivonce.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].st +";New: "+$SourceElement.st)
				}
				if ($SourceElement.co -ne $SourceContactElements[$SourceContactID].co) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[co.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].co +";New: "+$SourceElement.co)
				}
				# Mail
				if ($SourceElement.mail -ne $SourceContactElements[$SourceContactID].mail) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[mail.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].mail +";New: "+$SourceElement.mail)
				}
				#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.mailNickname -ne ($SourceContactElements[$SourceContactID].mailNickname)) {
					$SourceElement.SHStatus = "userUpdated"
					Write-Log -DebugEntry ("[mailNickname.Updated]"+$CurrentSourceElementHashGUID+";Old: "+ $SourceContactElements[$SourceContactID].mailNickname +";New: "+$SourceElement.mailNickname)
				}
				
			}
		}
	}
	# 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.extensionAttribute15 -eq $SourceElement.SHHashedGUID) {
					$SourceContactElement.SHStatus = "userExists"
					break	
				}
			}
		}
	}
}



#------------------------------------------------------------------------------------------------
# Check Target Elements (Compare your partners elements with the contacts created in your directory)
#------------------------------------------------------------------------------------------------
if (-not $DoNotSearchWithinTarget) {
	Write-Log -Entry "[COMPAREOBJECTS] RemoteUser::TargetContact"
	if ($TargetElements.Count -ne 0 -or $TargetContactElements.count -ne 0) {
		# Check for updates an new created users in source system
		foreach ($TargetElement in $TargetElements) {
			$CurrentTargetElementHashGUID = $TargetElement.SHHashedGUID
			$TargetElement.SHStatus = "userNotFound"
			if ($TargetContactElements.Count -gt 0) {
				foreach ($TargetContactElement in $TargetContactElements) {
					if ($TargetContactElement.extensionAttribute15 -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.sn -ne $TargetContactElements[$TargetContactID].sn) {
						$TargetElement.SHStatus = "userUpdated"
						Write-Log -DebugEntry ("[sn.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].sn +";New: "+$TargetElement.sn)
					}
					if ($TargetElement.GivenName -ne $TargetContactElements[$TargetContactID].GivenName) {
						$TargetElement.SHStatus = "userUpdated"
						Write-Log -DebugEntry ("[GivenName.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].GivenName +";New: "+$TargetElement.GivenName)
					}
					# 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.physicalDeliveryOfficeName -ne $TargetContactElements[$TargetContactID].physicalDeliveryOfficeName) {
						$TargetElement.SHStatus = "userUpdated"
						Write-Log -DebugEntry ("[physicalDeliveryOfficeName.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].physicalDeliveryOfficeName +";New: "+$TargetElement.physicalDeliveryOfficeName)
					}
					# telephoneNumber
					if ($TargetElement.telephoneNumber -ne $TargetContactElements[$TargetContactID].telephoneNumber) {
						$TargetElement.SHStatus = "userUpdated"
						Write-Log -DebugEntry ("[telephoneNumber.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].telephoneNumber +";New: "+$TargetElement.telephoneNumber)
					}
					if ($TargetElement.facsimileTelephoneNumber -ne $TargetContactElements[$TargetContactID].facsimileTelephoneNumber) {
						$TargetElement.SHStatus = "userUpdated"
						Write-Log -DebugEntry ("[facsimileTelephoneNumber.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].facsimileTelephoneNumber +";New: "+$TargetElement.facsimileTelephoneNumber)
					}
					if ($TargetElement.mobile -ne $TargetContactElements[$TargetContactID].mobile) {
						$TargetElement.SHStatus = "userUpdated"
						Write-Log -DebugEntry ("[mobile.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].mobile +";New: "+$TargetElement.mobile)
					}
					# Address
					if ($TargetElement.l -ne $TargetContactElements[$TargetContactID].l) {
						$TargetElement.SHStatus = "userUpdated"
						Write-Log -DebugEntry ("[l.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].l +";New: "+$TargetElement.l)
					}
					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.st -ne $TargetContactElements[$TargetContactID].st) {
						$TargetElement.SHStatus = "userUpdated"
						Write-Log -DebugEntry ("[StateOrPrivonce.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].st +";New: "+$TargetElement.st)
					}
					if ($TargetElement.co -ne $TargetContactElements[$TargetContactID].co) {
						$TargetElement.SHStatus = "userUpdated"
						Write-Log -DebugEntry ("[co.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].co +";New: "+$TargetElement.co)
					}
					# Mail
					if ($TargetElement.mail -ne $TargetContactElements[$TargetContactID].mail) {
						$TargetElement.SHStatus = "userUpdated"
						Write-Log -DebugEntry ("[mail.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].mail +";New: "+$TargetElement.mail)
					}
					#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 ($TargetElement.mailNickname -ne ($TargetContactElements[$TargetContactID].mailNickname)) {
						$TargetElement.SHStatus = "userUpdated"
						Write-Log -DebugEntry ("[mailNickname.Updated]"+$CurrentTargetElementHashGUID+";Old: "+ $TargetContactElements[$TargetContactID].mailNickname +";New: "+$TargetElement.mailNickname)
					}
				
				}
			}
		}
		# Check for deleted users in source system
		if ($TargetContactElements.Count -gt 0) {
			foreach ($TargetContactElement in $TargetContactElements) {
				foreach ($TargetElement in $TargetElements) {
					$TargetContactElement.SHStatus = "userRemoved"
					if ($TargetContactElement.extensionAttribute15 -eq $TargetElement.SHHashedGUID) {
						$TargetContactElement.SHStatus = "userExists"
						break	
					}
				}
			}
		}
	}
}



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



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


#------------------------------------------------------------------------------------------------
# update elements  (Your partners users as your contacts)
#------------------------------------------------------------------------------------------------
if (-not $DoNotSearchWithinTarget) {
	$UpdateSourceElements = @()
	$UpdateSourceElements = $TargetElements | Where-Object {$_.SHStatus -eq "userUpdated"} | Select-Object -ExpandProperty SHTargetContactElementID
	if ($UpdateSourceElements) {
		foreach ($UpdateContactID in $UpdateElements) {
			$ContactDN = $TargetContactElements[$UpdateContactID].DistinguishedName
			$TargetElementID = $TargetContactElements[$UpdateContactID].SHTargetElementID
			$extensionAttribute14 = $TargetContactElements[$UpdateContactID].extensionAttribute14
			$doNotUpdate = $true
			#Switch update only contacts created by script...
			if ($UpdateDeleteContactsOnlyCreatedByThisScript) {
				if ($extensionAttribute14 -eq $ThisScriptGUID) {
					$doNotUpdate = $false
				}
			} 
			else {
				$doNotUpdate = $false
			}
			if (-not $doNotUpdate) {
				Write-Log -DebugEntry ("[UPDATE-TARGETCONTACT] $ContactDN")
		
				$DisplayName = ($TargetElements[$TargetElementID]).DisplayName
				$Name = ($TargetElements[$TargetElementID]).Name
				$sn = ($TargetElements[$TargetElementID]).sn
				$GivenName = ($TargetElements[$TargetElementID]).GivenName
				$department = ($TargetElements[$TargetElementID]).department
				$title = ($TargetElements[$TargetElementID]).title
				$company = ($TargetElements[$TargetElementID]).company
				$physicalDeliveryOfficeName = ($TargetElements[$TargetElementID]).physicalDeliveryOfficeName
				$telephoneNumber = ($TargetElements[$TargetElementID]).telephoneNumber
				$facsimileTelephoneNumber = ($TargetElements[$TargetElementID]).facsimileTelephoneNumber
				$mobile = ($TargetElements[$TargetElementID]).mobile
				$l = ($TargetElements[$TargetElementID]).l
				$postalCode = ($TargetElements[$TargetElementID]).postalCode
				$streetAddress = ($TargetElements[$TargetElementID]).streetAddress
				$st = ($TargetElements[$TargetElementID]).st
				$co = ($TargetElements[$TargetElementID]).co
				$mail = ($TargetElements[$TargetElementID]).mail
				$mailNickname = ($TargetElements[$TargetElementID]).mailNickname
				
				$ChangeAttributes = @{"field" = "value"}
				$ClearAttributes = @()
				$renameObject = $false

				# name
				if ($Name -ne $TargetContactElements[$UpdateContactID].Name) { $renameObject = $true }
			
				# sn
				if ($sn -ne $null) { $ChangeAttributes.Add('sn' , "$sn") }
				else { $ClearAttributes += "sn"  }
				# GivenName
				if ($GivenName -ne $null) { $ChangeAttributes.Add('GivenName' , "$GivenName") }
				else { $ClearAttributes += "GivenName"  }
				# department
				if ($department -ne $null) { $ChangeAttributes.Add('department' , "$department") }
				else { $ClearAttributes += "department"  }
				# title
				if ($title -ne $null) { $ChangeAttributes.Add('title' , "$title") }
				else { $ClearAttributes += "title"  }
				# company
				if ($company -ne $null) { $ChangeAttributes.Add('company' , "$company") }
				else { $ClearAttributes += "company"  }
				# physicalDeliveryOfficeName
				if ($physicalDeliveryOfficeName -ne $null) { $ChangeAttributes.Add('physicalDeliveryOfficeName' , "$physicalDeliveryOfficeName") }
				else { $ClearAttributes += "physicalDeliveryOfficeName"  }
				# telephoneNumber
				if ($telephoneNumber -ne $null) { $ChangeAttributes.Add('telephoneNumber' , "$telephoneNumber") }
				else { $ClearAttributes += "telephoneNumber"  }
				# facsimileTelephoneNumber
				if ($facsimileTelephoneNumber -ne $null) { $ChangeAttributes.Add('facsimileTelephoneNumber' , "$facsimileTelephoneNumber") }
				else { $ClearAttributes += "facsimileTelephoneNumber"  }
				# mobile
				if ($mobile -ne $null) { $ChangeAttributes.Add('mobile' , "$mobile") }
				else { $ClearAttributes += "mobile"  }
				# l
				if ($l -ne $null) { $ChangeAttributes.Add('l' , "$l") }
				else { $ClearAttributes += "l"  }
				# postalCode
				if ($postalCode -ne $null) { $ChangeAttributes.Add('postalCode' , "$postalCode") }
				else { $ClearAttributes += "postalCode"  }
				# streetAddress
				if ($streetAddress -ne $null) { $ChangeAttributes.Add('streetAddress' , "$streetAddress") }
				else { $ClearAttributes += "streetAddress"  }
				# st
				if ($st -ne $null) { $ChangeAttributes.Add('st' , "$st") }
				else { $ClearAttributes += "st"  }
				# co
				if ($co -ne $null) { $ChangeAttributes.Add('co' , "$co") }
				else { $ClearAttributes += "co"  }
				# mail
				if ($mail -ne $null) { $ChangeAttributes.Add('mail' , "$mail"); $OtherAttributes.Add('targetAddress', $mail) }
				else { $ClearAttributes += "mail"  }
				# mailNickname
				if ($mailNickname -ne $null) { $ChangeAttributes.Add('mailNickname' , "$mailNickname") }
				else { $ClearAttributes += "mailNickname"  }
				$ChangeAttributes.Remove('field') 

				#
				if ($ChangeAttributes.Count -gt 0) {
					Write-Log -Entry "[UPDATE-SOURCECONTACT]REPLACEATTRIBUTE $ContactDN"
					Set-ADObject -Identity "$ContactDN" -Server $SourceDC -DisplayName $DisplayName -Replace $ChangeAttributes -Confirm:$false
				}
				if ($ClearAttributes.Count -gt 0) {
					Write-Log -Entry "[UPDATE-SOURCECONTACT]CLEARATTRIBUTE $ContactDN"
					Set-ADObject -Identity "$ContactDN" -Server $SourceDC -DisplayName $DisplayName -Clear $ClearAttributes -Confirm:$false
				}
				if ($renameObject) {
					Write-Log -Entry "[UPDATE-SOURCECONTACT]CLEARATTRIBUTE $ContactDN"
					Rename-ADObject -Identity "$ContactDN" -NewName $Name -Server $SourceDC -Credential $TargetCredential
				}
			}
		}	
	}
	else {
		Write-Log -DebugEntry ("[UPDATE-SOURCECONTACT] no contact to update")
	}
}


#------------------------------------------------------------------------------------------------
# create elements (Your partners users as your contacts)
#------------------------------------------------------------------------------------------------
if (-not $DoNotSearchWithinTarget) {
	$NewSourceElements = @()
	$NewSourceElements = $TargetElements | Where-Object {$_.SHStatus -eq "userNotFound"} | Select-Object -ExpandProperty SHElementID
	if ($NewSourceElements) {
		foreach ($SourceElementID in $NewElements) {
			$NewMailContactWarning = $null
			$SetMailContactWarning = $null
			$SetContactWarning = $null
			Write-Log -DebugEntry ("[CREATE-SOURCECONTACT] "+($TargetElements[$TargetElementID]).DisplayName)
		
			$DisplayName = ($TargetElements[$TargetElementID]).DisplayName
			$Name = ($TargetElements[$TargetElementID]).Name
			$sn = ($TargetElements[$TargetElementID]).sn
			$GivenName = ($TargetElements[$TargetElementID]).GivenName
			$department = ($TargetElements[$TargetElementID]).department
			$title = ($TargetElements[$TargetElementID]).title
			$company = ($TargetElements[$TargetElementID]).company
			$physicalDeliveryOfficeName = ($TargetElements[$TargetElementID]).physicalDeliveryOfficeName
			$telephoneNumber = ($TargetElements[$TargetElementID]).telephoneNumber
			$facsimileTelephoneNumber = ($TargetElements[$TargetElementID]).facsimileTelephoneNumber
			$mobile = ($TargetElements[$TargetElementID]).mobile
			$l = ($TargetElements[$TargetElementID]).l
			$postalCode = ($TargetElements[$TargetElementID]).postalCode
			$streetAddress = ($TargetElements[$TargetElementID]).streetAddress
			$st = ($TargetElements[$TargetElementID]).st
			$co = ($TargetElements[$TargetElementID]).co
			$mail = ($TargetElements[$TargetElementID]).mail
			$legacyExchangeDN = ($SourceElements[$SourceElementID]).legacyExchangeDN
			$mailNickname = ($TargetElements[$TargetElementID]).mailNickname
			$extensionAttribute15=($TargetElements[$TargetElementID]).SHHashedGUID
			
			$proxyaddresses=@()
			$proxyaddresses += ("SMTP:$mail")
			$proxyaddresses += e("X500:$legacyExchangeDN")

			$OtherAttributes = @{"field" = "value"}
			
			# sn
			if ($sn -ne $null) { $OtherAttributes.Add('sn' , "$sn") }
			# GivenName
			if ($GivenName -ne $null) { $OtherAttributes.Add('GivenName' , "$GivenName") }
			# department
			if ($department -ne $null) { $OtherAttributes.Add('department' , "$department") }
			# title
			if ($title -ne $null) { $OtherAttributes.Add('title' , "$title") }
			# company
			if ($company -ne $null) { $OtherAttributes.Add('company' , "$company") }
			# physicalDeliveryOfficeName
			if ($physicalDeliveryOfficeName -ne $null) { $OtherAttributes.Add('physicalDeliveryOfficeName' , "$physicalDeliveryOfficeName") }
			# telephoneNumber
			if ($telephoneNumber -ne $null) { $OtherAttributes.Add('telephoneNumber' , "$telephoneNumber") }
			# facsimileTelephoneNumber
			if ($facsimileTelephoneNumber -ne $null) { $OtherAttributes.Add('facsimileTelephoneNumber' , "$facsimileTelephoneNumber") }
			# mobile
			if ($mobile -ne $null) { $OtherAttributes.Add('mobile' , "$mobile") }
			# l
			if ($l -ne $null) { $OtherAttributes.Add('l' , "$l") }
			# postalCode
			if ($postalCode -ne $null) { $OtherAttributes.Add('postalCode' , "$postalCode") }
			# streetAddress
			if ($streetAddress -ne $null) { $OtherAttributes.Add('streetAddress' , "$streetAddress") }
			# st
			if ($st -ne $null) { $OtherAttributes.Add('st' , "$st") }
			# co
			if ($co -ne $null) { $OtherAttributes.Add('co' , "$co") }
			# mail
			if ($mail -ne $null) { $OtherAttributes.Add('mail' , "$mail"); $OtherAttributes.Add('proxyaddresses', $proxyaddresses); $OtherAttributes.Add('targetAddress', $mail) }
			# mailNickname
			if ($mailNickname -ne $null) { $OtherAttributes.Add('mailNickname' , "$mailNickname") }
			# User GUID
			$OtherAttributes.Add('extensionAttribute15', "$extensionAttribute15")
			# Script GUID
			$OtherAttributes.Add('extensionAttribute14', "$ThisScriptGUID")
		
			$OtherAttributes.Remove('field') 

			$null = New-ADObject -server $SourceDC -Name "$Name" -path "$SourceContactOU" -Type contact -OtherAttributes $OtherAttributes -confirm:$false
			
		
		
			#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



#------------------------------------------------------------------------------------------------
# delete elements  (Your Users as your Partners Contacts)
#------------------------------------------------------------------------------------------------
$DeleteTargetContacts = @()
$DeleteTargetContacts = $SourceContactElements | Where-Object {$_.SHStatus -eq "userRemoved"} | Select-Object -Property DistinguishedName, extensionAttribute14
if ($DeleteTargetContacts) {
	foreach ($DeleteContact in $DeleteTargetContacts) {
		$doNotDelete = $true
		#Switch update only contacts created by script...
		if ($UpdateDeleteContactsOnlyCreatedByThisScript) {
			if ($DeleteContact.extensionAttribute14 -eq $ThisScriptGUID) {
				$doNotDelete = $false
			}
		} 
		else {
			$doNotDelete = $false
		}
		if (-not $doNotDelete) {
			$DeleteContactDN = $DeleteContact.DistinguishedName
			Write-Log -DebugEntry ("[DELETE-TARGETCONTACT] $DeleteContactDN")
			Remove-ADObject -Identity "$DeleteContactDN" -Confirm:$false -Server $TargetDC -Credential $TargetCredential
		}
	}
}
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
		$extensionAttribute14 = $SourceContactElements[$UpdateContactID].extensionAttribute14
		$doNotUpdate = $true
		#Switch update only contacts created by script...
		if ($UpdateDeleteContactsOnlyCreatedByThisScript) {
			if ($extensionAttribute14 -eq $ThisScriptGUID) {
				$doNotUpdate = $false
			}
		} 
		else {
			$doNotUpdate = $false
		}
		if (-not $doNotUpdate) {
			Write-Log -DebugEntry ("[UPDATE-TARGETCONTACT] $ContactDN")
		
			$DisplayName = ($SourceElements[$SourceElementID]).DisplayName
			$Name = ($SourceElements[$SourceElementID]).Name
			$sn = ($SourceElements[$SourceElementID]).sn
			$GivenName = ($SourceElements[$SourceElementID]).GivenName
			$department = ($SourceElements[$SourceElementID]).department
			$title = ($SourceElements[$SourceElementID]).title
			$company = ($SourceElements[$SourceElementID]).company
			$physicalDeliveryOfficeName = ($SourceElements[$SourceElementID]).physicalDeliveryOfficeName
			$telephoneNumber = ($SourceElements[$SourceElementID]).telephoneNumber
			$facsimileTelephoneNumber = ($SourceElements[$SourceElementID]).facsimileTelephoneNumber
			$mobile = ($SourceElements[$SourceElementID]).mobile
			$l = ($SourceElements[$SourceElementID]).l
			$postalCode = ($SourceElements[$SourceElementID]).postalCode
			$streetAddress = ($SourceElements[$SourceElementID]).streetAddress
			$st = ($SourceElements[$SourceElementID]).st
			$co = ($SourceElements[$SourceElementID]).co
			$mail = ($SourceElements[$SourceElementID]).mail
			$mailNickname = ($SourceElements[$SourceElementID]).mailNickname

			$ChangeAttributes = @{"field" = "value"}
			$ClearAttributes = @()
			$renameObject = $false

			# name
			if ($Name -ne $SourceContactElements[$UpdateContactID].Name) { $renameObject = $true }
			
			# sn
			if ($sn -ne $null) { $ChangeAttributes.Add('sn' , "$sn") }
			else { $ClearAttributes += "sn"  }
			# GivenName
			if ($GivenName -ne $null) { $ChangeAttributes.Add('GivenName' , "$GivenName") }
			else { $ClearAttributes += "GivenName"  }
			# department
			if ($department -ne $null) { $ChangeAttributes.Add('department' , "$department") }
			else { $ClearAttributes += "department"  }
			# title
			if ($title -ne $null) { $ChangeAttributes.Add('title' , "$title") }
			else { $ClearAttributes += "title"  }
			# company
			if ($company -ne $null) { $ChangeAttributes.Add('company' , "$company") }
			else { $ClearAttributes += "company"  }
			# physicalDeliveryOfficeName
			if ($physicalDeliveryOfficeName -ne $null) { $ChangeAttributes.Add('physicalDeliveryOfficeName' , "$physicalDeliveryOfficeName") }
			else { $ClearAttributes += "physicalDeliveryOfficeName"  }
			# telephoneNumber
			if ($telephoneNumber -ne $null) { $ChangeAttributes.Add('telephoneNumber' , "$telephoneNumber") }
			else { $ClearAttributes += "telephoneNumber"  }
			# facsimileTelephoneNumber
			if ($facsimileTelephoneNumber -ne $null) { $ChangeAttributes.Add('facsimileTelephoneNumber' , "$facsimileTelephoneNumber") }
			else { $ClearAttributes += "facsimileTelephoneNumber"  }
			# mobile
			if ($mobile -ne $null) { $ChangeAttributes.Add('mobile' , "$mobile") }
			else { $ClearAttributes += "mobile"  }
			# l
			if ($l -ne $null) { $ChangeAttributes.Add('l' , "$l") }
			else { $ClearAttributes += "l"  }
			# postalCode
			if ($postalCode -ne $null) { $ChangeAttributes.Add('postalCode' , "$postalCode") }
			else { $ClearAttributes += "postalCode"  }
			# streetAddress
			if ($streetAddress -ne $null) { $ChangeAttributes.Add('streetAddress' , "$streetAddress") }
			else { $ClearAttributes += "streetAddress"  }
			# st
			if ($st -ne $null) { $ChangeAttributes.Add('st' , "$st") }
			else { $ClearAttributes += "st"  }
			# co
			if ($co -ne $null) { $ChangeAttributes.Add('co' , "$co") }
			else { $ClearAttributes += "co"  }
			# mail
			if ($mail -ne $null) { $ChangeAttributes.Add('mail' , "$mail"); $OtherAttributes.Add('targetAddress', $mail) }
			else { $ClearAttributes += "mail"  }
			# mailNickname
			if ($mailNickname -ne $null) { $ChangeAttributes.Add('mailNickname' , "$mailNickname") }
			else { $ClearAttributes += "mailNickname"  }
			$ChangeAttributes.Remove('field') 
			
			#
			if ($ChangeAttributes.Count -gt 0) {
				Write-Log -Entry "[UPDATE-TARGETCONTACT]REPLACEATTRIBUTE $ContactDN"
				Set-ADObject -Identity "$ContactDN" -Server $TargetDC -Credential $TargetCredential -DisplayName $DisplayName -Replace $ChangeAttributes -Confirm:$false
			}
			if ($ClearAttributes.Count -gt 0) {
				Write-Log -Entry "[UPDATE-TARGETCONTACT]CLEARATTRIBUTE $ContactDN"
				Set-ADObject -Identity "$ContactDN" -Server $TargetDC -Credential $TargetCredential -DisplayName $DisplayName -Clear $ClearAttributes -Confirm:$false
			}
			if ($renameObject) {
				Write-Log -Entry "[UPDATE-TARGETCONTACT]RENAME $ContactDN"
				Rename-ADObject -Identity "$ContactDN" -NewName $Name -Server $TargetDC -Credential $TargetCredential
			}
		}
	}
}
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)
		
		$DisplayName = ($SourceElements[$SourceElementID]).DisplayName
		$Name = ($SourceElements[$SourceElementID]).Name
		$sn = ($SourceElements[$SourceElementID]).sn
		$GivenName = ($SourceElements[$SourceElementID]).GivenName
		$department = ($SourceElements[$SourceElementID]).department
		$title = ($SourceElements[$SourceElementID]).title
		$company = ($SourceElements[$SourceElementID]).company
		$physicalDeliveryOfficeName = ($SourceElements[$SourceElementID]).physicalDeliveryOfficeName
		$telephoneNumber = ($SourceElements[$SourceElementID]).telephoneNumber
		$facsimileTelephoneNumber = ($SourceElements[$SourceElementID]).facsimileTelephoneNumber
		$mobile = ($SourceElements[$SourceElementID]).mobile
		$l = ($SourceElements[$SourceElementID]).l
		$postalCode = ($SourceElements[$SourceElementID]).postalCode
		$streetAddress = ($SourceElements[$SourceElementID]).streetAddress
		$st = ($SourceElements[$SourceElementID]).st
		$co = ($SourceElements[$SourceElementID]).co
		$mail = ($SourceElements[$SourceElementID]).mail
		$legacyExchangeDN = ($SourceElements[$SourceElementID]).legacyExchangeDN
		$mailNickname = ($SourceElements[$SourceElementID]).mailNickname
		$extensionAttribute15=($SourceElements[$SourceElementID]).SHHashedGUID
		
		$proxyaddresses=@()
		$proxyaddresses += ("SMTP:$mail")
		$proxyaddresses += ("X500:$legacyExchangeDN")

		$OtherAttributes = @{"field" = "value"}
		
		# DsiplayName
		if ($DisplayName -ne $null) { $OtherAttributes.Add('DisplayName' , "$DisplayName") }
		# sn
		if ($sn -ne $null) { $OtherAttributes.Add('sn' , "$sn") }
		# GivenName
		if ($GivenName -ne $null) { $OtherAttributes.Add('GivenName' , "$GivenName") }
		# department
		if ($department -ne $null) { $OtherAttributes.Add('department' , "$department") }
		# title
		if ($title -ne $null) { $OtherAttributes.Add('title' , "$title") }
		# company
		if ($company -ne $null) { $OtherAttributes.Add('company' , "$company") }
		# physicalDeliveryOfficeName
		if ($physicalDeliveryOfficeName -ne $null) { $OtherAttributes.Add('physicalDeliveryOfficeName' , "$physicalDeliveryOfficeName") }
		# telephoneNumber
		if ($telephoneNumber -ne $null) { $OtherAttributes.Add('telephoneNumber' , "$telephoneNumber") }
		# facsimileTelephoneNumber
		if ($facsimileTelephoneNumber -ne $null) { $OtherAttributes.Add('facsimileTelephoneNumber' , "$facsimileTelephoneNumber") }
		# mobile
		if ($mobile -ne $null) { $OtherAttributes.Add('mobile' , "$mobile") }
		# l
		if ($l -ne $null) { $OtherAttributes.Add('l' , "$l") }
		# postalCode
		if ($postalCode -ne $null) { $OtherAttributes.Add('postalCode' , "$postalCode") }
		# streetAddress
		if ($streetAddress -ne $null) { $OtherAttributes.Add('streetAddress' , "$streetAddress") }
		# st
		if ($st -ne $null) { $OtherAttributes.Add('st' , "$st") }
		# co
		if ($co -ne $null) { $OtherAttributes.Add('co' , "$co") }
		# mail
		if ($mail -ne $null) { $OtherAttributes.Add('mail' , "$mail"); $OtherAttributes.Add('proxyaddresses', $proxyaddresses); $OtherAttributes.Add('targetAddress', $mail) }
		# mailNickname
		if ($mailNickname -ne $null) { $OtherAttributes.Add('mailNickname' , "$mailNickname") }
		# User GUID
		$OtherAttributes.Add('extensionAttribute15', "$extensionAttribute15")
		# Script GUID
		$OtherAttributes.Add('extensionAttribute14', "$ThisScriptGUID")
		
		$OtherAttributes.Remove('field') 
		Write-Log -Entry "[CREATE-TARGETCONTACT] $Name in OU: $TargetContactOU"
		$null = New-ADObject -server $TargetDC -Credential $TargetCredential -Name "$Name" -path "$TargetContactOU" -Type contact -OtherAttributes $OtherAttributes -confirm:$false
			
		
		
		#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)
##------------------------------------------------------------------------------------------------
$SourceElements = $null
$SourceContactElements  = $null
if (-not $DoNotSearchWithinTarget) {
	$TargetElements = $null
	$TargetContactElements  = $null
}
Write-Log -Entry "Script execution is finished!"

	

>> syntax highlighting powered by highlight.js