Two-way Password Synchronization from one Active Directory Domain to another using DSInternals

Shaun VermaakCOG Lead Engineer
CERTIFIED EXPERT
My name is Shaun Vermaak and I have always been fascinated with technology and how we use it to enhance our lives and business.
Published:
Updated:
Edited by: Andrew Leniart
This article shows a process of synchronizing password from on Active Directory domain to another, even if in another forest

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://www.experts-exchange.com/articles/29569/How-to-extract-hashes-from-IFM-backup.html


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.


Please see the comments inside the script for an explanation of each step. In short, it gets hashes from the source domain and based on a group, sets it on the appropriate users.


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

# Create your credentials with these commands
# $credential = Get-Credential;
# $credential | Export-CliXml -Path 'C:\Temp\cred.xml';

# 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)
{
# Fix provided by Vikas Bhat, see article comments
$domain1User                        = $Null
$domain2User                        = $Null

# Get user object in both domain 1 and 2
$domain1User = Get-ADUser -Identity $user.SamAccountName -Properties "pwdLastSet" -Server $domain1FQDN -Credential $domain1Credential;
$domain2User = Get-ADUser -Identity $user.SamAccountName -Properties "pwdLastSet" -Server $domain2FQDN -Credential $domain2Credential;

# Only continue if both users exists
if ($Null -ne $domain1User -and $Null -ne $domain2User)
{
# Get the current user's hashes in both domain 1 and 2
$currentDomain1UserHash = $domain1Hashes | Where-Object {$_.saMAccountName -eq $user.SamAccountName};
$currentDomain2UserHash = $domain2Hashes | Where-Object {$_.saMAccountName -eq $user.SamAccountName};

# Get the current user's NT Hash in both domain 1 and 2
$currentDomain1UserNTHash = ([System.BitConverter]::ToString($currentDomain1UserHash.NTHash) -replace '-','').ToLower();
$currentDomain2UserNTHash = ([System.BitConverter]::ToString($currentDomain2UserHash.NTHash) -replace '-','').ToLower();

# Check if hashes are different AKA the account password is out-of-sync
if ($currentDomain1UserNTHash -ne $currentDomain2UserNTHash)
{
# Get user object in both domain 1 and 2
$domain1User = Get-ADUser -Identity $user.SamAccountName -Properties "pwdLastSet" -Server $domain1FQDN -Credential $domain1Credential;
$domain2User = Get-ADUser -Identity $user.SamAccountName -Properties "pwdLastSet" -Server $domain2FQDN -Credential $domain2Credential;

# Domain 1 password is more recent
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;
}
# Domain 2 password is more recent
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.

 

Note: If you need further "Support" about this topic, please consider using the Ask a Question feature of Experts Exchange. I monitor questions asked and would be pleased to provide any additional support required in questions asked in this manner, along with other EE experts...  

 

Please do not forget to press the "Thumbs Up" button if you think this article was helpful and valuable for EE members.


It also provides me with positive feedback. Thank you!



7
6,934 Views
Shaun VermaakCOG Lead Engineer
CERTIFIED EXPERT
My name is Shaun Vermaak and I have always been fascinated with technology and how we use it to enhance our lives and business.

Comments (13)

Vikas BhatExperienced IT Infrastructure Services/operations Manager
CERTIFIED EXPERT

Commented:
I get Does not Exists
Vikas BhatExperienced IT Infrastructure Services/operations Manager
CERTIFIED EXPERT

Commented:
and Exists for a valid user
Shaun VermaakCOG Lead Engineer
CERTIFIED EXPERT
Awarded 2017
Distinguished Expert 2019

Author

Commented:
Give this one a try. You can test by commenting out the two Set-SamAccountPasswordHash lines
Install-Module -Name DSInternals -Confirm:$false -Force

# Create your credentials with these commands
# $credential = Get-Credential;
# $credential | Export-CliXml -Path 'C:\Temp\cred.xml';

# 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)
{
    # Get user object in both domain 1 and 2
    $domain1User = Get-ADUser -Identity $user.SamAccountName -Properties "pwdLastSet" -Server $domain1FQDN -Credential $domain1Credential;
    $domain2User = Get-ADUser -Identity $user.SamAccountName -Properties "pwdLastSet" -Server $domain2FQDN -Credential $domain2Credential;

    # Only continue if both users exists
    if ($Null -ne $domain1User -and $Null -ne $domain2User)
    {
        # Get the current user's hashes in both domain 1 and 2
        $currentDomain1UserHash = $domain1Hashes | Where-Object {$_.saMAccountName -eq $user.SamAccountName};
        $currentDomain2UserHash = $domain2Hashes | Where-Object {$_.saMAccountName -eq $user.SamAccountName};

        # Get the current user's NT Hash in both domain 1 and 2
        $currentDomain1UserNTHash = ([System.BitConverter]::ToString($currentDomain1UserHash.NTHash) -replace '-','').ToLower();
        $currentDomain2UserNTHash = ([System.BitConverter]::ToString($currentDomain2UserHash.NTHash) -replace '-','').ToLower();

        # Check if hashes are different AKA the account password is out-of-sync
        if ($currentDomain1UserNTHash -ne $currentDomain2UserNTHash)
        {
            # Get user object in both domain 1 and 2
            $domain1User = Get-ADUser -Identity $user.SamAccountName -Properties "pwdLastSet" -Server $domain1FQDN -Credential $domain1Credential;
            $domain2User = Get-ADUser -Identity $user.SamAccountName -Properties "pwdLastSet" -Server $domain2FQDN -Credential $domain2Credential;

            # Domain 1 password is more recent
            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;
            }
            # Domain 2 password is more recent
            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";
        }
    }
}

Open in new window

Vikas BhatExperienced IT Infrastructure Services/operations Manager
CERTIFIED EXPERT

Commented:
Thankyou so much. I will give this a try. Meanwhile I also made sure that the users exists on both side.
Vikas BhatExperienced IT Infrastructure Services/operations Manager
CERTIFIED EXPERT

Commented:
Hello Shaun. Thankyou for all your help. After testing I finally found that the script was working properly and also handling the
if ($Null -ne $domain1User -and $Null -ne $domain2User) but it didn't clear the old values of $domain1User and $domain2User in the for loop.

I have now corrected it by adding below in the for loop.
    $domain1User = $Null
    $domain2User = $Null

Below is the updated script that works properly now even when users are not present on both sides.

Install-Module -Name DSInternals -Confirm:$false -Force

# Create your credentials with these commands
# $credential = Get-Credential;
# $credential | Export-CliXml -Path 'C:\Temp\cred.xml';

# 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
    # Get user object in both domain 1 and 2
    $domain1User = Get-ADUser -Identity $user.SamAccountName -Properties "pwdLastSet" -Server $domain1FQDN -Credential $domain1Credential;
    $domain2User = Get-ADUser -Identity $user.SamAccountName -Properties "pwdLastSet" -Server $domain2FQDN -Credential $domain2Credential;

    # Only continue if both users exists
    if ($Null -ne $domain1User -and $Null -ne $domain2User)
    {
        # Get the current user's hashes in both domain 1 and 2
        $currentDomain1UserHash = $domain1Hashes | Where-Object {$_.saMAccountName -eq $user.SamAccountName};
        $currentDomain2UserHash = $domain2Hashes | Where-Object {$_.saMAccountName -eq $user.SamAccountName};

        # Get the current user's NT Hash in both domain 1 and 2
        $currentDomain1UserNTHash = ([System.BitConverter]::ToString($currentDomain1UserHash.NTHash) -replace '-','').ToLower();
        $currentDomain2UserNTHash = ([System.BitConverter]::ToString($currentDomain2UserHash.NTHash) -replace '-','').ToLower();

        # Check if hashes are different AKA the account password is out-of-sync
        if ($currentDomain1UserNTHash -ne $currentDomain2UserNTHash)
        {
            # Get user object in both domain 1 and 2
            $domain1User = Get-ADUser -Identity $user.SamAccountName -Properties "pwdLastSet" -Server $domain1FQDN -Credential $domain1Credential;
            $domain2User = Get-ADUser -Identity $user.SamAccountName -Properties "pwdLastSet" -Server $domain2FQDN -Credential $domain2Credential;

            # Domain 1 password is more recent
            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;
            }
            # Domain 2 password is more recent
            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";
        }
    }
}

Open in new window

View More

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.