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:
- 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 Groupif(test-path $duplicateComputersReport){$duplicates|Export-Csv -Path $duplicateComputersReport -NoTypeInformation}else{write-host ($duplicates|ft|out-string).trim()} - 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
Sample Output
$lastLogonDaysExceeding = 30
$daysRange = (get-date).adddays(-$lastLogonDaysExceeding)
$duplicateComputers=$result|?{$_.lastlogondate -le $daysRange}
write-output $duplicateComputers|select SamAccountName,LastLogonDate,DistinguishedName|ftSamAccountName LastLogonDate DistinguishedName
Disable all duplicate computer accounts shown above
-------------- ------------- -----------------
$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$duplicateComputers|%{
a. Test disable computer account(s) in AD and logon to each machine to see if it still works
try{
Disable-ADAccount -Identity $_.SamAccountName
write-host "$($_.SamAccountName) $($_.DistinguishedName) disabled."
}catch{
write-warning $_
}
}
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 ADc. 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 $machineSidreturn $machineSid}getLocalhostSidfunction 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 Systernalspsgetsid.exe(Get-WmiObject -class Win32_UserAccount -computername $env:computername)[0]
Categories: