Active Directory Replication

Using it to find when accounts were disabled.

Posted by Michael Ryan on March 1, 2018

The question came up of how to determine when an account was last disabled. This technique can obviously be used to answer when other things changed about an account too, but your most common use cases are going to be group membership changes and determining if it's safe to delete an account based on when it was last disabled.

First, some background:

Replication:

Active Directory's Domain Controllers share data with one another through a process called replication. When a "replicated attribute" changes on one domain controller it communicates this change with the other domain controllers.

UserAccountControl:

This is a bunch of hexadecimal values that get computed and added together. Frankly it's a huge pain in the ass to make a determination based on this value about the state of an account, but it's worth knowing what things can trigger a change in this attribute. The most common ones are if the account is disabled or not, and if the account has a nonexpiring password or not (the full list of changes that change this here at this link)

So how can this knowledge help me answer the question of when an account was disabled?

Well, if we know an account is disabled *now*, then we know for certain the last useraccountcontrol change was either the change that disabled the account, or a change that happened when the account was already disabled.

Either way, we can tell approximately how long the account has been disabled by checking on a currently disabled account when that value was last changed.

Replication Metadata:

If you have access to a Windows 10 or Server 2012 device with the AD cmdlets installed (a windows feature that comes with RSAT), there's a handy dandy command called Get-ADReplicationAttributeMetadata.

This command doesn't require elevated rights to run, the OS requirement is basically the biggest hurdle.

Required parameters of this command are:
Identity - This command is rather specific and for an identity requires you use the DistinguishedName of the object you want to get some information about. Obviously the easiest way to get this is to use Get-ADUser or Get-ADGroup which give you the DistinguishedName value by default.
Server - You can use any Domain Controller that would have received the replication data, which usually means any writable domain controller in your domain.

So for an example if I have a DN
CN=mike,OU=Users,DC=contoso,DC=com
and I run
Get-ADReplicationAttributeMetadata "CN=mike,OU=Users,DC=contoso,DC=com" -Server ADDC01
It'll start showing me some replication data.

How can I use this to determine the disabled date?

Well lets assume that we know mike is disabled, and take a look at the data we got about mike. If you were looking closely you may have noticed that one of the pieces of replication data was UserAccount Control, it would have looked something like this:
AttributeName : userAccountControl
AttributeValue : 514
LastOriginatingChangeTime : 7/27/2016 1:09:52 PM
Object : CN=mike,OU=Users,DC=contoso,DC=com
Server : ADDC01.contoso.com
Version : 2
What this says to us is that mike's useraccountcontrol was changed back in July 2016, it was changed to 514 (disabled) and this was version 2. Version 1 was likely that the account was enabled, but it's worth noting we can't look back at previous attribute versions using this method.

It's possible he was created disabled with a nonexpiring password and on July his password was set to expire, generating this event, however we can say with certainty that mike has been disabled since at least July 2016.

How can I do this if I have a list of users?

If you have multiple users you want to look up disable dates on, it's probably not reasonable to run this command one by one by one, so here I'll show you a quick script to run this against a list of users.

In this example I'll assume you have a flat .txt file with a list of hard return separated usernames in it called "users.txt" at the root of your C drive on a Windows device. The output will be a .csv with the same usernames, and the date at which they were disabled.

Make sure you replace the domain controller supplied for the parameter "server" with a real domain controller from your domain, too.
Get-Content "C:\users.txt" | ForEach {
$aduser=Get-ADUser $_
$whendisabled=(Get-ADReplicationAttributeMetaData $aduser.DistinguishedName -Server ADDC01 |
Where {$_.AttributeName -eq "userAccountControl"}).LastOriginatingChangeTime
$aduser | select samaccountname,@{n="WhenDisabled";e={$whendisabled}}
} | Export-Csv C:\users.csv -NoTypeInformation