Using PowerShell to Find Disabled or Inactive User Accounts in Active Directory

One of the most common applications of PowerShell is with Active Directory, which makes a lot of sense. Active Directory is a huge source of information and naturally IT pros want an easy way to get that information. Perhaps you need to do something with the information or maybe you simply need a report so that someone else can make decisions. Using PowerShell to query Active Directory is not that difficult, especially if you have cmdlets at your disposal. A typical Active Directory task that can be easily automated with PowerShell is to identify disabled or inactive user accounts, which I’ll show you how to do in this PowerShell Problem Solver article.

The easiest solution is the Active Directory PowerShell module from Microsoft. This module requires at least one domain controller running Windows Server 2008 R2 or later that’s running Active Directory Web Services. On the client side you need PowerShell 3 or later and the Active Directory tools that are part of the Remote Server Administration Toolkit (RSAT) download. Get the latest version for your operating system. I am running PowerShell 4.0 on a Windows 8.1 desktop with RSAT installed. You can verify the module like this:

get-module ActiveDirectory -list

If you don’t see it, open Control Panel –Programs and select “Turn Windows Features on and off.” Scroll down to Remote Server Administration Tools, and make sure you’ve checked the box for the module.

Turning on the Active Directory Module for Windows PowerShell feature. (Image Credit: Jeff Hicks)
Turning on the Active Directory Module for Windows PowerShell feature. (Image Credit: Jeff Hicks)

 
One you have the module, you are ready to go.
You might think you need to use Get-ADUser, and that could work. Although creating the right filter might be a little tedious. Instead let’s look at Search-ADAccount. If you look at cmdlet help, you will see my very helpful parameters. The command can be as simple as this:

search-adaccount -UsersOnly –AccountDisabled

This expression will search the entire domain for user accounts that are disabled. The result will be a user account object.

Using the Search-ADAccount cmdlet in Windows PowerShell. (Image Credit: Jeff Hicks)
Using the Search-ADAccount cmdlet in Windows PowerShell. (Image Credit: Jeff Hicks)

 
But more than likely, you will want to limit your search to a particular organizational unit (OU).

search-adaccount -UsersOnly –AccountDisabled –searchbase "OU=employees,dc=globomantics,dc=local"/code>

The SearchBase will be the OU distinguishedname. It will search all child OUs as well.

Limiting our search to part of the organizational unit in Windows PowerShell with Search-ADAccount. (Image Credit: Jeff Hicks)
Limiting our search to part of the organizational unit in Windows PowerShell with Search-ADAccount. (Image Credit: Jeff Hicks)

 
I should point out that I am logged on with an account that has domain administrator rights. This cmdlet will accept a PSCredential, so you can specify alternate credentials if necessary.

Remember, you are getting an object, so you can do all sorts of things with it.

Search-ADAccount -UsersOnly -AccountDisabled -SearchBase "OU=Employees,DC=globomantics,dc=local" |
sort LastLogonDate | Select Name,LastLogonDate,DistinguishedName |
out-gridview -title "Disabled Employees"

Using Search-ADAccount to grab a list of disabled employees. (Image Credit: Jeff Hicks)
Using Search-ADAccount to grab a list of disabled employees. (Image Credit: Jeff Hicks)

 
My test domain has a number of sample accounts that never logon, which is why the LastLogonDate is blank for some of them. One important thing to remember is that this cmdlet is not giving you the full user account. You might want to know things like the user’s department or when the account was last modified, which in this case might indicate when the account was disabled.
To get more information, all we need to do is pipe the results to Get-ADUser and request the additional properties.

$paramhash=@{
UsersOnly = $True
AccountDisabled = $True
SearchBase = "OU=Employees,DC=globomantics,dc=local"
}
Search-ADAccount @paramHash |
Get-ADuser -Properties Description,Department,Title,LastLogonDate,WhenChanged |
sort LastLogonDate |
Select Name,Department,Title,Description,WhenChanged,LastLogonDate,DistinguishedName |
out-gridview -title "Disabled Employees"

With Get-ADUser, you have to specify the properties you want to see, otherwise you get a minimal set. But now my output is a bit richer.

Another example of Get-ADUser results with PowerShell. (Image Credit: Jeff Hicks)
Another example of Get-ADUser results with PowerShell. (Image Credit: Jeff Hicks)

 
I could have exported information to a CSV or XML file, or created an HTML report.
I can do something similar with expired accounts

$paramhash=@{
UsersOnly = $True
AccountExpired = $True
SearchBase = "OU=Employees,DC=globomantics,dc=local"
}
Search-ADAccount @paramHash |
Get-ADuser -Properties Department,Title |
Select Name,Department,Title,DistinguishedName

Finding expired accounts in Windows PowerShell. (Image Credit: Jeff Hicks)
Finding expired accounts in Windows PowerShell. (Image Credit: Jeff Hicks)

 
Finding inactive accounts takes a bit more planning. You may think any account that hasn’t been used in 10 days is inactive. But for another organization it might be nine months. We can still use Search-ADAccount, but need to add a second piece of information. You can specify a time frame with the Timespan parameter.

$paramhash=@{
UsersOnly = $True
AccountInactive = $True
TimeSpan = New-Timespan -Days 120
SearchBase = "OU=Employees,DC=globomantics,dc=local"
Server = "chi-dc04"
}
Search-ADAccount @paramHash | measure

Using the timespan parameter with Search-ADAccount in Windows PowerShell. (Image Credit: Jeff Hicks)
Using the timespan parameter with Search-ADAccount in Windows PowerShell. (Image Credit: Jeff Hicks)

 
I have almost 400 accounts that have not been active in the last 120 days. Hopefully your domain isn’t as dormant. You can either specify a TimeSpan like I did, or you can specify a string in the format Days.Hours:Minutes:Seconds.

If you use these parameters, you may get an error if your domain controller is running Windows Server 2008 R2. I told PowerShell to use a specific domain controller because I knew it was running Windows Server 2012 and that eliminated the error. Or if it is easier to think about you can specify a specific date to find users who have been inactive since that date.

$paramhash=@{
UsersOnly = $True
AccountInactive = $True
DateTime = "7/1/2014"
SearchBase = "OU=Employees,DC=globomantics,dc=local"
Server = "chi-dc04"
}
Search-ADAccount @paramHash 

I believe the Search-ADAccount cmdlet has changed over time since it was first released. If you don’t see these parameters, try to upgrade your client to the most current version of PowerShell and RSAT that it will support. Otherwise, post in the PowerShell forum on the site, and I’ll help you figure out the corresponding syntax with Get-ADUser. The Microsoft cmdlets are not the only solution. I’ll be back in a future article to demonstrate some alternatives.