PowerShell: Active Directory Cleanup – Part 1 – Duplicate Computers

Introduction

Hello World, Scott Williamson Senior Premier Field Engineer here. As a PFE, I frequently work with customers who ask how to cleanup Active Directory of old objects and data. To assist them in automating cleanup I have written several PowerShell scripts, functions, and workflows that I want to share in this blog series.

Duplicate Computers Script

The first of these scripts is checking for and cleanup of old Duplicate Computers. Duplicate computers are rarely seen in the newer versions of Active Directory (AD) unless you are having replication issues between domain controllers. Do you have any Duplicate computers in AD? Many customers still have some and don’t know it.

# PowerShell to Report Duplicate Computers
cls
$CDate = Get-Date -format "yyyyMMdd" 
$ScriptPath = Split-Path $MyInvocation.MyCommand.Path -Parent
$ComputerPropsCustom = $("Enabled","Description","LastLogonDate","Modified","whenChanged","PasswordLastSet","OperatingSystem","OperatingSystemServicePack","IPv4Address")
$ComputerPropsSelect = $("Name","SamAccountName","Enabled","DistinguishedName",@{Name="CreatedBy";Expression={$(([ADSI]"LDAP://$($_.DistinguishedName)").psbase.ObjectSecurity.Owner)}},"LastLogonDate","Modified","whenChanged","PasswordLastSet","OperatingSystem","OperatingSystemServicePack","IPv4Address")
        
$DuplicateComputers = Get-ADComputer -Filter {SamAccountName -like "*DUPLICATE-*"} -Properties $ComputerPropsCustom | Select-Object $ComputerPropsSelect | Sort-Object Name
$DuplicateComputers | Export-Csv -Path "$ScriptPath\$($CDate)_DuplicateComputers.csv" -NoTypeInformation
$DuplicateComputers

So let me walk through this short script line by line.

  • Clear the screen with “cls”.
  • Set a variable $CDate with the current date formatted as yyyyMMdd. Example 20191213.
  • Set $ScriptPath to the location of the script we are running.
  • Set $ComputerPropsCustom to a list of custom properties we want to pull.
  • Set $ComputerPropsSelect to all the properties we want in the output in the order desired. Notice we also have a custom defined property CreatedBy which is doing an LDAP lookup on the object to find who created it.
  • Set $DuplicateComputers to the output of the Get-ADComputer cmdlet. We filter the SamAccountName for only computer objects with “DUPLICATE-” in the name. Notice where we use $ComputerPropsCustom and $ComputerPropsSelect. In addition we sort the output by Name.
  • Next we export the $DuplicateComputers to a csv file in the script directory named as as the date underscore and DuplicateComputers.
  • The final line just sends the $DuplicateComputers contents to the screen for us to view.

Notice that the script only pulls information and doesn’t do any actual cleanup yet. Best practice is to do all the gathering, verify several times that you are only getting the data expected, and then add in the code to do the cleanup. Below is the final code to add to the bottom of the script above to perform the computer object removal.

# Uncomment next line to remove duplicate computers with no operating system
$DuplicateComputers | ? {$_.OperatingSystem -eq $null} | % {Remove-ADComputer -Identity $($_.DistinguishedName)}

Let’s walk through the last couple of lines.

  • The # line is a comment. I normally # comment out the action code of a script while I’m writing and perfecting the code. Once 100% sure I’m only getting the data I expect I remove the # from the action code. Usually I’m still a little hesitant so I usually add -whatif to the end of the action code for one final let’s test.
  • We send or pipe $DuplicateComputers to a filter that only selects objects without an operating system then pipes that into the Remove-ADComputer cmdlet to do the cleanup.

Summary

So although this is a short script it has a hint of some advanced code such as:

  • Date Formatting
  • Determining Script location
  • Setting Properties Arrays
  • Custom Property
  • Cmdlet filtering
  • Object Sorting
  • Exporting to CSV
  • ? = Where
  • % = ForEach-Object

Stay tuned for the next part in this series.

Series Links: