Share via


How To Remove SID History With PowerShell

Update: To see all articles in this series click here.

 

In the United States we celebrate Thanksgiving tomorrow.  No matter where you are in the world let us all give thanks for PowerShell.  If Windows were the pumpkin pie, then PowerShell would be the whipped cream on top.  If Active Directory were the turkey, then PowerShell would be the stuffing.  If Exchange were the mashed potatoes, then PowerShell would be the gravy.  Eating and scripting are complementary passions, so we give thanks for both.

This post is part four in the "PowerShell: SID Walker, Texas Ranger" series on documenting and remediating SID history in your AD forest. Go back and read over the previous articles in the series if you haven't had a chance yet. In today's post we will look at the final step of remediating SID history:  removing the SID history data from our migrated AD objects.  Cleaning up this stale data will greatly reduce the chance of token size issues for your users.

Background

In an earlier post I explained the process for cleaning up SID history, and the last step is actually removing the data from your environment.  This is the last step.  It is crucial that you have already remediated your ACLs, swapping old SIDs for new SIDs.  The preferred method is the ADMT, and then for NAS file shares you can use the function I provided in part 2.  Please note that removing SID history may cause significant impact to your users if you have not taken the time to migrate resource ACLs.  I highly recommend reviewing the ADMT Guide to make sure you have completed all migration steps up to this point.  You have been warned.  Proceed carefully.  As a backout plan make sure you have a good system state backup from two DCs per domain.  Using VBScript to clear SID history was painful. PowerShell achieves the same end with far fewer lines of code.

Get-SIDHistory | Remove-SIDHistory

I decided to divide this function into two parts.  I wanted a dynamic AD query to retrieve users and groups by specific criteria as a way to surgically target the SID history removal.  Then I wanted to pipe the entries returned into a dedicated function for doing the actual removal.  This allows us to craft the right AD query in a safe environment before pulling the trigger on SID history.

Get-SIDHistory

When removing SID history it is best to proceed in phases by department or business unit.  You know the drill.  Start with a few users in IT, then all of IT, then company departments beginning with the least critical business impact (ie. Grounds Keeping before Purchasing).  You must send notification to your users asking them to verify that all of their resource access and drive letters are still good after the change.

Using Get-ADObject to query by name or OU is pretty straightforward, so I won't go into much detail there.  The magic of this function, though, is the -DomainName parameter. You can specify the FQDN of the former domain for which you wish to remove SID history.  Now that is cool!  We can easily target the SID history by domain, because the SID object has a property called AccountDomainSid. We don't even have to parse it out. When we get back the SID history entries, we can filter on this attribute to target a specific domain in the SID history.

In order for this to work you have to first use Export-DomainSIDs to create the DomainSIDs.CSV file that maps the domain names to the domain SIDs.  If you don't know the old domain name, then you can use the -DomainSID switch to remove SID history for any unnamed domain.  You can copy the old domain SID from the SIDHistoryReport.CSV file produced by the function Export-SIDMapping (included in the attached code).  You could also modify the DomainSIDs.CSV file by manually adding any other domain names and SIDs from your own documentation.

Another feature of the Get-SIDHistory query is that it handles the multi-value nature of the sIDHistory attribute.  We do this using the ExpandProperty parameter of Select-Object which I demonstrated here.  This allows us to identify and target specific SID history entries when a user has been migrated more than once.  The ExpandProperty parameter ensures that we get a result row for every SID history entry regardless of the count.

Here is an example without ExpandProperty:

Get-ADUser user1 -Property sIDHistory | Select-Object name, sIDHistory | Format-Table name, sIDHistory –AutoSize

name sidhistory
---- ----------
user1 {S-1-5-21-3013500491-1380372588-2491230877-1122, S-1-5-21-179552...

Here is an example with ExpandProperty:

Get-ADUser user1 -Property sIDHistory | Select-Object name, sIDHistory -ExpandProperty sidHistory | Format-Table name, sIDHistory –AutoSize

name Value
---- -----
user1 S-1-5-21-3013500491-1380372588-2491230877-1122
user1 S-1-5-21-1795525639-2355993942-847153066-1695

Notice that ExpandProperty modifies the property name to "value".  We can rename that at the end of the pipe using Select-Object:

Select-Object DistinguishedName, @{name="SID";expression={$_.Value}}

Get-SIDHistory takes the parameters you specify, shapes them into an AD query, and then uses Invoke-Expression to run it.  In other words, this is code that writes code.  I love that feature of PowerShell.  The query parameters are cumulative.  Therefore you can pile on two or three criteria for laser accuracy.

Here are a few examples of how you can use Get-SIDHistory:

  • Get-SIDHistory –SamAccountName ashleym
  • Get-SIDHistory –DomainName wingtiptoys.com
  • Get-SIDHistory –DomainName wingtiptoys.com –SamAccountName ashleym
  • Get-SIDHistory –DomainSID S-1-5-21-2371126157-4032412735-3953120161
  • Get-SIDHistory –ObjectClass group
  • Get-SIDHistory –SearchBase "OU=Sales,DC=contoso,DC=com"
  • Get-SIDHistory –MemberOf MigratedUsers
  • Get-SIDHistory | Measure-Object

Experiment with different combinations of parameters until you isolate exactly the targets you need.  Then you can script out multiple statements for each phase of the remediation.

Remove-SIDHistory

This short TechNet article provides syntax for removing SID history:

Get-ADUser –filter 'sidhistory –like "*"' –searchbase "dc=name,dc=name" –searchscope subtree –properties sidhistory | foreach {Set-ADUser $_ -remove @{sidhistory=$_.sidhistory.value}}

That single line of PowerShell is a "resumé-generating-event".  DO NOT RUN THIS LINE IN YOUR ENVIRONMENT IF YOU WANT TO KEEP YOUR JOB.  Wiping SID history for every user all at once is not a best practice (in case you were wondering).

You can call Remove-SIDHistory by specifying the distinguishedName of the object and the SID entry to remove.  However, that is a lot of complicated typing.  The intention is to use Get-SIDHistory to tailor your query to exactly the results you want, and then simply pipe it to Remove-SIDHistory.  This works through a feature of PowerShell advanced functions called cmdlet binding.  The ValueFromPipelineByPropertyName argument allows us to pipe the output from one function into the next as long as the first output properties match the second input parameters. Features like this make PowerShell truly remarkable.

Get-SIDHistory –SamAccountName ashleym | Remove-SIDHistory

I recommend piping the output of the Remove-SIDHistory to a CSV file for change documentation.  Like this:

Get-SIDHistory –SamAccountName ashleym | Remove-SIDHistory | Export-CSV removed.csv

Your CSV will include a date/time stamp for each entry's removal:

image

This documentation will come in handy if users start to call and report issues after the removal.  But that shouldn't happen if you are thorough in scrubbing all SID history dependencies prior to removing the data. Please see the update at the end of this article.

Conclusion

If you've been putting off SID history remediation due to the mystery of how to remove it, then your puzzle is solved.  Using the functions in today's post will make the removal process swift and painless.  Let me reiterate that this is the LAST step in the migration process.  Do not proceed until all of your resource translation is complete and verified.  Phase the remediation by user groups to limit any unforeseen impact.  Following these recommendations will make the transition as smooth as possible.

So if you are celebrating Thanksgiving with family tomorrow and everyone around the table shares something they are thankful for, you can be the geek that is thankful for PowerShell.  Most likely no one else at the table will know what you're talking about.  Just smile and nod.

Coming Up Next…

In the next installment of our SID history series we'll provide a handy summary of each post in the series.  We will also take the function library we've been using and upgrade it to a PowerShell module.  Then we'll walk through the entire SID history remediation process using the provided cmdlets in this module.

Update 10 December 2015: Take an AD snapshot before major changes like this

Hi, folks. Yesterday we got a support case from someone who followed the guidance in this article, except the part about verifying that ALL ACLs in their environment had been migrated. There was pain involved for thousands of accounts at this particular company. SID history was still in use on some Windows file servers and Exchange.

To make recovery faster in situations like this it is very helpful if you take an AD snapshot prior to massive directory changes or updates. Before you remove any SID history in your environment please follow my guidance in this article to take an AD snapshot for data recovery purposes. You cannot easily re-write the sidHistory attribute, but at least you'll have a copy of the data to work with and build a SID mapping file to finish the job.

SIDHistoryV1.2.zip

Comments

  • Anonymous
    August 22, 2013
    This is great PoSh code.  Thank you so very much for putting all this together.  I work on several AD migration projects a year and always find it useful to add to my "toolbox".  This is going on the top of my list! :) I would like to recomend an update though.  Often times I am in the Source domain/forest running operations against the Target domain/forest.  It would be very useful to have some way to specify the preferred DC that you want to run the cmdlets against.  I hope this feedback helps you as much as your code has helped me.

  • Anonymous
    August 23, 2013
    The comment has been removed

    • Anonymous
      January 04, 2017
      +1
  • Anonymous
    December 19, 2013
    The comment has been removed

  • Anonymous
    February 20, 2014
    The comment has been removed

  • Anonymous
    February 21, 2014
    Hi worksfine,
    Sorry to hear you've run into a snag. Please use the Email Blog Author link at the top right of this page to send me a transcript of your PowerShell session reproducing the problem. (Start-Transcript / Stop-Transcript). Also, what operating system versions and PowerShell versions are involved in your tests?
    Thanks,
    Ashley
    @GoateePFE

  • Anonymous
    March 05, 2014
    Hello Ashley,

    I was wondering if there is way to delete Sid history for selected users after the migration in batches.
    I have recently completed a migration of about 2500 users and now client wants to proceed to Sid history cleanup, but wants to get it done in smaller user batches.
    Please advise on how to proceed for this one.

    Thanks
    Mohit

  • Anonymous
    March 06, 2014
    The comment has been removed

  • Anonymous
    March 25, 2014
    Thank you so much Ashley :-) It helped me to remove the SIDHistory from one of the problematic users.

  • Anonymous
    August 16, 2014
    The comment has been removed

  • Anonymous
    February 16, 2015
    Thanks a lot Ashley ;-) this module is very helpful. It helped me to remove SIDHistory from one user in exchange migration and mailbox delegation issue. You save me a lot of time!

  • Anonymous
    April 30, 2015
    The comment has been removed

  • Anonymous
    May 28, 2015
    Hi Ashley
    Is it possible to remove one single SID from the SIDHistory property.
    Simple example : Use A has 2 SIDs in SIDHistory property. Can i remove one SID and leave the other SID.

  • Anonymous
    May 28, 2015
    The comment has been removed

  • Anonymous
    June 03, 2015
    Hello Ashley,

    I have been following your articles and they are great help. We are working on creating a migration tool which allows cross forest migration we are stuck at a point where we need to migrate the SIDHistory from one forest to another with the help of Shell script without using ADMT. Do you think this is something you can guide us with?

  • Anonymous
    June 09, 2015
    The comment has been removed

  • Anonymous
    June 11, 2015
    Hi Bob,
    Try this instead:
    Get-SIDHistory –SamAccountName groupname | Remove-SIDHistory | Export-CSV removed.csv
    Let me know if that works.
    GoateePFE

  • Anonymous
    June 11, 2015
    Hello Gaurav,
    I may be able to help. Please use the "Email blog author" link at the top right of this page to contact me directly.
    Thanks,
    GoateePFE

  • Anonymous
    June 12, 2015
    The comment has been removed

  • Anonymous
    October 08, 2015
    A host of reference material for AD and Group Policy

  • Anonymous
    October 14, 2015
    I can get Sidnumber with get-sidhistory -samaccountname name , but
    command combination - Get-SIDHistory -DomainSID Sidnumber -SamAccountName name | Remove-SidHistory
    don't work :( It don't get any error, command just completes without any output :(

  • Anonymous
    October 16, 2015
    Hello Arunas,
    Your SID history query is returning no results, so no error. Make sure that when you supply the DomainSID that it is the SID for the domain, not a specific user or group.
    Hope this helps,
    Ashley
    GoateePFE

  • Anonymous
    February 28, 2018
    The comment has been removed