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:

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