Microsoft Security Best Practice: Clean Up Active Directory Accounts with PowerShell

Why use third-party security tools for Active Directory account cleanups when the ubiquitous PowerShell serves just as well?

In a presentation of Microsoft security best practices, 21-time Microsoft MVP Brien Posey detailed just how to do that, even supplying ready-to-go scripting commands for the audience to use.

Posey, a freelance IT tech author and event speaker, shared his enterprise security expertise in a half-day summit held by Virtualization & Cloud Review titled "Microsoft Security in 2023 Summit," which is available for on-demand viewing for those who want to benefit from all 10 of his top security best practices.

In his examination of "Top Best Practices for Windows Enterprise Security Heading Into 2023," Posey provided his PowerShell scripts in explaining best practice No. 6: clean up Active Directory user accounts.

He noted that one of favorite methods that attackers use is looking for accounts that maybe shouldn't be there in the first place.

"I'm talking about accounts that were created, but never actually used," Posey said. "A user never logged on into it, or accounts that belong to a user who no longer works for the company. And yet the account remains, the user hasn't logged into it for forever, things like that. And the reason why attackers look for these types of accounts is because it makes it easy to evade detection.

"Any attempts to gain access to the domain admin account are going to raise some red flags, and people are going to take notice. If, on the other hand, you begin your attack by trying to compromise standard user accounts, that's going to be a little bit less noticeable, particularly if you go after accounts that nobody's actually using."

Brien Posey, 21-time Microsoft MVP and freelance technology author

"Think about it for a moment. If you're an attacker and you're looking to compromise somebody's network, and you immediately go after the domain admin account, well, that's going to raise a lot of attention. You know, hopefully the organization that you're attacking has mechanisms in place to spot security breaches, and any attempts to gain access to the domain admin account are going to raise some red flags, and people are going to take notice. If, on the other hand, you begin your attack by trying to compromise standard user accounts, that's going to be a little bit less noticeable, particularly if you go after accounts that nobody's actually using."

So Posey advised the audience of several hundred IT security pros to look at their Active Directory database and search for accounts that are no longer needed. And then, obviously, either disable or get rid of those accounts.

So how do you go about doing that?

"Well, there are some really good third-party tools that can help you with that," Posey said. "But you can just as easily do it with PowerShell. So I want to show you a couple of techniques that you can use to go in and find those Active Directory user accounts that are no longer needed."

Here are his script commands.

Find Users Who Have Never Logged On
Here's the script:

Get-ADUser -Filter {(lastlogontimestamp -notlike "*")} | Select-Object Name, DistinguishedName

Here's the result:

Find Users Who Have Never Logged On
[Click on image for larger view.] Find Users Who Have Never Logged On (source: Brien Posey).

Here's Posey's description:

"So let's start by doing something easy taking a look at users who have never logged on. Now, why might this happen? Well, most commonly, an organization will hire a new user and they'll set up an account for that user. And then maybe the day or two before the user is supposed to start their new job, they call the HR departments and say, 'Hey, I got a better offer. I'm not coming to work for you, after all,' and they just move on. But at that point, the accounts have already been created. And it just kind of slips through the cracks. Nobody ever goes in and deletes that account, so you've got this account that has never been used. So how do you find those? By using this command right here.

"Get-ADUser is the PowerShell commandlet [or cmdlet] that retrieves information about Active Directory users. And so after the Get-ADUser commandlet, I've got this filter switch. And the filter switch allows us to look for users that fit very specific criteria. So in this case, that filter criteria is lastlogontimestamp -notlike "*". So what this means is that when a user logs on to an Active Directory account, there's a parameter -- or an attribute, rather -- associated with that account called lastlogontimestamp. And it contains a value showing the last time that the user logged on. So what we're doing here is we're checking to make sure that this particular timestamp is -notlike "*". So * is a wildcard -- it can be anything. So -notlike "*" basically means that this attribute doesn't contain a value, meaning that nobody has ever logged on to this account. So then once we've applied the filter, there's a pipe symbol and then Select-Object Name, DistinguishedName. And all that does, for anybody who's not familiar with PowerShell, is it causes the Get-ADUser commandlet to display the name that's associated with the account.

"So let's take a look at what this looks like in action. Okay, so if you look at the very top of the slide [see screenshot above], you can see the Get-ADUser -Filter command. And then we've got last logon timestamp, -notlike "*". So when I run this command, we get a list of all of the user accounts that have never logged on. In this particular situation, there's quite a few of them -- I set up several accounts just for this purpose.

"Now, if you look below the list of users who have never logged on, you can see that I entered a second PowerShell commandlet. And this one is almost identical to the first one but with one minor exception. Instead of using lastlogontimestamp -notlike "*", I use lastlogontimestamp -like "*". And what this does is it returns a list of users who have logged on at some point. So in this case, we've got two users who have logged on to this network. And we've got about a dozen users who have never logged on."

Find Disabled Users
Here's the script:

Get-ADUser -Filter {(Enabled -eq $False)} | Select-Object Name, DistinguishedName

Here's the result:

Find Disabled Users
[Click on image for larger view.] Find Disabled Users(source: Brien Posey).

Here's Posey's description:

"Another thing that you can do, is you can go out and look for user accounts that are disabled. Now this has a couple of useful functions. One is that it can help you to go clean up old user accounts. Because it's really common when a user leaves an organization to simply disable that account for a period of time, so that the user's data remains because you never know when a coworker might need something that belonged to that user. So it's easier to leave the account created and just disable it so nobody can log on to it. So that's one thing that this particular command is useful for. Another thing that useful is that this can help you to verify your security posture if you've got certain built-in accounts that you want to be disabled at all times. This gives you a very easy way of verifying that those accounts are still disabled.

"So how does the command work? Well, it's really similar to what I just showed you on the previous slide. We start with Get-ADUser. And again, that's the PowerShell commandlet that's used to return information about an Active Directory user. And then we're using the filter once again, but we're using a different filter. Instead of looking at the last logon timestamp, what we're looking at is whether or not the Enabled attribute is set to false. So if the Enabled attribute is set to true, that means that the account is enabled. If it's set to false, that means that the account is disabled. And then again, after the filter, I'm using Select-Object Name, DistinguishedName to display the user name. And you can see the results of this particular command down below. So in this case, user four is disabled. We've got another account, krbtgt, that's disabled. And then we have a guest account that's disabled. Now, the guest account is disabled by default. And there's really no good reason for enabling this account."

Find Users Who Haven't Logged in for a Long Time
Here's the script:

$InactiveDays = 90 
$Days = (Get-Date).Adddays(-($InactiveDays)) 
Get-ADUser -Filter {LastLogonTimeStamp -lt $Days -and enabled -eq $true} -
Properties LastLogonTimeStamp | Select-Object Name,@{Name="Date"; 

Note that Posey found this script at ShellGeek.

Here's Posey's description:

"So let's say you've got a user who has left the organization, their account remains and nobody's logged on to this account for ages. Well, this is how you would go about finding that account," said Posey, who noted he got the script from the ShellGeek site. "There are other ways that you do it. But this was one of the simpler ones that I could find.

"But the very first line creates a variable called InactiveDays, and that's set to 90. So as this is written, this is going to look for users who haven't logged on in the last 90 days. You could change the value of that InactiveDays to whatever you want. So you could change it to look for users who have logged on in 30 days, 120 days -- it doesn't really matter. But you would set the number of days using this line. So then we have another variable, $Days. And what we do is we use the Get-Date function, which gets the current date, and then we're adding a negative to get what the date was on, or 90 days ago in this particular case. So then we're using Get-ADUser. And then once again, we're setting up a filter. And then all of that stuff that you see after the filter is just date computations; it's a way of looking to find out if the user had has logged on after the date that is specified."

Note that all of the above simply constitute Posey's best practice No. 6. As indicated earlier, the presentation can be viewed on-demand to get his equally informative insights on the remaining best practices:

  • Treat Pass the Hash as a Serious Threat
  • Lock Down the Built-in Administrator Account
  • Think About How You Will Manage Domain Joined Workstations
  • Reassess Your Audit Policy, Especially on Older Networks
  • Use Very Specific Security Group Names
  • Reevaluate Your Service Accounts
  • Establish a Security Baseline and Enforce It
  • Look for Ways in Which Non-Windows Resources Can Impact Windows Security
  • Reconsider How You Are Controlling Endpoint Applications

However, attending such live presentations in person brings extra benefits, including the ability to engage in a Q&A with expert presenters (not to mention the chance to win great prizes typically valued at around $1,000 or more).

With that in mind, here are some summits coming up from Virtualization & Cloud Review and our partners, and


Subscribe on YouTube