I was asked for a PowerShell script to remove unresolvable SID’s because of a migration. User accounts didn’t exist on the target machine and the customer wanted to open up the security tab without any wait time and see a cleaned up and ordered ACL list.
Since I adopted PSreadline every maintenance task is scripted in the console so I began with a simple one-liner and eventually extended it to a multiliner merged in a oneliner using ‘;’ which stands for a new PowerShell line. So officially it isn’t exactly a oneliner but I wanted to share it anyway and what better place than the one-line category.
1 |
$arr=@();gci 'C:\YOURPATH' -recurse | % {$path=$_.fullname;$acl=get-acl $path;$sid=$acl.access | ? {$_.identityreference -match "S-1-.*" -and !($_.isinherited)};if($sid){$sid| % {$co=[pscustomobject]@{Path=$path;RemovedSID=$_.identityreference};$co|fl;$arr+=$co;$null=$acl.removeaccessrule($_)};set-acl $path $acl}};'Report';$arr|ft -auto |
First I define an array for the summary to report afterwards. Then I use ‘get-childitem’ (gci), to list all the files in the directory and recurse into every other directory and file underneath it. For each (%) directory or file it sets the full path ($_.fullname) to the path variable. On a new line the ACL for that path is retrieved with ‘get-acl’ and set to the $acl variable. Then again a new line where the access section for that acl is stored in the $sid variable. The access section contains each acl access entry, similar what you see in the security tab. Then the section is filtered with the ‘ ?’ (where) statement so it only saves entries to the $sid variable which matches a SID “S-1-.*” and entries which are not inherited from their parent folder, otherwise removal will fail and is of course not needed. After the $sid variable we define a new PS line with ‘;’ , this line begins with a IF statement, if we find access entry’s with unresolvable SID’s then executed ‘%’ (foreach) for each access entry and save the path and SID in question to the $co PS custom object variable then output the variable in a list with ‘| fl’ (format-list) so we know what the current operation is when the oneliner is executed. There is no scarier thing then staring into the dark! Then the $co variable is added to the $arr (array) variable added in the beginning for the report. A new PS line is entered again, yes again :) , the $null variable is defined to redirect output to NULL for the ‘$acl.removeaccessrule method which removes the rule with the SID access entry from the existing $ACL for that file/folder. The curly bracket closes the foreach loop on the $sid variable and another PS line is entered where we finally set the modified $ACL to the file/folder in question. The curly bracket closes the ” if ($sid)” statement and another curl bracket closes the foreach file/folder generated with ‘get-childitem’ for your path.
This is a somewhat detailed and longer explanation then usual but needed in case you’re wondering what I’m exactly doing. I also added the oneliner as a script to the Technet Gallery and used ISE Steroids for refactoring the oneliner into a much more readable piece of code.
https://gallery.technet.microsoft.com/Remove-orphaned-unresolvabl-2ebca3eb