I am working on a project where I need to be able to audit various users and user group permissions on a NTFS formatted Windows file server. I would like to use PowerShell and have it recursively search through the remote file share or it could be ran on the server itself and have it output all of the permissions it finds either for everything or for the specified user or user group. The goal is to be able to use this for periodic auditing of users and user groups to ensure that permission creep is not occurring and that all permissions are being setup in the same manner by different system administrators. Lastly, it would be used for identifying where we need to make changes when we change user groups in group policy. Are there any common ways of approaching this? Does PowerShell stand up to this task? Would it be possible to have PowerShell output the results into a readable format?


I read a brilliant post a few months ago, dealing with a similar situation by essentially running a script remotely that uses the Get-ACL cmdlet to list acl's for a path recursively, and piping the output trough the Export-CSV cmdlet for a nice overview:


| improve this answer | |
  • Thanks for the posting. However, I am not sure that the link provided would allow for one to filter on user or usergroup? Do you know if that is possible? – John Jan 3 '12 at 21:36
  • Modify it to fit your needs? Add an identity parameter and filter with something like (Get-ACL $container.fullname).Access | where {$_.IdentityReference -eq $givenIDParam} – Mathias R. Jessen Jan 3 '12 at 21:45

In this case, I think the Sysinternals AccessChk and AccessEnum tools might be a better fit for what you are looking for. They can be found under File and Disk Utilities here.

| improve this answer | |

Similar to the link Judaslscariot1651 provided. I went more toward taking the snapshot of what I know is good and then comparing it whenever I needed by running a script. I compared the file permissions by outputting to XML what it currently found the permissions to be and then compare that to my baseline XML file using Compare-Object. May not be exact, but just the way I went about doing it...

Note: this was a work in progress at the time and is geared toward particular paths I needed to watch, that I pulled from reading in registry key values. The main gist of what you probably needs starts near "Collecting information on...". I just wanted to provide all of the code to show how/what I was doing.

#Get date/time for file name
$d = Get-Date -format "yyyyMMdd"
# Pull instance names found on server 
$inst = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server').InstalledInstances

$warningpreference = "Continue"

foreach($i in $inst) 
{    $p = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL').$i
    $bin = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$p\Setup").SQLBinRoot
    $ver = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$p\MSSQLServer\CurrentVersion").CurrentVersion
    switch -wildcard ($ver) 
        "9*" {$client = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\90').VerSpecificRootDir; break }
        "10*" {$client = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\100').VerSpecificRootDir; break }

    $currentFile = $i + "_" + $d + ".xml"
    "Collecting information on $bin and $client for $i"
        $bin,$client | foreach { Get-ChildItem $_ -Recurse } |
                    Select FullName, CreationTimeUTC, LastWriteTimeUtc, PSIsContainer, Length,
                    @{Name='Owner';Expression={ (Get-Acl $_.PSPath).Owner }},
                    @{Name='ACLRights';Expression={ (Get-Acl $_.PSPath).Access | Select FileSystemRights }},
                    @{Name='ACLUser';Expression={ (Get-Acl $_.PSPath).Access | Select IdentityReference }},
                    @{Name='ACLAccessType';Expression={ (Get-Acl $_.PSPath).Access | Select AccessControlType}} |
                        Export-Clixml $currentFile
    "Current file is: $currentFile"

    $basefile = "baseline_$i.xml"
    if (Test-Path $basefile)
        $base = Import-Clixml $basefile
        $current = Import-Clixml $currentFile
        #now compare
        $results = "Results_" + $i + "_" + $d + ".txt"
        Compare-Object $base $current -Property CreationTimeUTC, LastWriteTimeUtc, Length, FullName, Owner, AclRights, AclUser, AclAccessType |
            Out-File $results

        #determine if the results file shows any changes, if so wave a flag
        if ((Get-Content $results).Length -eq $null)
            "ZERO Changes found"
            "changes found"
        Write-Warning -Message "WARNING: No base file found to compare."

| improve this answer | |
  • Thanks for sharing, this looks like an excellent idea as well. – John Jan 10 '12 at 22:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.