PowerShell How-To

3 Ways To Keep Active Directory Clean with PowerShell

Get rid of unused AD accounts quickly.

One of the most popular PowerShell topics I see in the community relates to finding Active Directory (AD) computers and users based on the age of the account.  Many people have a need to find "stale" computer and user accounts that are no longer needed. Increasingly, these folks are turning to PowerShell -- instead of tools like dsquery, ldapsearch, and various Joeware tools -- due to its ease of use.  I've seen hundreds of different solutions for finding these stale accounts using all kinds of methods -- most of them employing Get-AdUser and Get-AdComputer with the -Filter parameter.  What I haven't seen much of is the use of the Search-AdAccount cmdlet.  

Perhaps the Get verb tends to be used more often than the Search verb. Whatever the case, a lot of people are overlooking an extremely powerful and easy-to-use method to find these stale accounts— the Search-AdAccount cmdlet.  What people aren't seeing is that the Search-AdAccount cmdlet is already doing the heavy lifting for you!  By using Search-AdAccount, you don't have to mess with the nuances that come with the advanced filter -- or remember which AD attribute to use. (Was it LastLogonDate, LastLogonTimestamp, DateModified, or LastLogon I wanted?)  

Before you can use the Search-AdAccount cmdlet, you must first install Remote Server Administration Tools (RSAT).  RSAT is a group of tools that includes the Active Directory PowerShell Module, which Search-AdAccount is a part of.  RSAT differs by the operating system you'll be running it on, so just go to your favorite search engine and search for it, including your operating system version.  I have Windows 8.1, and you can download that version of RSAT from Microsoft here.  Get RSAT installed, and ensure the Active Directory PowerShell module is enabled. To do this, ensure you select the check box Active Directory Module for Windows PowerShell.

After you've got RSAT installed and the Active Directory module enabled, you should be able to use Search-AdAccount -- barring any permission or non-default configurations you have in your Active Directory environment.

Now that you've got RSAT installed, let's go over a few ways you can use Search-AdAccount to find those stale user and computer accounts in your AD environment.

1. Finding Disabled Accounts
Finding disabled accounts is a cinch with Search-AdAccount. It's just a simple matter of running Search-AdAccount -AccountDisabled.  It can't get any easier than that. By default, this finds all computer and user accounts. So before you're tempted to do something crazy with the Where-Object cmdlet, take a closer look at the Search-AdAccount cmdlet.  Search-AdAccount already gives you an option to filter users or computers with the -UsersOnly or -ComputersOnly parameters.

2. Finding Inactive Accounts
One of the biggest hassles of using Get-AdUser and Get-AdComputer with the advanced filter is figuring out the appropriate filter syntax. Search-AdAccount alleviates this by giving you the -AccountInactive parameter.  By using the -AccountInactive parameter and using a TimeSpan object (or even a date) as the parameter argument, you can now specify any kind of age you'd like.  Using the AD attribute LastLogonDate -- which is a friendly version of LastLogonTimestamp Search-AdAccount -- can easily give you a view of all those stale accounts.

For example, to find all accounts that haven't been active in 30 days, you'd simply write:

Search-AdAccount -AccountInactive -Timespan 30.00:00:00

Want to further limit that search to users or computers only? No problem!  Again, just use -UsersOnly or -ComputersOnly.  However, a word of caution regarding inactive accounts.  Microsoft only updates the LastLogonTimestamp attribute, which is the same as  LastLogonDate, once every 14 days to prevent replication storms.  Because of this characteristic of AD, using this method (or even Get-AdUser or Get-AdComputer with the advanced filter) won't be 100% accurate if you're attempting to get accounts with inactivity older than 14 days. So be forewarned.

3. Finding Accounts with an Expired Password
Another indicator that an account isn't used anymore is an expired password.  Once a password expires for an account, the account is unusable until the password is changed.  Finding accounts in AD that are expired -- and have remained expired for an extended amount of time—can be an indicator of a stale account. But this one can be tricky.

My goal here is to only return accounts that are no longer used.  I don't want to find accounts that are (or recently were) being actively used.  Using Search-AdAccount -UsersOnly -PasswordExpired gets me users that are not usable, but I don't know if those accounts just expired, or if they expired two months ago.  I need accounts that are expired and were expired a long time ago to be sure they're not being used.  So how do I do that?

$InactiveDays = 30

$MaxPasswordAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge.Days

Search-AdAccount -PasswordExpired -UsersOnly | Where-Object {((Get-Date) - (Get-AdUser -Filter "samAccountName -eq $_.SamAccountName").PasswordLastSet) -lt ($MaxPasswordAge + $InactiveDays)}

This script is more complicated than the other examples, so let me break it down.  First, what I'm trying to find here is all user accounts with an expired password that are connected to an account that has been expired for 30 days or more.   

In the first line, I'm just defining how many days the account needs to be expired before returning any results.  In the second line, I'm finding the domain's default domain password policy and getting the maximum password age in days.

Let's break down the third line.  First, I'm finding all users with an expired password.

Search-AdAccount -PasswordExpired -UsersOnly

I'm then getting the number of days the account has been in this state.

(Get-Date) - (Get-AdUser -Filter "samAccountName -eq $_.SamAccountName").PasswordLastSet)

I can't just take this number's word for it, because it all depends on the password policy applied.  I need to find the difference between when I know the password expired vs. how long it's been that way. So I'm taking the difference between how long ago it actually expired  and 30 days earlier than the maximum age.

($MaxPasswordAge + $InactiveDays)

This one took a little more work, but now we can be sure we're not going to accidently find accounts that were only recently expired—where the user might have been on vacation for a couple weeks.

The next time you're searching AD for inactive records, be sure to look at Search-AdAccount to see if it already does what you're trying to do.  It might just save you a ton of time!

About the Author

Adam Bertram is an independent consultant, technical writer, trainer and presenter. Adam specializes in consulting and evangelizing all things IT automation mainly focused around Windows PowerShell. Adam is a Microsoft Windows PowerShell MVP, 2015 powershell.org PowerShell hero and has numerous Microsoft IT pro certifications. He is a writer, trainer and presenter and authors IT pro course content for Pluralsight. He is also a regular contributor to numerous print and online publications and presents at various user groups and conferences. You can find Adam at adamtheautomator.com or on Twitter at @adbertram.

comments powered by Disqus

SharePoint Watch

Sign up for our newsletter.

I agree to this site's Privacy Policy.