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 2Please 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 (21)

  1. Anonymous says:

    “DO NOT RUN THIS LINE IN YOUR ENVIRONMENT IF YOU WANT TO KEEP YOUR JOB”

    Maybe next time put that before the line :(

    Just kidding, I’m currently perusing this series in the hope of finding the solution to all my troubles but I’m in fairly uncharted waters for me so I’m treading very carefully… Thanks heaps.

  2. Ashley McGlone says:

    Hi Richard,

    Thanks for the great feedback.  That is good to hear.

    You're not the first to ask for domain/DC targeting with the functions in the module.  It is on my list of future updates.  Thanks for reinforcing this need.

    Ashley

  3. Ashley McGlone says:

    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

  4. Ashley McGlone says:

    Hello Mohit,
    Yes, you can remove them in batches. Notice the Get-SIDHistory commands in the middle of the article. You can combine multiple criteria to get the right target audience, and then you can pipe it to Remove-SIDHistory. I also recommend piping the output again to Export-CSV so that you have a record of what changed.
    Hope this helps,
    Ashley
    GoateePFE

  5. imkottees says:

    Thank you so much Ashley :-) It helped me to remove the SIDHistory from one of the problematic users.

  6. Anonymous says:

    You could also do this in one line :)

    Set-ADObject -Identity -Remove @{sidhistory=""}

  7. Ashley McGlone says:

    Hello Sugit,
    Yes. Please read again the paragraphs above explaining the function Get-SidHistory. You can use it to specifically target only the SidHistory entries desired. Then pipe the result to Remove-SidHistory to remove just those, leaving the others. Like this:
    Get-SIDHistory -DomainSID S-1-5-21-2371126157-4032412735-3953120161 -SamAccountName ashleym | Remove-SidHistory
    Hope this helps,
    @GoateePFE

  8. Richard Adams says:

    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.

  9. worksfine says:

    Hey ! At first, thanks for the great work, i really appreciate this ! but i have also a little problem. i tested it out in our environment and it seemed as if it works fine so far. i made an output file and removed the sid-history of a test-user account.
    "status removed" with the actual date and the old sid was listed there. but if i do another get-sidhistory for that user account, that old sid still appears. i even did a 1on1 copy of each method and line here how to remove the sid. im a domain admin and can
    change everything and every other object / attribute, permissions cant be the problem here. any ideas? :)

  10. Mohit says:

    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

  11. kimberlybryan says:

    GRACIAS PERO NO ENTIENDO NI UNA PINGA HAHAHA.

  12. David@33 says:

    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!

  13. Sujit says:

    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.

  14. Gaurav says:

    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?

  15. Bob says:

    Hi,

    I try to remove the history of one specific group with this line:

    Get-SIDHistory –ObjectClass groupname | Remove-SIDHistory | Export-CSV removed.csv

    When I do this powershell gives the error that the command is incorrect. Any suggestions on which command to use to remove the Sid history are greatly appreciated

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

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

  18. Bob says:

    Thanks Ashley the –SamAccountName line did the trick in removing SID history from a group

  19. Anonymous says:

    A host of reference material for AD and Group Policy

  20. Arunas says:

    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 :(

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