The following is more of a selfish post.It serves mostly as documentation of an issue encountered at work recently - one that I want to be able to just look up for a refresher and not have to google when my brain isn’t working. A coworker of mine was recently tasked with providing one of our client’s a list of the ACLs on their file share due to some work they were doing. This is pretty standard run of the mill stuff, and in the past we have used multiple utilities - some of which produced some pretty ugly output and we would end up having to manually manipulate the data in excel. Thankfully my coworker decided to use powershell to generate the list of ACLs on the file share. Unfortunately he ran into an issue that people probably don’t think about that often when generating a list of ACLs.

Take for example the following permissions on a folder:

alt text

As you can see in screenshot above my test account has List Folder contents as the only permission on it. (Read & Execute is not selected specifically - this will be important in a minute) This user is also (on my test machine) not part of any groups and the local users group and authenticated users groups were removed from the ACL. This was done on purpose to test and validate the behavior seen by my coworker. My coworker reported that they ran the standard get-acl command on a particular folder and piped the results out to a CSV file. In the resulting file they only listed the File systems rights property from the cmdlet to make things easier on our client - and the resulting output showed Read&Execute even though it should be List folder contents only.

Now the immediate response of some people is “This just goes to show that powershell is broken! It’s giving us back invalid data using a built in cmdlet!” And those people would be wrong. In fact powershell is giving you back exactly the RIGHT data in a way that makes sense to computers.

Come again?

Let’s look at the powershell output for the following command (with a little filtering to make it easier to read) for the above test folder. The command in question is simply: get-acl c:\example\ListOnlyACL\

alt text

When providing this list to our clients, as mentioned above, we typically only provide the FileSystemRights. The rest of the properties are not exported out to the CSV file. If I was the client and received the data my coworker sent me - of course I would be upset that a folder that should be list only for a particular group is ReadAndExecute. There is no context as to how inheritance is interacting with the ACLs presented in the report. Based on the data, it would appear that TestUser has ReadAndExecute permissions. And honestly for a client report, unless they request seeing all the data behind the scenes, they should not have to worry about what inheritance flag is on a particular folder - that should all be handled on the report generation side.

If we look at the raw data instead of just the data exported to csv, we see that the FileSystemRights does indeed include ReadAndExecute but if you continue reading you notice that the InheritanceFlags is set to ContainerInherit. ContainerInherit is the same as saying “apply this ACL to this folder and subfolders”. In other words this ACL is never applied to the files within the folders - just the folders. This is necessary so that users are able to traverse and “Execute” (aka open) the folders and show the contents of them. This shows why it is important to list and look at ALL attributes of an object’s output - otherwise you can end up incorrectly declaring that a folder allows a user to execute (open) a file when the user really cannot do that.

A simple - and to be honest slightly ugly - modification to the original command using calculated properties on select (as shown below) gives you the following output:

alt text

This output is not only correct (it was technically correct in the original - but missing part of the data so it appeared incorrect - therefore being incorrect) but also human readable in a way that is quick and easy to understand. Of course the provided select statement below is an example of how we can get a List Only ACL object. As more ACL and Inheritance Flag cases are built up it may be worth while to convert all of these calculations into a function and just call the function as the expression of the calculated property.

Select Statement:

get-acl C:\example\ListOnlyACL\ | Select-Object -ExpandProperty access | where-object{
    $_.identityreference -like "*testuser"
    
} | Select-Object @{
    name = "FileSystemRights"; expression = {
        $acl = $_
        $outputString = $null
        foreach ($item in $acl.filesystemrights.tostring().split(","))
        {
            if ($item -eq "ReadAndExecute" -and ($acl.inheritanceflags -eq "ContainerInherit"))
            {
                $outputString += "List Only,"
            }
            else
            {
                $outputString+= "$($item.tostring().trim()),"
            }
        }
        $outputString.substring(0,($outputString.length -1))
    }
}

So what is the biggest lesson learned from this?
That lesson would have to be to make sure that all properties are accounted for when generating reports, as one property may influence how another property is interpreted. More is better on the report generation side. Data can always be trimmed down on the final output if needed.

As a final note.I’m glad that my coworker is getting more involved with powershell.This is a learning experience for him I’m sure - just like I had more than my fair share of learning experiences in the past.This is in no way making fun of his skills/interpretations - as I have more than likely made even worse mistakes myself.This just happens to be an example that was recently brought up and was hard to find information on for our discussion so I wanted to research and document it myself.

Until then!


Paul DeArment Jr

Systems Engineer, Powershell Enthusiast, Lover Of Automating All Things.