Find and Remove Old Computer Objects with PowerShell

Let’s face it. Active Directory can get messy real fast if people aren’t cleaning up after themselves. The rate at which computers are rebuilt and / or replaced can clutter up any domain if not properly maintained. I’ve put together the following script to help identify old computer objects based on a few criteria:

  1. PasswordLastSet
  2. LastLogonTimestamp
  3. Is the computer pingable?

The Script

For starters let’s clear our screen and import the AD module

Import-Module activedirectory

Now let’s define how old is “too old”. I’ve decided on a fairly generous number of 90 days, but you can do what makes sense for you. The filter defined below will only return computers that have both their PasswordLastSet and LastLogonTimestamp attributes as being older than 90 days. If only one of the criteria is met, it won’t be included in the list.

$old = (Get-Date).AddDays(-90)
$filter = '(PasswordLastSet -le $old) -and (LastLogonTimestamp -le $old)'

I think it’s probably smart to specifically target an OU. This will look in all the nested OUs as well, so keep that in mind.

$ou = "ou=workstations,dc=company,dc=com"

Now using the variables defined above, let’s query AD for our computer objects and store them in an array. I only want to return certain properties and you can see them below. I also would like to sort the list by the computer name, but you can do whatever you like obviously.

$ComputerList = @(Get-ADComputer -filter $filter -searchbase $ou -properties Name, Enabled, PasswordLastSet, lastlogondate | Sort-Object -Property Name)

Sometimes you may only want to return a list of computers that are already disabled in AD. If that’s the case, you can re-write the $ComputerList variable as follows

$ComputerList = @(Get-ADComputer -filter $filter -searchbase $ou -properties Name, Enabled, PasswordLastSet, lastlogondate | where-object {$_.enabled -ne $true} | Sort-Object -Property Name)

We now have our computer objects, but I still want to do one more test before we can call these objects officially stale. Let’s see if we can ping any of the computers in our list. What this will do is try pinging every computer in our array. If they don’t ping, the Write-Output portion will store this computer in the $NoPing variable. Also, I went ahead and set it up so there will be a progress bar while it’s running.

$count = 0
$NoPing = @(foreach($computer in $ComputerList){
Write-Progress -Activity "Pinging $computer" -PercentComplete (($count / $ComputerList.Count)*100)
$DoesItPing = Test-Connection -CN $ -Count 1 -Buffersize 16 -quiet
IF($DoesItPing -match $false) {$computer | Write-Output}
$count ++

Last but not least, let’s output our $NoPing array to the screen in a nice table with only the information we care about. And let’s also throw in a total number of computers at the end.

$NoPing | Select Name, Enabled, PasswordLastSet, lastlogondate | ft -AutoSize -Property name,enabled,@{n="PasswordLastSet";e={$_.PasswordLastSet};f="MM/dd/yyyy"},@{n="LastLogonDate";e={$_.LastLogonDate};f="MM/dd/yyyy"}
Write-Host "Total Computers" $NoPing.Count

While not completely foolproof, I would be pretty comfortable at this point removing the returned computers from active directory.

The Scary Part

If you are confident the computer objects contained in the $NoPing array can all be removed, you can remove them all with PowerShell. You will be prompted with an “Are you sure?” message when you run this. I would go ahead and verify that you have the AD Recycle Bin up and running before blindly running this command though. Run this at your own risk!

$NoPing | Remove-ADObject -Recursive

Leave a Reply

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