Introduction
Password synchronization is crucial during co-exists of Active Directory domains. Syncronising password allows users to seamlessly log into the new environment. Password synchronization is a feature that most migration suites offer, usually at a pretty penny.
I have used DSInternals for various tasks when I have my security hat on, and one of those processes is detailed in my previous article.
How to extract hashes from IFM backup
https://ittelligence.blog/2017/05/16/extract-hashes-ifm-backup/
It is a fantastic Powershell library by Michael Grafnetter and should be part of your toolset if you do IT security for your profession.
My Approach
I have previously done this with ADMT to do the initial password migration and FIM 2010 R2 to keep passwords in sync.
Another way is to use DSInternals and extract the hash from the source domain then set it on the target domain.
Below is a Powershell script I have put together. Note that I have purposefully left out function/parameters/checking if the hash is current etc. to hopefully promote collaboration on the repo.
Install-Module -Name DSInternals
# Create your credentials with these commands
# $credential = Get-Credential;
# $credential | Export-CliXml -Path 'C:\Temp\cred.xml';
# Configure your Source Domain configuration
$sourceDomainNetBIOS = 'Domain1';
$sourceDomainFQDN = 'Domain1.com';
$sourceDomainDN = 'DC=Domain1,DC=com';
$sourceDomainCredential = Import-CliXml -Path 'C:\Temp\Domain1.xml';
# Configure your Target Domain configuration
$targetDomainNetBIOS = 'Domain2';
$targetDomainFQDN = 'Domain2.com';
$targetDomainDN = 'DC=Domain2,DC=com';
$targetDomainCredential = Import-CliXml -Path 'C:\Temp\Domain2.xml';
$syncGroup = 'Some Group';
# Get Source Domain hashes
$hashes = Get-ADReplAccount -All -NamingContext $sourceDomainDN -Server $sourceDomainFQDN -Credential $sourceDomainCredential;
# The group of users to sync passwords for
$users = Get-ADGroupMember $syncGroup -server $targetDomainFQDN -Credential $targetDomainCredential;
# Loop through these users
foreach ($user in $users)
{
# Get the hash of the user in the hashes collection
$currentUserHash = $hashes | ? {$_.saMAccountName -eq $user.SamAccountName};
# Convert hash to string
$NTHash = ([System.BitConverter]::ToString($currentUserHash.NTHash) -replace '-','').ToLower();
# Set target domain password to the source domain hash
Set-SamAccountPasswordHash -SamAccountName $user.SamAccountName -Domain $targetDomainNetBIOS -NTHash $NTHash -Server $targetDomainFQDN -Credential $targetDomainCredential;
}
You can also clone it if you prefer
git clone https://svermaak@bitbucket.org/snippets/svermaak/kezMGE/password-sync.git
If you prefer to do two-way password sync, use the following script
Install-Module -Name DSInternals -Confirm:$false -Force
# Configure Domain 1
$domain1NetBIOS = 'Domain1';
$domain1FQDN = 'Domain1.com';
$domain1DN = 'DC=Domain1,DC=com';
$domain1Credential = Import-CliXml -Path 'C:\Temp\Domain1.xml';
$domain1Hashes = Get-ADReplAccount -All -NamingContext $domain1DN -Server $domain1FQDN -Credential $domain1Credential;
# Configure Domain 2
$domain2NetBIOS = 'Domain2';
$domain2FQDN = 'Domain2.com';
$domain2DN = 'DC=Domain2,DC=com';
$domain2Credential = Import-CliXml -Path 'C:\Temp\Domain2.xml';
$domain2Hashes = Get-ADReplAccount -All -NamingContext $domain2DN -Server $domain2FQDN -Credential $domain2Credential;
# The group of users to sync passwords for
$syncGroup = 'SG-PasswordSync';
# Loop through these users
$users = Get-ADGroupMember $syncGroup -server $domain1FQDN -Credential $domain1Credential;
foreach ($user in $users)
{
$domain1User = $Null
$domain2User = $Null
$domain1User = Get-ADUser -Identity $user.SamAccountName -Properties "pwdLastSet" -Server $domain1FQDN -Credential $domain1Credential;
$domain2User = Get-ADUser -Identity $user.SamAccountName -Properties "pwdLastSet" -Server $domain2FQDN -Credential $domain2Credential;
if ($Null -ne $domain1User -and $Null -ne $domain2User)
{
$currentDomain1UserHash = $domain1Hashes | Where-Object {$_.saMAccountName -eq $user.SamAccountName};
$currentDomain2UserHash = $domain2Hashes | Where-Object {$_.saMAccountName -eq $user.SamAccountName};
$currentDomain1UserNTHash = ([System.BitConverter]::ToString($currentDomain1UserHash.NTHash) -replace '-','').ToLower();
$currentDomain2UserNTHash = ([System.BitConverter]::ToString($currentDomain2UserHash.NTHash) -replace '-','').ToLower();
if ($currentDomain1UserNTHash -ne $currentDomain2UserNTHash)
{
if ($domain1User.pwdLastSet -gt $domain2User.pwdLastSet)
{
Write-Host "Sync user '$($user.SamAccountName)' password from domain 1 to domain 2";
Set-SamAccountPasswordHash -SamAccountName $user.SamAccountName -Domain $domain2NetBIOS -NTHash $currentDomain1UserNTHash -Server $domain2FQDN -Credential $domain2Credential;
}
elseif ($domain2User.pwdLastSet -gt $domain1User.pwdLastSet)
{
Write-Host "Sync user '$($user.SamAccountName)' password from domain 2 to domain 1";
Set-SamAccountPasswordHash -SamAccountName $user.SamAccountName -Domain $domain1NetBIOS -NTHash $currentDomain2UserNTHash -Server $domain1FQDN -Credential $domain1Credential;
}
}
else
{
Write-Host "User '$($user.SamAccountName)' passwords are the same, no need to sync";
}
}
}
I hope you found this tutorial useful. You are encouraged to ask questions, report any bugs or make any other comments about it below.
