Identifying NTFS Folder Permission Differences

I’ve taken on the task of doing folder/file cleanup on our NAS and I’ve run into a scenario where I’m definitely thinking that Powershell can really help. To note, my PS experience is limited at best. I’m familair with some of the AD/Office 365 cmdlets from my daily tasks but do not have a ton of experience when creating advanced scripts.

I have a parent folder on our NAS with a large amount of access entries on it. This parent folder has hundreds of subfolders. I’m doing some organization of the access entries, but over the years, inheritance has been broken on several subfolders and ACLs have been changed. I’d like to develop a script (or be pointed towards a good resource) that I can recursively run against my parent folder and get a list of subfolders where the ACL is different than that of the parent folder.

I’m thinking that I can leverage both the Get-ChildItem and Get-Acl cmdlets to accomplish what I want but I’m having some difficulty in bringing it all together.

I appreciate any advice that you can share with me or resources that you can point me to.

Thank you!

Get-ACL will get you a permission object; I’d get it from the parent and store it in a variable.

From there, Get-ChildItem can get you the child folders, which can be piped to Get-ACL to get their permission object. I’d probably do that in a loop.

$parent_acl = get-cal c:\parent
foreach ($folder in (get-childitem c:\parent -directory -recurse)) {
  $folder_acl = get-acl $folder
  compare-object $parent_acl $folder_acl
}

That - or something similar - will give you a difference object for each child whose ACL is different from the parent’s.

But… Get-ACL only shows direct ACEs, not inherited ones. So a child folder with no ACEs will show as “different” from the parent, because it’s inheriting from the parent, which is in fact what you wanted. So in that case, “different” is good, not bad.

It might be worth thinking carefully about what, exactly, you want to look at and mitigate, before you go down the path of a lot of coding.

Hey Don …Nice logic and code. Awesome…
Just wanted to understand David’s requirement here…
Do you want the script to ask for a path of folder and should match the ACL with its parent folder ?

Thank you, Don. Really like how this came together.

Deepak…it doesn’t necessarily need to prompt for a folder path. I can supply the top level parent directory. I’m just looking for it to recursively move through all of the subfolders and determine the difference between each one of them against what the ACLs on the top level parent folder are.

What about something like this?

Get-ChildItem C:\folder -Recurse | ForEach-Object {
    try { # Get-Acl throws terminating errors, so wrap it in a try/catch block
        $_ | Get-Acl | 
            where { $_.AreAccessRulesProtected -or ($_.Access | where { $_.IsInherited -eq $false }) } |
            foreach { Convert-Path $_.Path }
    }
    catch {
        Write-Error $_
    }
}

That should return the path to any files/folders (depends on how you call Get-ChildItem) where DACL inheritance is disabled or a single non-inherited ACE is present. It would be a much prettier command if Get-Acl handled errors properly and you didn’t have to put it in that ForEach-Object block…

With a little tweaking, you could get it to show you the differences instead of just giving you the paths.

Just created below one. The script might not be clean :(…The number of lines in code can be reduced. But I just started with PS.

The script will check if inheritance is broken in any directories and list the ACL of current and its parent. I am not sure if it helps you.

I am looking for the suggestions from experienced guys to get this cleaned …Thanks…

# Give the path of directory below
$DirDetails = Get-ChildItem -path C:\Work\Test -Recurse -Attributes Directory

ForEach ($Dir in $DirDetails) {
$DirFullName = $Dir.FullName
$DirName = $Dir.Name
$Acl = Get-Acl $DirFullName
$AclAccess = $Acl.Access
$InheritedList = $AclAccess.Isinherited
[int]$BadFolderCount = "0"
foreach ($ISInherit in $InheritedList) {
If ($ISInherit -like "False") {
$BadFolderCount++
}
}
If ($BadFolderCount -gt "0") {
Write-Host "Directory Name                      :$DirFullName" -ForegroundColor Red
Write-Host "Count of Not Inherited ACLs         :$BadFolderCount" -ForegroundColor Red
Write-Host ""
Write-Host "ACL of Current Directory            :"
(Get-Acl $DirFullName).Access | select FileSystemRights , IdentityReference | fl
Write-Host "ACL of Parent Directory             :"
(Get-Acl ($DirFullName.TrimEnd($DirName))).Access | select FileSystemRights , IdentityReference | fl
}
Else {
Write-Host "Directory Name                      :$DirFullName" -ForegroundColor Green
Write-Host "Count of Not Inherited ACLs         :$BadFolderCount" -ForegroundColor Green
}
Write-Host ""
}

By the way Rohn Edwards , wonderful code.
PS code should be like this. Short and powerful :slight_smile:

Try this:

$DirDetails = Get-ChildItem -path C:\Temp\* -Recurse -Attributes Directory

$Acls = ForEach ($Dir in $DirDetails) {
    Get-Acl $Dir.FullName |
        Select Owner,
               AccessToString,
               @{Name="Non-Inherited Perms";Expression={($_.Access | Where{$_.IsInherited -eq $false}) | foreach{"{0}: {1}" -f $_.IdentityReference, $_.FileSystemRights}}},
               @{Name="Non-Inherited Count";Expression={($_.Access | Where{$_.IsInherited -eq $false}).Count}},
               @{Name="DirFullName";Expression={$Dir.FullName}},
               @{Name="DirName";Expression={$Dir.Name}}
}

$Acls

Your script is using Write-Host, which isn’t a good practice to begin with. The Power of Powershell comes from working with objects. In your script, you’re just writing information to the prompt, which tells you what is wrong but you have to dig thru output to understand how many directories have non-inherited permissions. If I use the script I posted, I just have to do a simple query to see what folder are not set the way I want:

$Acls | Where{$_."Non-Inherited Count" -gt 0}

Additionally, if I want to fix them, it’s a simple as:

$Acls | Where{$_."Non-Inherited Count" -gt 0} | foreach{
    Set-ACL $_.DirFullName ...
}

So, I highly recommend trying to keep your data in an object versus writing it in colors to the screen.