Search Active Directory for Accounts with Passwords Set to Never Expire

In this Ask the Admin, I’ll show you how to audit Active Directory for accounts that have passwords set to never expire. Although it’s possible to configure policy in Active Directory (AD) to require a minimum password length, complexity, and to set how often passwords should be changed, AD should also be regularly audited for accounts that have passwords set to never expire, especially if they have administrative privileges to the domain or workstations.

Searching for accounts with passwords set to never expire

The easiest way to audit Active Directory is to use PowerShell’s search-adaccount cmdlet. It’s only supported on Windows Server 2012 and Windows 8 (or later), so you should perform the instructions below on a Windows 8.1 management workstation with the Remote Server Administration Tools (RSAT) installed, or on a Windows Server 2012 R2 domain controller (DC).

  • Log in with an account that has read permission to Active Directory.
  • Press the Windows key to switch to the Start screen.
  • Type powershell and make sure that Windows PowerShell is selected in the search results on the right.
  • Press ENTER to start PowerShell.
  • In the PowerShell console, type the command shown below and press ENTER.
​search-adaccount –passwordneverexpires

The built-in guest account in Active Directory is disabled by default and has its account password set to never expire, so you are likely to see guest listed in the results. We can pipe the results of search-adaccount to the where-object cmdlet to show only accounts that are currently enabled.

​search-adaccount –passwordneverexpires | where {$_.enabled}
Use PowerShell search-adaccount to audit Active Directory (Image: Russell Smith)
Use PowerShell search-adaccount to audit Active Directory (Image: Russell Smith)

You can also select the fields shown in the results using format-table as shown here:

​search-adaccount –passwordneverexpires | where {$_.enabled} | format-table name,objectclass

Accounts that are disabled can be ignored, so the ability to see the results only for enabled accounts is valuable. I can make the results more useful by piping the output to PowerShell’s grid view, giving an Excel like display where the search results are organized into columns.

​search-adaccount –passwordneverexpires | where {$_.enabled} | out-gridview

If you want to limit the fields in out-gridview, you’ll need to use the select-object cmdlet instead of format-table as shown here:

​search-adaccount –passwordneverexpires | where {$_.enabled} | select-object name,objectclass | out-gridview

Search and destroy

OK, well not quite. But if your Active Directory is carefully managed, you might decide to disable all enabled accounts that have passwords set to never expire. That’s easily achieved using PowerShell. Note that I’ve added the –usersonly parameter to the search-adaccount cmdlet to avoid disabling any other kind of account accidently.

​search-adaccount –passwordneverexpires -usersonly | where {$_.enabled} | disable-adaccount

To further reduce the risk of automating the process of disabling accounts, you could also limit the search command to a specific Organizational Unit (OU) or container. In the example below, I’ve added the –searchbase parameter to the search-adaccount cmdlet to limit the search to the default Users container in the ad.contoso.com domain. You will need to modify the string to match your own domain name.

​search-adaccount –passwordneverexpires -usersonly -searchbase "cn=users,dc=ad,dc=contoso,dc=com" | where {$_.enabled} | disable-adaccount

A safer strategy is to export the results to a text file, manually inspect and clean up the search if necessary, then parse the file using import-csv and pipe the results to a foreach loop to disable the accounts. Sounds difficult? Not at all. Note that the c:temp directory must exist before running the command.

​search-adaccount –passwordneverexpires -usersonly -searchbase "cn=users,dc=ad,dc=contoso,dc=com" | where {$_.enabled} | export-csv “c:tempuserstodisable.csv”

After editing the userstodisable.csv manually, parse the file and disable the accounts:

​$users = import-csv “c:tempuserstodisable.csv”

foreach ($user in $users){

  disable-adaccount -identity $user.distinguishedname
  write-host $user.name “has been disabled.”

}

For more information on parsing comma-delimited text files using import-csv, see Parsing Comma-Delimited Text Files using PowerShell on Petri.