Summary: Scripting tips for cleaning up stale Active Directory accounts.
Microsoft Scripting Guy, Ed Wilson, is here. Today we continue our series about Active Directory PowerShell by Ashley McGlone. Before you begin, you might enjoy reading these posts from the series:
See yesterday’s post for a discussion and guidance about stale-object cleanup. You really need to read it for context.
Continuing from yesterday…
I have been doing Active Directory scripting for well over a decade. You need to know that there is no fail-safe way to identify stale users and groups with 100% accuracy. We can have a high degree of certainty; but ultimately, there could be an odd case where the account has a dependency in the environment.
Do not over emphasize this point with management, but they need to know that there is a slight degree of risk. This minor risk factor turns off many departments from implementing the stale account cleanup.
Our goal is to eliminate as much risk as possible and offer the quickest recovery time should the wrong account get “cleaned.” Therefore, we must use multiple account properties to determine if accounts are still in use. Also refer to the safety steps in yesterday’s post.
It would not be practical for me to script the entire process for you in this post. But I do want to offer some scripting tips for identifying stale objects. This primarily involves the date fields in Active Directory and a few syntax nuances.
Which date do I query?
There are many date/time stamp and activity attributes in Active Directory accounts. Here are some of the most popular ones and a description of each. There are pros and cons to consider for most of these.
- WhenCreated
Date and time that the account was created. Compare with a modified date to see if it was ever actually used. - **WhenModified
**Great property to see the last change date of an account. However, this property is not replicated and must be queried from every domain controller to be truly accurate. - LastLogon
**Date and time of the last logon. However, this property is not replicated. Use **LastLogonTimestamp instead.
For more information, see “The LastLogonTimeStamp Attribute”–What it was designed for and how it works. - **LastLogonTimestamp
**Date and time of last logon. This is replicated to all domain controllers and a generally reliable measure.
For more information, see “The LastLogonTimeStamp Attribute”–What it was designed for and how it works. - **LogonCount
**Count of logons against each domain controller. However, this property is not replicated, and it must be queried from every domain controller to be truly accurate. - **PwdLastSet
**Equal to zero or 9223372036854775807 if the password has never been set. This is generally a reliable way to gauge account usage—except for accounts with passwords that never expire. This number is actually a date/time value represented as an Int64 data type. - msDS-LastSuccessfulInteractiveLogonTime
This attribute is only updated when a particular Group Policy setting is enabled. That policy is not practical for some environments, so this field is not as useful as it sounds initially.
For more information, see this Question and Answer on the Ask the Directory Services Team blog.
There are many more attributes to explore. For some super-secret details about these attributes, I highly recommend checking out User Security Attributes.
Dates or numbers?
After you have picked the date fields, the next challenge is working with the date values. There are normal date/time data-type fields, and then there are “those” dates. You know—the dates that look like a long string of numbers. Whatever are we going to do with those?
We can use a couple methods to convert to and from that gnarly date format:
# Password last set within the last 30 days?
$pwdLastSet = Get-ADUser Administrator -Properties pwdLastSet |
Select-Object -ExpandProperty pwdLastSet
# Option A
$pwdLastSet -gt (Get-Date).AddDays(-30).ToFileTimeUTC()
# Option B
[datetime]::fromFileTimeUTC($pwdLastSet) -gt (Get-Date).AddDays(-30)
For more details, see Back To The Future: Working with date data types in Active Directory PowerShell.
Documentation in the description
Modifying the account description may not be intuitive, so let’s take a quick look at Set-ADObject. To append your staleness comment, try something like this:
Get-ADUser JoeUser -Properties Description |
ForEach {Set-ADUser -Identity $_.DistinguishedName `
-Replace @{Description = $_.Description + ” *** Disabled as stale ” + `
(Get-Date -Format g)}}
Quick Windows PowerShell reports
There are some commands that will quickly show you inactive accounts:
### USERS ###
# Quick inactivity report
Search-ADAccount -AccountInactive -UsersOnly
# Timespan
Search-ADAccount -AccountInactive -UsersOnly -TimeSpan (New-TimeSpan -Days 90)
# Datetime
Search-ADAccount -AccountInactive -UsersOnly -DateTime ’10/1/2014′
# Disabled
Search-ADAccount -AccountDisabled -UsersOnly
# Expired
Search-ADAccount -AccountExpired -UsersOnly
### COMPUTERS ###
# Quick inactivity report
Search-ADAccount -AccountInactive -ComputersOnly
# Timespan
Search-ADAccount -AccountInactive -ComputersOnly -TimeSpan (New-TimeSpan -Days 90)
# Datetime
Search-ADAccount -AccountInactive -ComputersOnly -DateTime ’10/1/2014′
# Disabled
Search-ADAccount -AccountDisabled -ComputersOnly
These commands will return a list of user or computer accounts that you can filter against the exception list you created. For more information about** Search-ADAccount** cmdlets, see this Question and Answer on the Ask the Directory Services Team blog.
Then you can use Test-Connection to see if they respond to a ping (although this is not always reliable either). Use Resolve-DNSName for computers to see if they are still listed in DNS, assuming that you scavenge stale DNS records.
Wrapping up
I realize that these two posts do not offer a complete, scripted solution. However, you now have an expert design and code samples to begin your solution as you learn Windows PowerShell for Active Directory.
Stale account cleanup is a popular topic, and you can find many people who have attempted and published their scripts. You can use these as a place to start your script, or you can write it from scratch. Script resources for IT professionals in the Script Center Repository is a great place to start looking. (Of course any time you download a script from the Internet, you should read it thoroughly and test it in your lab first.)
After reviewing these considerations, it is easy to see why some people avoid the task. There is some fuzzy logic involved, a decent amount of coding, and a small degree of risk. Leverage online resources to help you through the process. If you get completely stuck, talk to your Microsoft premier services technical account manager. They can set up a call for you to talk through your challenges with a premier field engineer like me. You can also post questions to the Script Center Forums. Happy scripting!
~ Ashley
Thanks, Ashley! This is a very helpful series. Come back tomorrow for the final post of Ashley’s Active Directory Week series.
Ashley recently recorded a full day of free Active Directory PowerShell training: Microsoft Virtual Academy: Using PowerShell for Active Directory. Watch these videos to learn more insider tips on topics like getting started with Active Directory PowerShell, routine administration, stale accounts, managing replication, disaster recovery, and domain controller deployment.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy
Windows PowerShell, Scripting Guy!, Guest blogger, Ashley McGlone, Active Directory
0 comments