Goatee PFE

Blog of Microsoft Premier Field Engineer Ashley McGlone featuring PowerShell scripts for Active Directory.

AD Group History Mystery: PowerShell v3 REPADMIN

AD Group History Mystery: PowerShell v3 REPADMIN

  • Comments 16
  • Likes

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!

 

 

REPADMIN PowerShell
  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
  Add-ADDomainControllerPasswordReplicationPolicy
  Remove-ADDomainControllerPasswordReplicationPolicy
  Get-ADAccountResultantPasswordReplicationPolicy
  Get-ADDomainControllerPasswordReplicationPolicyUsage

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).

image

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.

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

Can you help me?  Yes!

If you would like to have me or another Microsoft PFE visit your company and assist with the ideas presented in this blog post, then contact your Microsoft Premier Technical Account Manager (TAM) for booking information.

For more information about becoming a Microsoft Premier customer email PremSale@microsoft.com.  Tell them GoateePFE sent you.

Sharing Links
Comments
  • Excellent post!  I'll be sharing it :)

    Cheers,

    Rhoderick

  • Great article! Even for me who doesn't understand the full capability of PowerShell yet, it made sense!

    Satish

  • If you are using Powershell v3 you shouldn't need to import the active directory module manually.  Powershell will import the module the first time you run a cmdlet from a particular module.

  • And, do you know if Get-ADReplicationAttributeMetadata works in Powershell v3 on a Windows 7/2008R2 system?

  • @LA Richards

    I did a quick check on my Win7 box running PowerShell v3, and it doesn't look like it.  Granted I wasn't surprised to find that given the ActiveDirectory module on that machine didn't get upgraded.  I'm not even sure there are plans to create RSAT tools for Win7 that support Server 2012 unfortunately.  Granted it would be a huge plus if they did release it, but based on past RSAT history I'm not holding my breath.  I guess the alternative would be to remote to a machine that has the updated module.

  • Hi LA,

    Thanks for the feedback.

    You're right that it is not necessary to load the module in PS v3, but I consider it good form.  That way the script is slightly more efficient without searching for the module to load.

    You are also correct that you must have the Windows 8 version of the RSAT to use the new AD module cmdlets (or a Windows Server 2012 box).  I am not aware of any effort to back-port those to Windows 7.

    Keep scripting,

    GoateePFE

  • Do you really need a 2012 DC? Wouldn't a 2012 member server or any server with the new powershell cmdlets installed be sufficient? Or do the 2012 AD cmdlets have to run against a 2012 DC?

  • Hi Fred,

    Great questions. Regarding the AD cmdlets for 2012 please see this article where I discuss the various ways to get them from Windows 7:

    blogs.technet.com/.../how-to-use-the-2012-active-directory-cmdlets-from-windows-7.aspx

    It is true that you cannot install the AD cmdlets for 2012 on Windows 7, but you can remotely import them.  And this link explains how to do it.

    Thanks,

    Ashley

  • Great article Ashley. I am trying to turn this into a function, and it works for one user. But when I want to add the param value [string[]]$name, it fails. From a get-member for the get-aduser command, I see the property name and its value is a string. What am I doing wrong?
    Function Get-ADUserGroupMembershipDetail
    {
    [CmdletBinding()]

    Param (
    [Parameter(Mandatory=$True,
    HelpMessage="Enter the user ID to query.",
    ValueFromPipelineByPropertyName=$True)]
    [ValidateNotNullOrEmpty()]
    [String[]]
    $Name
    )

    $userobj = Get-ADUser $Name
    #Trap {Return "error"}

    Get-ADUser $userobj.DistinguishedName -Properties memberOf |
    Select-Object -ExpandProperty memberOf |
    ForEach-Object {
    Get-ADReplicationAttributeMetadata $_ -Server DC2 -ShowAllLinkedValues |
    Where-Object {$_.AttributeName -eq 'member' -and
    $_.AttributeValue -eq $userobj.DistinguishedName} |
    Select-Object FirstOriginatingCreateTime, Object, AttributeValue
    } | Sort-Object FirstOriginatingCreateTime -Descending | Out-GridView
    }
    Any help would be appreciative since I'm still learning.
    Johnny

  • Hi Johnny,

    Can you provide more detail? How are you calling the function? Is "DC2" a valid DC name in your environment? What is the exact error message?

    Ashley
    GoateePFE

  • Pls give the complete powershell command to get object changes in AD.

  • Works beautifully. Love it

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment