PowerShell: SID Walker, Texas Ranger (Part 2)

More bad Chuck Norris one liners:

  1. You don't get access unless Chuck Norris says you get access.
  2. The Chuck Norris array starts and ends on 1, because Chuck Norris is never a zero.
  3. Chuck Norris' PowerShell console NEVER shows red text.

This is part two in a series on translating SIDs in ACLs.  Start by reading Part One if you haven't done so already.  Today we're going to unleash a Chuck Norris round house kick on old SIDs stinking up our file servers.  It's time to take out the trash.

The Scenario

In my former post on SID history I explained…

Tell me if this scenario has ever happened at your company. You are doing an AD migration with the ADMT (or a similar product). You migrated the users and groups. The project schedule was falling behind. You take a shortcut with the member servers. Instead of using the ADMT security translation process you just rejoin the servers to the new domain, leaving all of the old ACLs and SIDs untouched. You leave SID history in place and move on to the next big project and deadline.

The Requirements

We need a solution that will

  • Scan a file server
  • Find all of the NTFS permissions (ACLs)
  • Find the SIDs in those permissions (ACEs)
  • Compare each ACE SID to our SID history mapping file to see if it needs swapped
  • Swap the old SID with the new SID

An Added Twist… The NAS

The ADMT (Active Directory Migration Tool) is the ideal solution, well almost.  An added complexity here is that many companies migrate Windows file servers to a NAS (network attached storage).  The ADMT does security translation by deploying an agent that runs on Windows servers and translates all of the SIDs locally.  Since a NAS is not a Windows server, the ADMT is powerless to translate the SIDs stored there.  The security translation agent cannot run on a NAS.  I know what you're thinking, though.  "From a Windows server just map the NAS as a drive or mount it under a local folder.  Then the ADMT will see it."  That's what I thought, too.  We're both wrong.  After rereading the ADMT guide, trying a few work-arounds, and confirming this internally I have realized that the ADMT simply cannot work with a NAS.  (If you've done it I would love to hear your story.)

Then I turned to a host of utilities that have been around for years:  SIDWALK, SHOWACCS, SUBINACL, and ICACLS.  After hours studying and tinkering with these EXEs I was not able to find a satisfactory fit for our requirements.  Although they support SID translation and some form of an input file each tool was limited in some way or simply too out-dated to be of use.  Now please understand these are still amazing utilities, but for our specific scenario I could not get them to work.

Finally I considered the NAS vendor's ACL migration utility.  Most storage vendors have a suite of file migration utilities, including SID translation.  I found the tools in this case to be simplified and not scalable.  We need more POWER!

Enter PowerShell (aka Chuck Norris)

Our script today will help find and translate those old NTFS SIDs lingering behind on file server ACLs.

Basically the script traverses an NTFS path (local or UNC) and swaps any ACE SIDs that it finds in the SID mapping file.  At the end it outputs a text file verbose log and a CSV with all folders and any SID mappings identified.  Use the WhatIf switch to run in audit mode and check for SID history without making any changes.  In the interest of efficiency we only scan folder permissions; you can modify the script quickly to do both files and folders if desired.  We also ignore owner information in the ACL.

The Round House Kick

The Big O Notation to check every SID in every ACL against every row of the SID mapping file would be quite large.  (This is the problem with some of the EXEs we mentioned earlier.)  The beauty of PowerShell is that we can do the SID mapping quickly with a hash table, and that is the heart of the magic.

First, we read the mapping file into a hash table:

 $SIDMapHash = @{}
Import-CSV $MapFile | ForEach-Object {$SIDMapHash.Add($_.OldSID,$_.NewSID)}

The key If statement buried in the heart of our SID searching loop simply does a Contains method against the hash table:

 If ($SIDMapHash.Contains($ACLEntrySID)) {#We have a winner}

This lightning-fast technique saves us from traversing the entire mapping file each time we search for a SID match.  Each time we find a match we do the old switcheroo with the old SID and new SID in the ACE.

 $SDDLSplit[$i] = $SDDLSplit[$i].Replace($ACLEntrySID,$SIDMapHash.($ACLEntrySID))

When we finish swapping SIDs in the ACEs we write back the change with Set-ACL.

 $acl.SetSecurityDescriptorSddlForm($NewSDDL)            
Set-Acl -Path $acl.path -ACLObject $acl

See the full code in the attached zip file, including sample output.  There is also a fancy progress bar as we scan the folders.  Think of the progress dots as Chuck Norris' foot flying to the target.  I finally followed The Scripting Guy's advice and took the time to add all the bells and whistles... comment-based help, advanced functions, parameter validation, etc.  With each new post I'll build this function library into a full dojo of karate moves.

Explicit Instructions

As always you should test scripts like this in a lab or on a small subset of data until you are comfortable with what it will do in production.  I have also provided a couple backup options in these steps:

  1. Make sure you have a full backup from the night before.
  2. Notify your backup administrator that you could potentially trigger a significant list of file backups if your software treats ACL changes as backup triggers.
  3. For the PowerShell console use Run As with sufficient credentials to edit security on all subfolders in the path specified.
  4. Use ICACLS to save a permissions template so that it can be reapplied in the event of an issue.
  5. Dot-source the script file (dot space dot backslash script file):
       PS C:\>. .\SIDHistory.ps1
  6. For instructions use Get-Help -Full to read about each function:
       PS C:\>Get-Help Export-SIDMapping -Full
       PS C:\>Get-Help Convert-SIDHistoryNTFS -Full
  7. Create a SID mapping file:
       PS C:\>Export-SIDMapping
  8. Review the report files, proceed with WhatIf first.
  9. PS C:\>Convert-SIDHistoryNTFS \\server\share\path -WhatIf
  10. Review the report files, decide to remove WhatIf and commit the changes.
  11. PS C:\>Convert-SIDHistoryNTFS \\server\share\path
  12. Review the report files.
  13. Have your users test file access and login script drive mappings to make sure everyone is still happy.

Coming Up Next

Today's function library is not a full replacement for SIDWALK or ADMT.  It just does NTFS permissions for wherever you point it.  Next time we'll look at how to get a list of Domain SIDs in your environment so that you can interpret where SID history is coming from.  Now go unleash your Norris moves on the NAS.

For More Information

 PS... I'm running out of Chuck Norris lines. Add yours in the comments below, and I'll put them in the next post.

SIDHistory.p-s-1.zip