I occasionally find myself needing to determine which folders or files a user or group does or does not have access to.  With nested security groups things can get even more tricky.

I ended up writing get-ntfsAccess, a quick function that will do this for me.  I feed in one or more paths to test, one more users/groups, and I get back either all the folders they have access to, or all the folders they don’t have access to.  The function does the following:

  1. If specified, define and run Get-NestedGroups on the provided entities to provide all nested security groups
  2. Check ACL of each path for any ACE matching the users/groups or their nested security groups

Define and run Get-NestedGroups

This function is a bit longer than the Get-ntfsGroups function it is defined in.

$ADObjectDetails = Get-ADObject -filter "samAccountName -eq '$ADObject'" -Properties memberOf

Get the memberOf property for the object – we need this to determine if it’s a group or another object.

$ADObjectDetails | select -expand memberof | foreach {
     $subGroups += Get-adgroup $_ | select -ExpandProperty samaccountname

If it isn’t a group, we want to know what security groups provide access.  Loop through memberOf and get the samAccountName for each of these groups

if($group){  $subGroups = ( Get-ADGroupMember -Identity $ADObject | where {$_.objectClass -like "group"} ).samAccountName  }

If it is a group, we only care what groups are inside the security group

#If there are sub groups, recurse through them

    #add results of current query
    $allNestedGroups = [string[]]$subGroups

    #initialize subSubgroupscollection
    $subSubGroupsCollection = [string[]]""

    #look for subsubgroups in each sub group
    foreach($subgroup in $subgroups){
        #Run query, return query results for verbose output, then add it to a collection
        $subSubGroups = Get-NestedGroups -ADObject $subgroup -nestLevel $nestLevel
        if($subSubGroups){ write-verbose "$tabs Level $nestLevel $subgroup returned the following groups:`n$( $subSubGroups | out-string)" }
        $subSubGroupsCollection += $subSubGroups

    #add results from subgroups only if they aren't blank or already included
    $allNestedGroups += $subSubGroupsCollection | ?{$_ -and $allNestedGroups -notContains $_}


    #If we hit an empty group, return with nothing

#Once we've recursed through all groups, return results
Write-Verbose "$tabs Level $nestLevel Returning from $ADObject with following nestedGroups:`n$( $allNestedGroups | Out-String)"

After this, we add each group in subGroups to allNestedGroups, and recursively call this function on each of those groups

Check out the innards of Get-ntfsAccess on Script Center for the full definition of the function.

Check ACL of each path for any matching ACE

#Loop through each folder
foreach($folder in $folderlist){

    #Get ACL for the folder
    write-verbose "Checking access for $folder"
    $accessList = (get-acl $folder).access

    #Set access to null - used for determining noAccess
    $access = $null

    #For each access item in the ACL
    foreach($accessItem in $accessList){

        #Identify the group and track overall access for -noaccess parameter
        $accessItemGroup = $accessItem.IdentityReference.Value

        #loop through groups we are searching for
        foreach($group in $entity)
            #if we match a group...
            if($accessItemGroup -like $group)
                #add it to a list, if listOnly or noAccess
                    #add the result unless $noAccess is specified
                    if(-not $noAccess){ $folderResults += $folder | where { $folderResults -notcontains $_ } }
                    $access = 1
                    write-verbose "Access for $folder"
                #otherwise, add an object with access information to results
                    #add the result unless $noAccess is specified
                    if(-not $noAccess){
                        $folderResults += [pscustomobject] @{
                            Path = $folder;
                            Group = $accessItemGroup;
                            FileSystemRights = $accessItem.FileSystemRights;
                            AccessControlType = $accessItem.AccessControlType;
                            IsInherited = $accessItem.isInherited;
                            InheritanceFlags = $accessItem.InheritanceFlags;
                            PropagationFlags = $accessItem.PropagationFlags
                    $access = 1
                    write-verbose "Access for $folder"

    #if we didn't find a group matching input, $access is still $null
    if(-not $access -and $noAccess){
        $folderResults += $folder
        write-verbose "No access for $folder"


This is the final piece of the code.  I tried to comment things out to show what’s going on.

We have all the users/groups and folders (or files) to test.  The first stage looks at a single path and gets the ACL.  The next stage looks at each ACE in the ACL to see if it matches the users/groups we are interested in.  When it does, add it to the results (if checking for access) or note that we do have access (if checking for no access).  Once we’ve checked all ACEs in an ACL, if nothing matched the group we note that there is no access (if checking for no access).

The function in action

Pick up the script here and make sure you are running PowerShell 3

I start out with C:\temp:

Everything has default inherited ACEs, apart from one I added for a group nested under VSR VDI PFS:

Perhaps I only want a list of folders the provided entities have access to:

Lastly, the folders that VSR VDI PFS does not have access to (all but one):

Bonus:  You can run this on network shares

My next step is to add a parameter that controls the depth of path recursion.  As it is, I’m using the built in Get-ChildItem -Recurse parameter, but I could see situations where you only care about the first few levels of directories.

On an aside, Raimund Andrée wrote a fantastic module that covers managing permissions beyond get and set-acl – check it out here.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s