My Guidance on Identifying Stale Computers Objects in Active Directory using Powershell


This is a very common discussion, and a simple search using your favorite search engine provides multiple results from both the community and my Microsoft Peers.  This is my take on the topic and the guidance I usually provide.

These are common questions I get:

What AD attribute should be used pwdlastset or Lastlogontimestamp to determine if a computer object is stale?   I like this guidance “One-Liner: My Take On Finding Stale User and Computer Accounts”. Ian recommends using both attributes as a way to determine when an object is stale.   Since it is using the get-adcomputer powershell cmdlet, I am going to add to that recommendation and include the ipv4address attribute equals null, along with looking for cluster related spn entries (Cluster and Stale Computer Accounts).   Create a report and review the results to see if this guidance works.

Create a Report

This script example is to identify the impact based on the criteria.  Run the PowerShell cmdlets below and review the findings.


   1: $d = [DateTime]::Today.AddDays(-90)
   2: $default_log = $env:userprofile + '\Documents\stale_computer_report.csv'
   4: Foreach($domain in (get-adforest).domains){
   5: Get-ADComputer  -Filter {(isCriticalSystemObject -eq $False)} -Properties UserAccountControl,`
   6:  PwdLastSet,WhenChanged,SamAccountName,LastLogonTimeStamp,Enabled,admincount,IPv4Address,`
   7:  operatingsystem,operatingsystemversion,serviceprincipalname  -server $domain |
   8: select @{name='Domain';expression={$domain}}, `
   9: SamAccountName,operatingsystem,operatingsystemversion,UserAccountControl,Enabled, `
  10: admincount,IPv4Address, `
  11: @{Name="Stale";Expression={if((($_.pwdLastSet -lt $d.ToFileTimeUTC()) -and ($_.pwdLastSet -ne 0)`
  12:  -and ($_.LastLogonTimeStamp -lt $d.ToFileTimeUTC()) -and ($_.LastLogonTimeStamp -ne 0)`
  13:   -and ($_.admincount -ne 1) -and ($_.IPv4Address -eq $null)) -and `
  14:   (!($_.serviceprincipalname -like "*MSClusterVirtualServer*"))){$True}else{$False}}}, `
  15: @{Name="ParentOU";Expression={$_.distinguishedname.Substring($_.samaccountname.Length + 3)}} `
  16: | export-csv $default_log -append -NoTypeInformation}

Reviewing the Results

Open stale_computer_report.csv in Excel, look over the results.  Check to make sure the objects with ip addresses aren't showing as True in the Stale column.


Following this blog theme, by looking at the data with charts and tables creates an easier way to review and tell a story about the data,

To do this in excel, select insert, pivot chart, and then pivot chart again.


Select OK


Select Chart 1


View Stale Information By Domain

In the PivotChart Fields drag the fields to match the following


This will produce a nice graph grouped by domain that shows the number of computers that are / not stale.


View Stale Data Grouped by Parent OU

Drag the fields to match the following




View Stale Data Grouped by Operating System

Now that you have the new data you can also chart/graph it by operating system. Drag the fields to match the following




Clean Up

Does the criteria work based on the data you have collected? If there are linux or mac osx devices, do you feel comfortable deleting those objects? Since I do not touch on non windows operating systems, I added a filter in the script below to only include windows computer objects.

This is what it will look like:

   1: $d = [DateTime]::Today.AddDays(-90)
   2: Foreach($domain in (get-adforest).domains){
   3:     Get-ADComputer -Filter {((pwdLastSet -lt $d) -and (LastLogonTimeStamp -lt $d) -and (OperatingSystem -like "Windows*") -and (Enabled -eq $True) -and (isCriticalSystemObject -ne $True))} -Properties IPv4Address, PwdLastSet, LastLogonTimeStamp, servicePrincipalName, operatingsystem -server $domain  | where {(($_.IPv4Address -eq $null)) -and (!($_.serviceprincipalname -like "*MSClusterVirtualServer*"))}
   4: }

Another common question:

When an object is stale should it be disabled, moved to another ou, or deleted right away? My thought, disable the object so that you don’t have to deal with objects getting moved and having to deal with another process to undo moves in the event the object is no longer stale.   !!!Please test this before implementing it, consider using the –whatif after the disable-adaccount cmdlet!!!!!

This script will find the stale objects and disable them. It will require PowerShell to be ran in administrator mode

   1: $d = ([DateTime]::Today.AddDays(-90)).ToFileTimeUTC()
   2: Foreach($domain in (get-adforest).domains){
   3:  Get-ADComputer -Filter {(pwdLastSet -lt $d) -and (LastLogonTimeStamp -lt $d) -and (OperatingSystem -like "Windows*")
   4:  -and (Enabled -eq $True) -and (IsCriticalSystemObject -ne $TRUE)}`
   5:  -Properties IPv4Address,PwdLastSet,LastLogonTimeStamp,servicePrincipalName,operatingsystem -server $domain | where {(($_.IPv4Address -eq $null))`
   6:  -and (!($_.serviceprincipalname -like "*MSClusterVirtualServer*"))} ! disable-adcomputer
   7: }

Now, set up a script that will find the disabled computer objects and delete them from AD.  Make sure to filter for items that are disabled and are not critical AD objects like domain controllers.  The idea is to delete disabled objects that have not changed in 90 days (it could be easy to justify this as low as 30 days).

   1: $d = [DateTime]::Today.AddDays(-90)
   3: Foreach($domain in (get-adforest).domains){
   4:  get-adcomputer -Filter {(Enabled -eq $false) -and (isCriticalSystemObject -ne $True) -and (whenchanged -lt $d)} '
   5: | remove-adcomputer
   6: }


What if LAPS is used and you’re finding that computer objects are being used even though pwdlastset or lastlogintimestamp isn't changing?  This has recently come up where for what ever reason none of the attributes in the criteria that has been identified is updating.  However, the laps password is still being updated and support staff is upset because they are now unable to retrieve the local admin password. 

One way to eliminate this problem is to add this filter: (ms-Mcs-AdmPwdExpirationTime -lt $d) to the disabled script. Adding this to the filter will only disable computers that have not changed their local admin password in 90 days (or what every you change $d to) after it was set to be changed.



There are multiple ways to do this.  Hopefully, you will leverage some of this to discover what is going on with computer objects in your environment and use it to help with Active Directory object hygiene.

Thank you for reading and have a good day.


Download the Code – Technet Script Gallery

Comments (1)

  1. Rafael Bandeira de Oliveira says:

    Great tip

Skip to main content