The Powershell Diaries - Finding Problem User Accounts in AD

Published: 2015-06-24
Last Updated: 2015-06-24 18:20:52 UTC
by Rob VandenBrink (Version: 1)
10 comment(s)

Powershell has gotten a lot of attention lately as a pentester's tool of choice, since it has access to pretty much every low-level system function in the Microsoft ecosystem, and the AV industry isn't dealing well with that yet (aside from ignoring powershell completely that is).

But what about day-to-day system administration?  Really, the possibilities for admins are just as limitless as for pentesters - that's what Powershell was invented for after all !

A simple call like "get-aduser -filter * -properties * "  can get you everything you want on domain user accounts.  However, most sysadmins will look at this and give me the TLDR response - it's just too much information to process effectively.

But how about filtering that- let's find all users who aren't required to reset their passwords?

Or who don't have passwords at all?

How about have never reset their passwords (ie - haven't changed the initial password set at creation):
get-aduser -filter * -properties * | select samaccountname,passwordlastset

Operationally - let's add to the list - say folks who've had their accounts locked.  This might be a "reset password on Friday, can't remember on Monday" symptom, but might also  be someone brute forcing that account on the corporate website or VPN  (hint - 2-factor authentication does wonders for those!)

get-aduser -filter * -properties * | select samaccountname,passwordlastset,lockedout

You can use the above to also find out who's left the organization.  If you're like lots of IT groups, maybe HR isn't so timely in letting you know about departures!  Let's dig to see who hasn't logged in in 4 weeks.  8 weeks?  12 weeks?   Best call HR with this list in-hand to see if these folks are on longer term leave, or if they've moved on or maybe just stopped showing up for work?

get-aduser -filter * -properties * | select samaccountname,lastlogondate

At this point it becomes obvious that you want to sort these lists.  You can go directly to a GUI view, where you can sort an play with the data as needed:

get-aduser -filter * -properties * | select samaccountname,passwordlastset | out-gridview

I find the CSV output, which can  then be imported to excel - to be the most useful.  If for regulatory (or other) reasons, you then need to save those files to demonstrate that you do audit yourself, and that you compare your audits to previous data, this can be a real help

The list that I use most often is below (change the field order as needed):

get-aduser -filter * -properties * | select samaccountname, name, enabled, scriptpath, passwordlastset, passwordexpired, passwordneverexpires, passwordnotrequired, lockedout, lastlogondate, cannotchangepassword, accountexiprationdate | export-csv "c:\pathspec\account-problems-yy-mm-dd.csv"

This imports directly into Excel (or any other spreadsheet), where you can slice and dice to your heart's content.

In closing, let me acknowledge Jason Fossen and SANS SEC 505 for re-kindling my enthusiasm for Powershell !  If you want to dig deeper into Powershell with a security slant, I'll be posting on this topic for a while, stay tuned.  But if you want 6 days solid of concentrated powershell+windows goodness, take a look at SEC 505!


Rob VandenBrink

10 comment(s)


I once created some reports for an auditor that dumped info to TXT or CSV files, only to find out that they really preferred print screens, possibly because text files can be manipulated without trace but print screens can not.
For finding passwords that never expire using PS there is also this:

I often use ADSI queries in a linked server through SQL Server instead of PS, mainly because we have some apps that use them, too, and it is quick and easy. There are some limitations with that approach (can't query multi-value attributes), but overall it works great. To find users with passwords that never expire in A/D using ADSI in SQL, I use:

displayName AS DisplayName,
sAMAccountName AS Account

The first ASN.1 "magic string" specifies accounts whose password doesn't expire, and the second one only brings back non-disabled accounts.
nice intro, i really gotta up to speed on powershell
I can see the line of reasoning that led the auditor to printscreens, but bitmaps can be manipulated as well. More importantly though, in an AD of a few thousand or even a few hundred users, you'll find that printscreens just don't scale - you really do need reports of some kind to find and resolve problems.

Maybe print all the reports to PDF for a non-technical auditor??
PowerShell is good stuff for sure....

In any environment with multiple domain controllers you need to make sure that you query all domain controllers and use the most recent time stamps. Windows does not sync the times across controllers. Simply using "get-addomaincontroller -Filter *" will return your domain controllers. You will then want to write a script to find the most recent time stamps.

The filter could also be set to look for certain types of accounts instead of pulling out a large number of accounts.

For instance, maybe you only care about enabled accounts so -filter "enabled -eq 'True'" would only find enabled accounts. The same could be done to only pull out accounts that the password never expires.

If the admin naming scheme is known

-filter "samaccountname -like '*admin*'"


-filter "samaccountname -like '*adm*'"

There is so much more that can be done. I have scripts used to enumerate all local admins on workstations across the enterprise as just one example.
This is great and will come in handy. Thanks for putting it out there. I will probably look into this class
Generally, I think you will get better performance if you pull only the attributes you need, instead of all of them.

Instead of -properties * you would use -properties passwordlastset,otherproperty1,otherproperty2
I've changed things a bit with your example. One is to remove an annoying header in the output (export-csv -notype), the others are to get sortable date output, and to use the lastLogonDate, which is a synthetic output of the lastLogonTimeStamp. This last is much more convenient because it's already in date format, and the lastLogonTimeStamp is replicated amongst the DCs.

Get-ADUser -filter * -properties * -resultpagesize 0 -SearchBase "ou=users,ou=USOffice,dc=example,dc=com" | select samAccountName, Name, Enabled, ScriptPath, PasswordNeverExpires, PasswordNotRequired, LockedOut, CannotChangePassword, @{Name="PasswordLastSet"; Expression={$_.PasswordLastSet.ToString("u")}}, @{Name="PasswordExpired"; Expression={$_.PasswordExpired.ToString("u")}}, @{Name="AccountExpirationDate"; Expression={$_.AccountExpirationDate.ToString("u")}}, @{Name="lastLogonDate"; Expression={$_.lastLogonDate.ToString("u")}} | export-csv -notype c:\temp\yyyy-MM-dd_AccountData.csv


Yes. In small directories retrievign all the properties is probably fine but it will cause issues when querying thousands of users accounts.
Specifying the prperties to be retrieved helps reduce the load:

$Properties = @(samaccountname, name, enabled, scriptpath, PasswordLastSet, PasswordExpired, passwordneverexpires,
passwordnotrequired, lockedout, lastlogondate, cannotchangepassword, accountexiprationdate)

get-aduser -filter * -properties $Properties |
select $Properties |
export-csv "c:\pathspec\account-problems-yy-mm-dd.csv"

You can create an array with the required property names and then reference that in the Get and in the Select.

Also, look at Search-ADAccount for a quick way to perform certain filtering.

Diary Archives