7

I have a PowerShell script that is recursing down thru the file system using GetChildItem in a for-loop. As it travels, it is fixing ACL issues it finds (mostly where someone has blocked the BUILTIN\Administrators account)... but there are some it can't handle on it's own, like when I get [System.UnauthorizedAccessException] if there is an explicit "Deny" ACE.

The line of code looks like this:

foreach($file in Get-ChildItem $dirRoot -Recurse -ErrorAction Continue) {
    ...
}

When it stumbles on a path it can't read, it gives this exception:

Get-ChildItem : Access to the path 'C:\TEMP\denied' is denied. At Fix-ACLs.ps1:52 char:31 + foreach($file in Get-ChildItem <<<< $dirRoot -Recurse -ErrorAction Continue) { + CategoryInfo : PermissionDenied: (C:\TEMP\denied:String) [Get-ChildItem], Unauthorized AccessException + FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand

I would like to try/catch or trap the error so that I can repair the ACL (that is, remove the "Deny") in-place, and--most importantly--continue the loop without losing my place. Any suggestions for me?

ewall
  • 27,179
  • 15
  • 70
  • 84

4 Answers4

8

have you used silentlycontinue?

foreach($file in Get-ChildItem $dirRoot -Recurse -ErrorAction silentlycontinue) {
    ...
}
Matt
  • 1,931
  • 12
  • 20
  • 1
    Indeed I have... but it still runs through the code block with a bunch more errors. Since `trap` and `try/catch` generally work on "Stop" errors, the best option that I've found so far is to use the `-ErrorAction SilentlyContinue` as you suggested, and then in the first line of the code block check if we got just an error with `if ($error) {...`. – ewall Aug 04 '11 at 15:50
5

How about Inquire?

foreach($file in Get-ChildItem $dirRoot -Recurse -ErrorAction Inquire) {
...
}

Maybe open up a second PS window to troubleshoot the error then continue the command in the first PS window by selecting Y for continue.

You can also use ErrorVariable

foreach($file in Get-ChildItem $dirRoot -Recurse -ErrorVariable a) {
...
}

Get-Variable a or $a will show you all the errors incurred by the command. You can also use +variablename (+a) to add errors to an existing variable.

foreach($file in Get-ChildItem $dirRoot -Recurse -ErrorVariable +a) {
...
}
Jesse
  • 51
  • 1
  • 1
  • To Everyone: Don't make the same mistake I made when I tried this.. Don't add the $ for the variable on the -ErrorVariable line. A simple thing to overlook and I would have thought this didn't work till I caught that typo. – Jean-Claude DuBois May 03 '18 at 19:54
1

Try and Catch blocks will only handle terminating errors so if (as it is in this case) the error is non terminating you will need to define an error action that terminates. If you change your error action to 'Stop' it will jump to the Catch block(s) and then carry on with your script.

You can then create a specific catch block for that error and then attempt to remediate your issue / test.

More info below: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_try_catch_finally?view=powershell-7.2

With that in mind you could do something like the below:

foreach($file in Get-ChildItem $dirRoot -Recurse -ErrorAction Stop) {
        ...
    }
    Catch [System.UnauthorizedAccessException]{
    #Catch error in question, remediate, log or 'PAUSE' for manual intervention
    }
    Catch{
    #Deal with any other errors
    }

Hopefully that is useful.

Qasmaj
  • 11
  • 3
0

I would use this to:

ForEach($file in Get-ChildItem $dirRoot -Recurse -ErrorAction silentlycontinue) {
    ...
}

And then, you can filter $Error to get specifically Permission Denied type errors:

$permError += $Error | Where-Object { $_.CategoryInfo.Category -eq 'PermissionDenied' }

ForEach($deniedAccess in $permError)
{
    $deniedAccess.CategoryInfo.TargetName | Do Stuff
}
Kluk
  • 126
  • 6