locked
Log Errors and failures from script RRS feed

  • Question

  • My end goal is to find what groups have rights to which data path's. Now I don't have access to everything and the root data path has a lot of data and many different levels so I want to limit how many levels down I look in the data path as well.

    I've written the following script to get the permissions and only go three levels deep. I'm having a issue with logging the errors I get and I'm hoping someone can help. If someone else has an more effecient way to accomplish this I'd be very interested in learning about it.

    Thanks to anyone and everyone that posts I do appreciate the help.

    $Passed = @() $Failed = "C:\TEMP\Perm-Failed.txt" $RootPath = "\\Data\Path" @(gi $RootPath;gci $RootPath ;gci $RootPath\*; gci $RootPath\*\*; gci $RootPath\*\*\*)|where{$_.psiscontainer -eq $True}| foreach-object { $FilePath = $_.fullname Try { $Passed += get-acl $_.fullname |select @{n='Path';e={Split-Path $_.Path-NoQualifier}} -expand access | select Path, IdentityReference, FileSystemRights, IsInherited #select Path, IdentityReference, FileSystemRights, IsInherited, pschildname, pspath, accesstostring } Catch { $FilePath | Add-Content $Failed } } $Passed | export-csv "C:\temp\Perm-4-atcgroups-data.csv"  

    Here are some examples of the errors I'm trying to get: Get-ChildItem : Access to the path '\\Data\path\Sub-folder\someCharts' is denied. Get-Acl : Attempted to perform an unauthorized operation Get-ChildItem : Logon Failure: The target account name is incorrect. Get-ChildItem : The network path was not found.

    I did find this link and the post from "Kazun" but haven't been able to get it to work.
    http://social.technet.microsoft.com/Forums/windowsserver/en-US/59558a13-6c46-4aeb-b152-96ce19467267/error-handling-during-getacl-access-denied?forum=winserverpowershell

     


    Thanks in Adavance



    Tuesday, February 25, 2014 7:31 PM

Answers

  • $AccessErrors won't contain CSV data. Earlier, I was suggesting exporting the results of Get-EffectiveAccess to a CSV. $AccessErrors should be able to be piped to Out-File, though. In your code, I'm guessing that the errors are going to occur from the dir call and the Get-Acl call, so you're not actually catching the errors. Let's do it this way, and redirect the errors instead of capturing them (since you're going to be running this against a lot of folders):

    $OriginalErrorView = $ErrorView
    $ErrorView = "CategoryView"  # Gives a more friendly error format
    
    $GciErrors = "c:\Test-Perm\gci_errors.txt"
    $GetAceErrors = "c:\Test-Perm\getace_errors.txt"
    
    $RootPath = "\\data\path"
    dir @("$RootPath\*"; "$RootPath\*\*"; "$RootPath\*\*\*") 2> $GciErrors | 
        where { $_.PsIsContainer } |  
        Get-AccessControlEntry 2> $GetAceErrors |
        Export-Csv "C:\Test-Perm\Test-Perm.csv"
    
    $ErrorView = $OriginalErrorView


    The 2> means don't write the errors to the screen, write them to the file that's specified (see about_Redirection help topic). You should be able to do the -ErrorVariable in place of redirection, but remember to assign an error variable to any functions that may produce errors.

    Also notice that Get-Acl wasn't necessary in that call. Get-Ace can take the objects directly (and it has to to view the ACEs for shares, services, printers, WMI namespaces, AD objects (which will be in the next version), etc.

    Let me know if you have any questions or issues.

    Edited to add:

    Actually, if you use -ErrorVariable, the error record output does do pretty well in CSV format. I'd never attempted to save errors out like that, so disregard the first thing I said in this reply :)

    • Edited by Rohn Edwards Wednesday, February 26, 2014 12:32 AM
    • Marked as answer by John-Barrett Friday, March 7, 2014 3:25 AM
    Wednesday, February 26, 2014 12:24 AM

All replies

  • This isn't what you're specifically asking for, but would the effective access be of interest? This module has a function called Get-EffectiveAccess that lets you pass different users and see the effective permissions they have. It can also break each permission down the way that the 'Effective Access' tab in the ACL Editor GUI does. Unfortunately, passing groups doesn't usually work, and shows no access (the same thing happens from the 'Effective Access' tab in the GUI), but maybe it would still be useful.

    Here's a sample command you could try:

    dir $RootPath -Recurse | where { $_.PsIsContainer } | 
        Get-EffectiveAccess -Principal user1, user2 -ErrorVariable AccessErrors -ErrorAction SilentlyContinue

    The errors from that command should be stored in a variable called $AccessErrors. The results from the command could be piped to Export-Csv or Out-File.

    You can also use the -ListAllRights switch if you're looking at one or two objects at a time (you can actually use it in the previous example, but you're going to get more info than you can handle). If you don't call it with any principals, it will default to checking the current user's effective access.

    Tuesday, February 25, 2014 8:07 PM
  • @Rohn

    Thanks for the post/reply and the information. I'm really more interested in the groups more than the users. The second thing is I really don't want to use the -recuse switch just because it will look a lot father than I want to look. Just to give you an idea of how much data I talking about just looking 3 level deep might take a couple of hours. When I first started looking into this I tried using the -recuse swith and after 3 or 4 days it was still running.

    I found this script as well, but I forgot where I found it that I think will do the same thing that your module will do, but I'm not positive/sure.

    #Prompt for input (username, root folder)
    $User = Read-Host "Enter the username to check"
    $RootDir = Read-Host "Enter the root directory to check"
    #Get list of subdirectories. We just need the Fullname (Complete path).
    $Dirs = @(gci $RootDir -recurse) | Where {$_.mode -match "d"} | Select Fullname
    #Create array to store results
    $Compiled = @()
    forEach ($Dir in $dirs)
    {
       #Get the ACL for the current subdirectory. Format it in a way we can work with.
        $ACL = Get-Acl $dir.fullname
        $List = @($ACL.access | Select IdentityReference,AccessControlType,FileSystemRights)
       #Filter for $user, remove List and ACL arrays
        $Filtered = $List | Where {$_.identityreference -match $user} 
        Remove-item variable:ACL
        Remove-item variable:List
        
       #Create an object for each line in the above $List.
        forEach ($line in $Filtered)
        {
            $Object = New-Object PSObject -property @{
                "Directory" = $dir.Fullname
                "User" = $line.IdentityReference
                "AccessControl" = $line.AccessControlType
                "Rights" = $line.FileSystemRights
                }
           #Add this object to the $Compiled array.    
            $Compiled += $Object
        }
    }
    #Save the report.  You could add a Read-Host statement here to prompt for a filename/location, if you want.
    $Compiled | Select Directory,user,accesscontrol,rights | Sort Directory | Export-CSV -notypeinformation "C:\Scripts\PS-Scripts\Permissions\Test\Permissions_Test.csv"


    Thanks in Adavance

    Tuesday, February 25, 2014 8:47 PM
  • The Get-AccessControlEntry function from the same module should be able to handle that, then. It will also let you filter on inheritance, principal, rights, etc. Here are some sample commands to do that (NOTE: I'm just doing a dir w/o filtering on folders or going any deeper. All you have to do is replace the dir command with the calls you were doing in your example to get three levels deep):

    # Show all ACEs
    dir $Root | Get-AccessControlEntry
    
    # Show ACEs that aren't inherited
    dir $Root | Get-AccessControlEntry -NotInherited
    
    # Show ACEs that apply to Users group or SYSTEM account (the
    # -NotInherited switch could still be used here):
    dir $Root | Get-AccessControlEntry -Principal Users, SYSTEM
    
    # Show ACEs that apply to Users group or SYSTEM account that
    # have Write access (this isn't checking where the rights are
    # equal to 'WriteData', it's checking where the 'WriteData' 
    # bit is enabled, i.e., FullControl and Write permissions
    # would trigger this check)
    dir $Root | Get-AccessControlEntry -Principal Users, SYSTEM -FolderRights WriteData
    

    If you pipe that to 'Format-List *' or Export-CSV, you'll see that each ACE has the path attached as a property, too.

    Tuesday, February 25, 2014 9:01 PM
  • @Rohn

    Now the:

    -ErrorVariable AccessErrors -ErrorAction SilentlyContinue
    part of your post might be more helpful to me. I don't think I understand how to export "$AccessErrors into a csv or out-file? Would it be something like ????
    $Passed = @()
    $Failed = "C:\TEMP\Perm-Failed.csv"
    $RootPath = "\\Data\Path"
    #@(gi $RootPath;gci $RootPath ;gci $RootPath\*; gci $RootPath\*\*; gci $RootPath\*\*\*)|where{$_.psiscontainer -eq $True}|-ErrorVariable AccessErrors -ErrorAction SilentlyContinue
    @(gci $RootPath)|where{$_.psiscontainer -eq $True} -ErrorVariable AccessErrors -ErrorAction SilentlyContinue| export-csv $Failed
    foreach-object {
    $FilePath = $_.fullname 
        Try 
        { 
         $Passed += get-acl $_.fullname |select @{n='Path';e={Split-Path $_.Path-NoQualifier}} -expand access | select Path, IdentityReference, FileSystemRights, IsInherited #select Path, IdentityReference, FileSystemRights, IsInherited, pschildname, pspath, accesstostring
        }
        Catch 
        {
         $FilePath | Add-Content $Failed
        }
    }
    $Passed | export-csv "C:\temp\Perm-test.csv"

    When I tried this it just created a blank csv file

    Thanks in Adavance

    Tuesday, February 25, 2014 9:15 PM
  • @Rohn

    Sorry about my last post I replied before I refreshed the page. Let me look further into your module that you suggested.


    Thanks in Adavance

    Tuesday, February 25, 2014 10:09 PM
  • @Rohn

    Thanks again for pointing me to your module. I got your module installed and started playing with it. Its pretty slick, wish I would have found it sooner :-)

    $RootPath = "\\data\path" # any UNC will do
    dir @("$RootPath\*"; "$RootPath\*\*"; "$RootPath\*\*\*") | where { $_.PsIsContainer } | Get-Acl | 
    Get-AccessControlEntry -ErrorVariable AccessErrors -ErrorAction SilentlyContinue |
    select DisplayName, IdentityReference, AccessMask,IsInherited |
    Export-Csv "C:\Test-Perm\Test-Perm.csv"
    $AccessErrors | Export-Csv C:\Test-Perm\Test-Perm-Errors.csv

    I'm still having a issue with getting "AccessErrors" to work. Any ideas on what I'm doing wrong?


    Thanks in Adavance

    Tuesday, February 25, 2014 11:34 PM
  • $AccessErrors won't contain CSV data. Earlier, I was suggesting exporting the results of Get-EffectiveAccess to a CSV. $AccessErrors should be able to be piped to Out-File, though. In your code, I'm guessing that the errors are going to occur from the dir call and the Get-Acl call, so you're not actually catching the errors. Let's do it this way, and redirect the errors instead of capturing them (since you're going to be running this against a lot of folders):

    $OriginalErrorView = $ErrorView
    $ErrorView = "CategoryView"  # Gives a more friendly error format
    
    $GciErrors = "c:\Test-Perm\gci_errors.txt"
    $GetAceErrors = "c:\Test-Perm\getace_errors.txt"
    
    $RootPath = "\\data\path"
    dir @("$RootPath\*"; "$RootPath\*\*"; "$RootPath\*\*\*") 2> $GciErrors | 
        where { $_.PsIsContainer } |  
        Get-AccessControlEntry 2> $GetAceErrors |
        Export-Csv "C:\Test-Perm\Test-Perm.csv"
    
    $ErrorView = $OriginalErrorView


    The 2> means don't write the errors to the screen, write them to the file that's specified (see about_Redirection help topic). You should be able to do the -ErrorVariable in place of redirection, but remember to assign an error variable to any functions that may produce errors.

    Also notice that Get-Acl wasn't necessary in that call. Get-Ace can take the objects directly (and it has to to view the ACEs for shares, services, printers, WMI namespaces, AD objects (which will be in the next version), etc.

    Let me know if you have any questions or issues.

    Edited to add:

    Actually, if you use -ErrorVariable, the error record output does do pretty well in CSV format. I'd never attempted to save errors out like that, so disregard the first thing I said in this reply :)

    • Edited by Rohn Edwards Wednesday, February 26, 2014 12:32 AM
    • Marked as answer by John-Barrett Friday, March 7, 2014 3:25 AM
    Wednesday, February 26, 2014 12:24 AM
  • I ran the above script and it was running along just fine but it stoppped with the following error code:

    Get-Acl : Attempted to perform an unauthorized operation.

    I'm also still stumped on how to export AccessErrors and why it would stop? Again any and all help is apprecitated
    • Edited by John-Barrett Wednesday, February 26, 2014 1:28 AM
    Wednesday, February 26, 2014 1:18 AM
  • Hmmm. Not sure what's going on. Is there any data in any of the three files? You might run it this way to see where it's stopping:

    dir @("$RootPath\*"; "$RootPath\*\*"; "$RootPath\*\*\*") | 
        where { $_.PsIsContainer } |  
        Get-AccessControlEntry
    

    That should put everything to the screen, so you can see the last thing it stops on. Is there anything that jumps out when it stops?

    Wednesday, February 26, 2014 1:29 AM
  • Rohn,

    Thanks again for all your help on this I really do appreciate it. Thanks for explaining about the "2>" part as well. The more I learn about powershell the more I like it and the more I realize how much I don't know. I put the "get-acl" in the code because of the method 2 example from the repository example. As for your question there is could be data but its not a guarantee there is, I just don't know. I'm running the two scripts now and it seems to be going along fine now. I'll post again if I run into another issue.


    Thanks in Adavance

    Wednesday, February 26, 2014 7:16 PM
  • Not a problem at all. Thanks for pointing out the old example I have on the repository. I'll have to fix that (it's from a very old version of the module). If you come across any issues or suggestions for the module, just let me know on the Q&A section on the repository page.
    Wednesday, February 26, 2014 7:20 PM
  • Rohn - nice utility.  Fixes would be nice.


    ¯\_(ツ)_/¯

    Wednesday, February 26, 2014 10:06 PM
  • Thanks, jrv. I'm glad you like it. Are you having an issue with the module?
    Wednesday, February 26, 2014 10:33 PM
  • Thanks, jrv. I'm glad you like it. Are you having an issue with the module?

    no but you said you had to fix it.


    ¯\_(ツ)_/¯

    Wednesday, February 26, 2014 10:48 PM
  • Rohn,

    This is kind of a minor thing, but I'm hoping you can help. Below are some examples of what I'm getting in the error logs

    From getace_errors.txt: NotSpecified: (:) [Write-Error], WriteErrorException

    From gci_errors.txt: PermissionDenied: (\\data\path\sub-folder\White Paper:String) [Get-ChildItem], UnauthorizedAccessException PermissionDenied: (\\data\pat...READ THIS PAPER:String) [Get-ChildItem], UnauthorizedAccessException

    Is there a way I can get the the full path from where the issue is happening in the getace_errors.txt? In the gci_errors.txt I get the full path for every error as well. Its not that big of a deal because when it does have the "..." in the path its been in the same path as the line above it. I'm guessing to get the full path in the gci_errors.txt file I would I just have to do something like ?

    dir @("$RootPath\*"; "$RootPath\*\*"; "$RootPath\*\*\*") 2> $GciErrors -width 200

    Just an FYI, its continues to run when it does run into an issue.

    The script did also out put this as well: (gotta admit you have a pretty cool module)

    WARNING: The access rules for '\\Data\that\sub-flder\2nd-Subfolder' are not in canonical order. To fix this, please run the 'Repair-AclCanonicalOrder' function.
    Last thing, do you have any recomendation for learning about writting modules?

    Thanks in Adavance

    Thursday, February 27, 2014 12:06 AM
  • Rohn,

    This is kind of a minor thing, but I'm hoping you can help. Below are some examples of what I'm getting in the error logs

    From getace_errors.txt: NotSpecified: (:) [Write-Error], WriteErrorException

    From gci_errors.txt: PermissionDenied: (\\data\path\sub-folder\White Paper:String) [Get-ChildItem], UnauthorizedAccessException PermissionDenied: (\\data\pat...READ THIS PAPER:String) [Get-ChildItem], UnauthorizedAccessException

    Is there a way I can get the the full path from where the issue is happening in the getace_errors.txt? In the gci_errors.txt I get the full path for every error as well. Its not that big of a deal because when it does have the "..." in the path its been in the same path as the line above it. I'm guessing to get the full path in the gci_errors.txt file I would I just have to do something like ?

    dir @("$RootPath\*"; "$RootPath\*\*"; "$RootPath\*\*\*") 2> $GciErrors -width 200

    Just an FYI, its continues to run when it does run into an issue.

    The script did also out put this as well: (gotta admit you have a pretty cool module)

    WARNING: The access rules for '\\Data\that\sub-flder\2nd-Subfolder' are not in canonical order. To fix this, please run the 'Repair-AclCanonicalOrder' function.
    Last thing, do you have any recomendation for learning about writting modules?

    Thanks in Adavance

    Yeah, that's no good. I didn't realize that the errors would do that. Keeping the $ErrorView as ErrorView still cuts off the good stuff. You could also try the -ErrorVariable route again. How about this:


    $GciErrors = "c:\Test-Perm\gci_errors.txt"
    $GetAceErrors = "c:\Test-Perm\getace_errors.txt"
    
    $RootPath = "\\data\path"
    dir @("$RootPath\*"; "$RootPath\*\*"; "$RootPath\*\*\*") -ErrorVariable GciErrorVar | 
        where { $_.PsIsContainer } |  
        Get-AccessControlEntry -ErrorVariable GaceErrorVar |
        Export-Csv "C:\Test-Perm\Test-Perm.csv"
    
    $GciErrorVar | % { "{0}: {1}" -f $_.CategoryInfo.Category, $_.CategoryInfo.TargetName } | Out-File $GciErrors
    $GaceErrorVar | % { $_.ToString() } | Out-File $GetAceErrors
    

    Hopefully that will save what you're looking for (let me know if it's not). The errors from my module aren't nearly as cool as the errors from gci, so a simple ToString() gets their info. One day I'll get around to working on that. 

    If you don't want to see the errors in real time, set the -ErrorAction to SilentlyContinue. One more thing to note: you'll have to wait until the gci/get-ace line is done before you can see the errors that are saved to the file.

    I'm glad you're enjoying the module. I still have a lot of plans for, so keep checking back.

    If you're interested in books, the best book I've ever read for PowerShell is Bruce Payette's PowerShell in Action (I haven't read many, though). It's a very deep dive into the language by one of the main architects (unfortunately, it's PSv2). A book that might be better at getting started with modules, though, would be Don Jones' Toolmaking in a Month of Lunches book (not sure on the exact title). I haven't read it, but I've heard great things about the MoL series, and I would think that modules are covered there (check the book summary, though). Maybe someone else can jump in and make some good recommendations.

    There's also a ton of info on the internet (you can also ask questions here on the forums as you're learning). Just remember that a module is pretty much a collection of functions that you want to bundle together. If you get a few together in a script and give it a .psm1 extension, then you've made your first module.

    Thursday, February 27, 2014 12:59 AM
  • Thanks, jrv. I'm glad you like it. Are you having an issue with the module?

    no but you said you had to fix it.


    ¯\_(ツ)_/¯

    Yeah, I'll have to fix the example on the repository later tonight. If you do come across problems or suggestions, please let me know on the Q&A section.
    Thursday, February 27, 2014 1:01 AM
  • Yeah, I'll have to fix the example on the repository later tonight. If you do come across problems or suggestions, please let me know on the Q&A section.

    I was planning on building a similar module.  I had in mind a replacement for SubInAcl. YOU seem to be a long way to that end so I was encouraging you to finish.

    If I need it I will let you know if it has an issue.  I am sure there are plenty of Admins who could use it.


    ¯\_(ツ)_/¯

    Thursday, February 27, 2014 1:08 AM
  • Yeah, I'll have to fix the example on the repository later tonight. If you do come across problems or suggestions, please let me know on the Q&A section.

    I was planning on building a similar module.  I had in mind a replacement for SubInAcl. YOU seem to be a long way to that end so I was encouraging you to finish.

    If I need it I will let you know if it has an issue.  I am sure there are plenty of Admins who could use it.


    ¯\_(ツ)_/¯

    I appreciate that. I hope that it can be half as useful as SubInAcl.

    I'm really excited about the additions in the next version. Maybe I'll have something worth posting this weekend...

    Thursday, February 27, 2014 1:26 AM
  • Rohn,

    I really just can't thank you enough, for not only helping me but also for taking the time to teach/show new things that can be done. If funny that you mention Don Jones....I actually attended a class that was taught by Don Jones, he a REALLY smart guy for sure. He came to my city once and my boss at the time let me go and attend the class, I was glad to get the chance to go and it was fun as well.

    His book is "Learn Windows Powershell in a Month of Lunches". I would highly recommend the book, but to be honest I'm not big into tech books. I do find that looking and asking on-line is a better learning style for me rather than reading a book and trying something. I'm sure it also has to do with when I'm looking on-line I'm trying to accomplish a specific task rather than trying to learn about an something that I might use later.  In his class he did write show how to wrte some modules, but I was JUST getting into powershell at the time and knew even less than I do now so it was hard for me to even ask a question about modules let alone understand the answer. Two areas that I need more experience with are functions and modules, but I want to learn more and I like to think I'm stuborn enough to figure it out. I think I'll get there, but taking the time to thank you and everyone who helps me is something that is "best practice" :-)

    In the code you posted you did something new again for me...Now I could be wrong but it looks like you declared an array without @() with "GciErrorVar" and a variable with GaceErrorVar....If I'm right I'll just chalk it up to something I didn't know could be done....If I am wrong can you provide me a link so I can understand that better. (you can ingore the links it was more for my own understanding and for anyone in the future that comes across this post in the future)

    I came across a good site that explains what it looks like your doign when you pull specific infomation out of an array". I wanted to see the errors in real time so I didn't change it. At the same time I'm also running the where {$_.PsIsContainer } | Get-AccessControlEntry script from before so I can get the watch/get the full path with the errors for the "GaceErrorVar". The output to the screen is the same as before

    NotSpecified: (:) [Write-Error], WriteErrorException NotSpecified: (:) [Write-Error], WriteErrorException NotSpecified: (:) [Write-Error], WriteErrorException PermissionDenied: (\\data\path\Subfolder:String) [Get-ChildItem], UnauthorizedAccessException PermissionDenied: (\\data\path\Diff-Subfolder:String) [Get-ChildItem], UnauthorizedAccessException PermissionDenied: (\\data\pat...m_Antenna-May05:String) [Get-ChildItem], UnauthorizedAccessException

    Now just as an FYI...the full path is: \\data\path\Another-subfolder\DSX_Boom_Antenna-May05


    I wasn't too worried about what was being displayed on the screen, the following link "Using Hash Tables for More Complex Formatting". and "Formatting Strings using the .NET framework" I think explains the formatting pretty well. Now the output from the error logs looks like the following:

    From "gci-errors.txt":
    PermissionDenied: \\data\path\subfolder
    From "getace-errors.txt":
    Getting security descriptor for '\\data\path\subfolder\3ndlevelsubfolder': Access is denied
    Getting security descriptor for '\\data\path\subfolder\3ndlevelsubfolder': Access is denied
    It duplicated it the entry...

    It did give the full path so I have the information I want but is it duplicated becayse of the "%" ?

    Thanks in Adavance


    • Edited by John-Barrett Thursday, February 27, 2014 8:16 PM
    Thursday, February 27, 2014 8:07 PM
  • No problem. That's what the forums are here for, right?

    GciErrorVar and GaceErrorVar are arrays, but they were assigned during the calls to Get-ChildItem and Get-AccessControlEntry since we passed their names to the -ErrorVariable parameter. 

    After the pipeline is finished, the code goes through each element in the arrays using the ForEach-Object cmdlet. The % is an alias for ForEach-Object (and I shouldn't have used it in the code; I should have used the full name). Each array of error objects is treated differently. Get-ChildItem is returning real error objects to $GciErrorVar that have lots and lots of properties that are populated. The ForEach-Object scriptblock is pulling out two pieces of information, and using the -f operator to create a new string with them. Each new string is being passed to Out-File to save the text to a file for viewing later. 

    The same thing is happening to $GaceErrorVar, but instead of pulling specific info from the error object and making a new string, the error is being converted to a string through the ToString() method (the errors coming from Get-Ace are technically error objects, but they're just glorified strings; a future version of the module will add more information to the objects).

    Edit: The duplicate errors are probably a bug in the module. I'll check it out tonight to see what's going on. The call to foreach-object shouldn't create duplicate entries like that.

    Friday, February 28, 2014 12:56 AM