AD Group History Mystery: PowerShell v3 REPADMIN

I remember back in high school the janitor had this massive ring of keys on his belt.  The keys would jingle with each step as he pushed the broom down the hall.  It was like his own percussion section accompanying the tune he whistled.  So what does this have to do with PowerShell?

The Scenario

After speaking about SID history and token size at PowerShell Saturday last month an attendee approached me with a common concern.  I was so excited to code the answer that I did it in the airport on the way home.

Joe User has been with the company for 23 years and has accumulated more group memberships than the entire desktop support team.  Joe has rotated through five different departments during his career and managed to survive all of the layoffs.  As a result he has access to every share in the company.  Even worse his access token is so big that it won’t fit through the door.

imageWe would love to clean up his group memberships, but we have no way of knowing when he was added to all these groups.  If we could see the dates he joined those groups it would give us a clue about removing just the older group memberships.  Without this information his token will continue to bloat… just like that overloaded key ring swinging on the janitor's hip.

Where can we find group membership details?

When you look into the member attribute of an AD group you’ll find a list of all members in distinguished name format.  But that’s it.  There is no smoking gun or finger prints that tell you how they got there.  However, there is a little-known piece of data called replication metadata that can tell us exactly what we need.  This data is quite special for groups, because it shows us the date individual members were added and removed.  Awesome!  But if you try to view it in the GUI it looks like ugly hex.

REPADMIN is so last decade

imageThat’s where REPADMIN helps with the handy showObjMeta parameter.  While this command will show us the data, it wraps and scrolls so much in the console that it is difficult to read.  Also it is extremely painful to parse with any kind of script.

Try it for yourself:

repadmin.exe /showObjMeta DCNAME “CN=GroupName,OU=SomeOU,DC=contoso,DC=com”

This is a cool command that I’ve used for forensic investigations in the past to see when an attribute was last modified and which DC originated the change.  Then you may be able to trace it down in the logs on that DC to find the account that made the change.  You can read more about this here and here.

Can I do it with PowerShell?  Please say yes.

Way back in PowerShell v1 MVP Brandon Shell wrote a script called Get-ADObjectReplicationMetadata to do this.  The AD cmdlets in PowerShell v2 had little parity with REPADMIN.  Now in PowerShell v3 the AD cmdlets have made good progress.  We still have a ways to go, but you can see in the chart below that PowerShell is catching up with REPADMIN.  This is an unofficial comparison chart that I created based on my own observations.  Any corrections or additions are welcome.

Notice now we have one of my new favorite cmdlets Get-ADReplicationAttributeMetadata.  When I found this in the Windows Server 2012 beta it was like Christmas morning!



2012 Cmdlets
/FailCache Get-ADReplicationFailure
/Queue Get-ADReplicationQueueOperation
/ReplSingleObj Sync-ADObject
/ShowConn Get-ADReplicationConnection
/ShowObjMeta Get-ADReplicationAttributeMetadata
/ShowRepl  /ReplSum Get-ADReplicationPartnerMetadata
/ShowUTDVec Get-ADReplicationUpToDatenessVectorTable
/SiteOptions Set-ADReplicationSite
2008 R2 Cmdlets
/ShowAttr Get-ADObject
/SetAttr Set-ADObject
/PRP Get-ADDomainControllerPasswordReplicationPolicy

The Script

Here is the PowerShell goodness we’ve been awaiting (also attached at the bottom of the post):

 Import-Module ActiveDirectory            
$username = "janitor"            
$userobj  = Get-ADUser $username            
Get-ADUser $userobj.DistinguishedName -Properties memberOf |            
 Select-Object -ExpandProperty memberOf |            
 ForEach-Object {            
    Get-ADReplicationAttributeMetadata $_ -Server localhost -ShowAllLinkedValues |             
      Where-Object {$_.AttributeName -eq 'member' -and             
      $_.AttributeValue -eq $userobj.DistinguishedName} |            
      Select-Object FirstOriginatingCreateTime, Object, AttributeValue            
    } | Sort-Object FirstOriginatingCreateTime -Descending | Out-GridView


I realize that it looks complicated, but it is practically a one-liner.  Notice the highlighted pieces:

  • You’ll need to provide a username in the appropriate variable.  This can be a short user ID or a distinguished name.
  • The metadata cmdlet needs the switch ShowAllLinkedValues in order to return all of the group membership metadata.  You only need this parameter with AD objects containing linked values.
  • Replace localhost with the FQDN of your nearest DC containing the user account in question.

Note that you will need a Windows Server 2012 domain controller and optionally the AD PowerShell module installed from the Windows 8 RSAT .

When you run this script you’ll get a clean grid view full of dated group memberships.  If any groups are missing in the list, then they have likely not been converted to Linked Value Replication (LVR).


It would be easy to wrap this code into a function or module where you could reuse it for processing a large number of accounts.  You could pipe a list of users into it, and then send the results to a CSV file.  To scale it more efficiently you could simply dump the member metadata for every group in the domain instead of retrieving it multiple times for multiple users.

Do Your Part: Reduce Token Bloat Today

Armed with this code you can now begin the process of reviewing token bloat users and their group memberships.  Hopefully the date information will empower you to remove them from some of those stale groups.  Who knows, you might even be able to get by with a smaller key ring.

get user group membership history.p-s-1.txt