Handeling non-stopping errors for Get-ChildItem in PowerShell - powershell

I'm currently writing a script which should be used to get the average access/list time for a directory tree on a CIFS share. To do this I'm using the following code (as a snippet):
$time = Measure-Command {
try{
$subitems = Get-ChildItem $directory
}catch{
$msg = "Error accessing "+$dir+": "+$_.Exception.Message
}
}
That piece of code is working fine and does get me the information I want. But one issue I'm facing is that there are non stopping errors for Get-ChildItem which are not caught by the catch (as they are non stopping). To prevent this I could add -ErrorAction Stop to Get-ChildItem but if I do that I won't be able to get a listing for the directory that has even one item that throws an error.
Examples of this include missing permissions and paths exceeding 260 characters (for whatever reason that is still a thing). I really would like to get that information in some way to do some further handling/reporting on it. Would anyone know how to catch those/react to those?
My research so far always suggests to use -ErrorAcction Stop which would "discard" any information for $subitems that I could use.

So you want to catch the error and the script to continue,I have modified your code to redirect error output and then check previous command's exit status to check whether any error occured.
Is this what you are looking for?
$time = Measure-Command {
try{
$subitems = Get-ChildItem $directory 2> $outnull
if(-not $?){
#whatever action you want to perform
$msg = $msg + "Error accessing "+$dir+": "+$error[0].Exception.Message
}
}catch{
$msg = "Error accessing "+$dir+": "+$_.Exception.Message
}
}
I am concatenating $msg in the block with itself ,so that no msg will be lost by overwriting

Related

Retrieve all unique permissions of all folders in SharePoint libraries

I am trying to generate a report with all unique permissions in all folders of a SharePoint Online library (and not user permissions, the goal is to retrieve group permissions). I wrote a Powershell script using SharePoint PnP. Please see below.
$permissionCSV = "path-to-csv.csv"
$CAMLQuery = "<View Scope='RecursiveAll'><RowLimit>5000</RowLimit></View>"
$items = Get-PnPListItem -List "My-List" -Query $CAMLQuery
$ctx = $Get-PnPContext
foreach($item in $items) {
if(item.FileSystemObjectType -eq 'folder') {
Get-PnPProperty -ClientObject $item -Property HasUniqueRoleAssignments
if ($item.HasUniqueRoleAssignments) {
$ctx.load($item.RoleAssignments)
$ctx.load($item.Folder)
$ctx.ExecuteQuery()
foreach($RoleAssignments in $item.RoleAssignments) {
$ctx.Load($RoleAssignments.Member)
$ctx.Load($RoleAssignments.RoleDefinitionBindings)
$ctx.ExecuteQuery()
foreach($RoleDefinition in $RoleAssignments.RoleDefinitionBindings) {
$RoleDefinition |
Select #{expression={$item.Folder.Name};Label="Name"},
#{expression={$item.Folder.ServerRelativeUrl};Label="Path"},
#{expression={$RoleAssignments.Member.Title};Label="Role"},
#{expression={$_.Name};Label="Permission"}|
Export-CSV $permissionCSV -Append -Force -Encoding UTF8 -NoTypeInformation
}
}
}
}
}
This code works. However, because our library is very large (more than 5000 folders), such a script would take days to be executed. So I am wondering if I am taking a wrong approach. All the posts I read only mention this method, which requires network communication for each single folder...
Does anyone knows the best way to generate such report without taking days? Any idea is welcome, including using other programming languages, but I would like to avoid paid third-party services if possible.
Thank you for your help.

Better custom error handling for powershell

So I have a powershell script that integrates with several other external third-party EXE utilities. Each one returns its own kind of errors as well as some return non-error related output to stderr (yes badly designed I know, I didn't write these utilities). So What I'm currently doing is parsing the output of each utility and doing some keyword matching. This approach does work but I feel that as I use these scripts and utilties I'll have to add more exceptions to what the error actually is. So I need to create something that is expandable,possibly a kind of structure I can add to an external file like a module.
I was thinking of leveraging the features of a custom PSObject to get this done but I am struggling with the details. Currently my parsing routine for each utility is:
foreach($errtype in {'error','fail','exception'})
{
if($JobOut -match $errtype){ $Status = 'Failure' }
else if($JobOut -match 'Warning'){$Status = 'Warning' }
else { $Status = 'Success' }
}
So this looks pretty straightforward until I run into some utility that contain some of the keywords in $errtype within $JobOut that is not an error. So now I have to add some exceptions to the logic:
foreach($errtype in {'error','fail','exception'})
{
if($JobOut -match 'error' -and(-not($JobOut -match 'Error Log' }
elseif($JobOut -match $errtype){ $Status = 'Failure' }
else if($JobOut -match 'Warning'){$Status = 'Warning' }
else { $Status = 'Success' }
}
So as you can see this method has the potential to get out of control quickly and I would rather not start editing core code to add a new error rule every time I come across a new error.
Is there a way to maybe create a structure of errors for each utility that contains the logic for what is an error. Something that would be easy to add new rules too?
Any help with this is really appreciated.
I would think a switch would do nicely here.
It's very basic, but can be modified very easily and is highly expandable and I like that you can have an action based on the input to the switch, which could be used for logging or remediation.
Create a function that allows you to easily provide input to the switch and then maintain that function with all your error codes, or words, etc. then simply use the function where required.
TechNet Tips on Switches
TechNet Tips on Functions

Getting result of .Net object asynchronous method in powershell

I'm trying to call an async method on a .Net object instantiated in Powershell :
Add-Type -Path 'my.dll'
$myobj = new-object mynamespace.MyObj()
$res = $myobj.MyAsyncMethod("arg").Result
Write-Host "Result : " $res
When executing the script, the shell doesn't seem to wait for MyAsyncMethod().Result and displays nothing, although inspecting the return value indicates it is the correct type (Task<T>). Various other attempts, such as intermediary variables, Wait(), etc. gave no results.
Most of the stuff I found on the web is about asynchronously calling a Powershell script from C#. I want the reverse, but nobody seems to be interested in doing that. Is that even possible and if not, why ?
I know this is a very old thread, but it might be that you were actually getting an error from the async method but it was being swallowed because you were using .Result.
Try using .GetAwaiter().GetResult() instead of .Result and that will cause any exceptions to be bubbled up.
For long running methods, use the PSRunspacedDelegate module, which will enable you to run the task asynchronously:
$task = $myobj.MyAsyncMethod("arg");
$continuation = New-RunspacedDelegate ( [Action[System.Threading.Tasks.Task[object]]] {
param($t)
# do something with $t.Result here
} );
$task.ContinueWith($continuation);
See documentation on GitHub. (Disclaimer: I wrote it).
This works for me.
Add-Type -AssemblyName 'System.Net.Http'
$myobj = new-object System.Net.Http.HttpClient
$res = $myobj.GetStringAsync("https://google.com").Result
Write-Host "Result : " $res
Perhaps check that PowerShell is configured to use .NET 4:
How can I run PowerShell with the .NET 4 runtime?

StreamWriter in Powershell Outputs Blank File

Here is the code:
$outputPath = "c:\Output"
$scopePath = "$($outputpath)\scopes.csv"
$clientsPath = "$($outputpath)\clients.csv"
$scopeStream = New-Object System.IO.StreamWriter($scopePath)
$clientStream = New-Object System.IO.StreamWriter($clientsPath)
$scopeStream.WriteLine("Scope,ScopeName")
$clientStream.WriteLine("IP,MAC,Lease,Reservation,Hostname")
$scopeStream.Flush()
$clientStream.Flush()
...
$scopeStream.WriteLine("$($scope.Name),$($scope.Address)")
...
$clientStream.WriteLine("$($client.IP),$($client.MAC),$($client.Lease),$($client.Reservation)$($client.Hostname)")
...
$scopeStream.close()
$scopeStream.dispose()
$clientStream.close()
$clientStream.dispose()
The files are created if they don't exist, but nothing is ever written to them. Perfectly blank files, and I can't figure out why, for the life of me.
I've seen this before - usually it's because the StreamWriter is throwing an Exception. You'd think that this would cause an exception in your script, but I've seen cases where it's throwing non-terminating exceptions, so your script doesn't ever receive it.
After running the block of code, try getting the last exception details:
$Error[0].Exception | Format-List * -Force
If there's an exception, it should help you figure out exactly what is causing the problem. My guess would be that it's a permissions issue.

PowerShell IE9 ComObject has all null properties after navigating to webpage

I have a PowerShell script that navigates to a (presumably) classic ASP page on our intranet to stop a Windows Service running on our server as part of the deployment process for that service (and restarts it after deploying the new files). It ran fine until we recently upgraded to IE9. Here's the script.
# Open service page in IE
$ie = new-object -comobject InternetExplorer.Application
$ie.visible = $true
$ie.navigate($serviceUrl)
while($ie.busy) { start-sleep 1 }
# Stop service
$ie.Document.getElementById("dropDownActionList").value = "Stop"
$ie.Document.getElementById("buttonTakeAction").click()
while($ie.busy) { start-sleep 1 }
Now when I run the script, it successfully launches IE, but throws the following error:
You cannot call a method on a null-valued expression.
At C:\Projects\ABC\Scripts\Deploy.ps1:85 char:28
+ $ie.Document.getElementById <<<< ("dropDownActionList").value = "Stop"
+ CategoryInfo : InvalidOperation: (getElementById:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
When I investigate in PowerShell, I find that if I create the IE ComObject, it at first has valid properties, but as soon as I navigate to the the service control page, all the properties are null (almost as if the ComObject gone away?). For example, before the HWND property had a valid value, but now it's null ($ie.hwnd -eq $null returns true). No error is displayed in PowerShell when I navigate to the page.
I looked at some similar questions, but the first one doesn't match my circumstance (the Document property is null in my case) and as for the latter one, IE9 defaults to compatibility mode for intranet sites. I saved the ASP page and ran it through the w3c validator and it threw some errors (although none related to the elements I'm trying to deal with). Unfortunately I can't fix those. Other sites don't seem to have this problem. Any suspicions on what the problem may be and recommendations on work-arounds?
I just worked through this.. sort of. I was seeing the same behavior until I turned off protected mode in IE. This seems to have something to do with submitting from one security zone to the next. So.. assuming that your original page is in the internet zone, with protected mode on, you submit to a page in a trusted zone or intranet or whatever, it seems like the COM context is lost. Probably intentional. I'm going to try fixing the zones, and keeping protected mode on.
Hope this helps.
EDIT: This is also a non-issue if you run your powershell in elevated mode (run as admin)
In addition:
http://msdn.microsoft.com/en-us/library/bb625962.aspx
This problem is caused by integrity levels since Internet Explorer 8.
That is also the reason, why the application runs well as administrator.
Since IE-8 runs in "low integrity" mode, it is not possible to automate IE from within a script. This is because the script runs as an user which belongs to "medium integrity" mode. The security design is such that it can send instructions from medium to low integrity, but can not receive data from low to medium integrity.
Update: Here is a working example how to do it without changing any settings. It gets back the lost com-Object.
function ConnectIExplorer() {
param($HWND)
$objShellApp = New-Object -ComObject Shell.Application
try {
$EA = $ErrorActionPreference; $ErrorActionPreference = 'Stop'
$objNewIE = $objShellApp.Windows() | ?{$_.HWND -eq $HWND}
$objNewIE.Visible = $true
} catch {
#it may happen, that the Shell.Application does not find the window in a timely-manner, therefore quick-sleep and try again
Write-Host "Waiting for page to be loaded ..."
Start-Sleep -Milliseconds 500
try {
$objNewIE = $objShellApp.Windows() | ?{$_.HWND -eq $HWND}
$objNewIE.Visible = $true
} catch {
Write-Host "Could not retreive the -com Object InternetExplorer. Aborting." -ForegroundColor Red
$objNewIE = $null
}
} finally {
$ErrorActionPreference = $EA
$objShellApp = $null
}
return $objNewIE
}
$HWND = ($objIE = New-Object -ComObject InternetExplorer.Application).HWND
$objIE.Navigate("https://www.google.com")
$objIE = ConnectIExplorer -HWND $HWND