How to Fix Duplicate Computer Account Names Issue in Active Directory

Automatic cleanup:

# disableDuplicateComputers.ps1
# Version 0.0.1

$defaultPasswordPeriod=30
$disabledComputersReport='c:\disabledComputersReport.csv'
function disableDuplicateComputers{
    param(
        $lastLogonDaysExceeding=30,
        $disabledComputersReport='c:\disabledComputersReport.csv'
        )
    # Obtain duplicates within the domain
    function getDuplicateComputerNames{
        try{
            import-module activedirectory
            $computers=Get-ADComputer -Filter * -properties Name,LastLogonDate,Created,DistinguishedName,SamAccountName,SID|select Name,LastLogonDate,Created,DistinguishedName,SamAccountName,SID,Enabled
            $groups=$computers|Group-Object -Property {$_.name}|?{$_.Count -ge 2}
            write-host "$($groups.count) duplicating groups found." -ForegroundColor Green
            if($groups){
                $duplicates=$groups|Select-Object -ExpandProperty Group|sort name                
                write-host $($duplicates|select SamAccountName,LastLogonDate,DistinguishedName|ft|out-string)
                return $duplicates
            }else{
                return $null
            }
        }catch{
            write-warning $_
            return $null
        }
    }
    write-host "Discovering duplicates in $env:userdnsdomain..."
    $duplicateComputers=getDuplicateComputerNames
    if($duplicateComputers){
        # Selecting expired duplicates
        $daysRange = (get-date).adddays(-$lastLogonDaysExceeding)            
        $duplicateComputersPastXDays=$duplicateComputers|?{$_.lastlogondate -le $daysRange}
        if($duplicateComputersPastXDays){
            # Disabling dups
            $output=($duplicateComputersPastXDays|select SamAccountName,LastLogonDate,DistinguishedName|ft|out-string).trim()
            write-host "These are duplicate computer accounts that have not login past 30 days:`r`n$output" 
            $disabledComputers=@()
            $duplicateComputersPastXDays|%{
                try{
                    if($_.Enabled){
                        Disable-ADAccount -Identity $_.SamAccountName
                        write-host "$($_.SamAccountName) $($_.DistinguishedName) disabled."
                        $disabledComputers+=$_
                    }else{
                        write-host "$($_.SamAccountName) has already been disabled."
                    }
                }catch{
                    write-warning $_
                }
            }
            $parentFolder=split-path $disabledComputersReport -parent    
            if($disabledComputers){
                try{
                    if(!(test-path $parentFolder)){
                        new-item $parentFolder -Type Directory -Force
                    }
                    $disabledComputers|Export-Csv -Path $disabledComputersReport -NoTypeInformation
                    return $true
                }catch{
                    Write-Warning $_
                    return $false
                }
            }else{
                write-host "No computers were disabled during this run."
            }
        }else{
            write-host "Although there are duplicates, none has passed the $lastLogonDaysExceeding threshold. Thus, no computer accounts were disabled."
        }
    }else{
        write-host "There are currently no duplicates in $env:userdnsdomain"
        return $true
    }
}

disableDuplicateComputers $defaultPasswordPeriod $disabledComputersReport

Manual cleanup:

  1. Find duplicate computer names in Active Directory
    # Obtain duplicates within the domain
    function getDuplicateComputerNames{
    $computers=Get-ADComputer -Filter * -properties Name,LastLogonDate,Created,DistinguishedName,SamAccountName,SID|select Name,LastLogonDate,Created,DistinguishedName,SamAccountName,SID
    $groups=$computers|Group-Object -Property {$_.name}|?{$_.Count -ge 2}
    $duplicates=$groups|Select-Object -ExpandProperty Group|sort name
    write-host "$($groups.count) duplicating groups found."
    return $duplicates
    }
    $result=getDuplicateComputerNames

    # Write just the important info
    write-output $result|select SamAccountName,LastLogonDate,DistinguishedName|ft

    # Output all collected info
    write-output $result|ft

    # Optional: export out to CSV
    $duplicateComputersReport='c:\duplicateComputersReport.csv'
    $result|Export-Csv -Path $duplicateComputersReport -NoTypeInformation
    $searchBase='DC=kimconnect,DC=com'
    $duplicateComputersReport='c:\duplicateComputersReport.csv'
    $computers=Get-ADComputer -Filter * -SearchBase $searchbase -Properties Created|Select-Object -Property Name, Created,DistinguishedName,SamAccountName,SID
    $duplicates=$computers|Group-Object -Property {$_.name}|?{$_.Count -ge 2}|Select-Object -ExpandProperty Group
    if(test-path $duplicateComputersReport){
    $duplicates|Export-Csv -Path $duplicateComputersReport -NoTypeInformation
    }else{
    write-host ($duplicates|ft|out-string).trim()
    }
  2. Get the computer SID of each PC
    By default, the results collected above should assist Administrators in determining which computer account has not contacted its Domain Controller for a password change after the default period of 30 days. Those would most likely be ‘duplicate’ accounts. Moreover, the ‘SamAccountName’ value could also indicate that a computer is, in fact a duplicate. One should exercise best judgement when purging AD accounts. Here are some follow-through options:
    # Display duplicate computer accounts that have not login past 30 days
    $lastLogonDaysExceeding = 30
    $daysRange = (get-date).adddays(-$lastLogonDaysExceeding)
    $duplicateComputers=$result|?{$_.lastlogondate -le $daysRange}
    write-output $duplicateComputers|select SamAccountName,LastLogonDate,DistinguishedName|ft
    Sample Output
    SamAccountName LastLogonDate DistinguishedName
    -------------- ------------- -----------------
    $DUPLICATE-18400 3/26/2020 2:04:24 PM CN=testpc007,OU=TEST1,DC=kimconnect
    testpc007$ 10/29/2020 8:23:35 AM CN=testpc007,CN=TEST2,DC=kimconnect
    $DUPLICATE-19008 4/1/2020 12:45:02 PM CN=testpc008,OU=TEST1,DC=kimconnect
    testpc008$ 4/1/2020 12:45:02 PM CN=testpc008,OU=TEST2,DC=kimconnect
    $DUPLICATE-19009 4/1/2020 12:32:45 PM CN=testpc009,OU=TEST1,DC=kimconnect
    testpc009$ 4/1/2020 12:32:45 PM CN=testpc009,OU=TEST2,DC=kimconnect
    Disable all duplicate computer accounts shown above
    $duplicateComputers|%{
    try{
    Disable-ADAccount -Identity $_.SamAccountName
    write-host "$($_.SamAccountName) $($_.DistinguishedName) disabled."
    }catch{
    write-warning $_
    }
    }
    a. Test disable computer account(s) in AD and logon to each machine to see if it still works
    Expected result of mistakenly disabling a valid computer account:


    b. Unjoin each machine from AD > remove the remaining duplicate computer account > rejoin computer back to AD
    c. Get the current computer SID – This method doesn’t work because the SID in AD would not match one retrieved from each localhost
    function getLocalhostSid{
    $localhostSid = Get-WmiObject -Query "SELECT SID FROM Win32_UserAccount WHERE LocalAccount = 'True'" |Select-Object -First 1 -ExpandProperty SID
    $machineSid = ($p = $localhostSid -split "-")[0..($p.Length-2)]-join"-"
    #return New-Object System.Security.Principal.SecurityIdentifier -ArgumentList $machineSid
    return $machineSid
    }
    getLocalhostSid
    function getSid
    {
    $localAdmin = new-object System.Security.Principal.NTAccount('Administrator')
    $rawSid=$localAdmin.Translate([System.Security.Principal.SecurityIdentifier] ).toString()
    return $rawSid.Substring(0,$rawSid.Length-4)
    # return ((Get-LocalUser | Select-Object -First 1).SID).AccountDomainSID.ToString()
    }
    getSid
    # Use Systernals
    psgetsid.exe
    (Get-WmiObject -class Win32_UserAccount -computername $env:computername)[0]

Leave a Reply

Your email address will not be published. Required fields are marked *