dcaz
dcaz

Reputation: 909

How do I test for failure or get the error?

Two situations. first is the previously discussed Start-Process process that I need to use due to -wait. Start-Process, Invoke-Command or?)

  1. How do I get the error from this since a command window opens and runs.
Start-Process -FilePath 'C:\Gyb\gyb.exe' -ArgumentList @("--email $UserEmail", "--action backup", "--local-folder $GYBfolder", "--service-account") -Wait
        $filename = "C:\reports\" + $UserEmail.Split("@")[0] + "_Backup.csv"
  1. How do I know if the email sent?
Send-MailMessage @MailArguments -to $PEmail -cc -Body $Body -Subject $mailSubject

Upvotes: 3

Views: 708

Answers (1)

codewario
codewario

Reputation: 21418

I will answer these in reverse order.


How do I know if the email sent?

Send-MailMessage will throw an error if the mail server can't send the message. Beyond that, you're at the mercy of the network of servers ensuring email gets delivered to the appropriate mailbox.

If you really need to verify receipt, you could always request a delivery receipt, but
Send-MailMessage does not support this. You would need to use System.Web.Mail namespace to prepare and send the mail instead, which you can set the DeliveryNotificationOptions.OnSuccess option. However, heed the following warning from Microsoft:

The Send-MailMessage cmdlet is obsolete. This cmdlet does not guarantee secure connections to SMTP servers. While there is no immediate replacement available in PowerShell, we recommend you do not use Send-MailMessage. For more information, see Platform Compatibility note DE0005.

The linked additional information explains that SmtpClient is the component at fault, and other .NET libraries are recommended for sending emails instead.

A less reliable option would be to wait for a Non-Delivery Receipt in the inbox of the sending account to appear, but there is no guarantee how long it might take to receive one back. You could wait 5 mins before declaring success, but the NDR might not come back for 10.


How do I get the error from this since I command opens and runs?

If you mean you want to figure out why the Start-Process bit failed, you have three options. IMO both Start-Process solutions aren't perfect in this regard but will get the job done, but the third solution gives you the most control.

  1. Tell the process not to run in a new window, but you can't easily evaluate the output programmatically.

-NoNewWindow cannot be used with -Verb RunAs

Start-Process -NoNewWindow ....
  1. Redirect the output to a file then read it, but this isn't scalable:
Start-Process -RedirectStandardOutput stdout.txt ....

$stdout = Get-Content stdout.txt
  1. Use the Process.Start method with the ProcessStartInfo class to launch the process. Most robust, but more complicated than using Start-Process:
# Create process start information. See documentation for property details
$pInfo = New-Object System.Diagnostics.ProcessStartInfo -Property @{
  FileName = 'ping'
  Arguments = 'google.com'
  UseShellExecute = $false
  RedirectStandardOutput = $true
  CreateNoWindow = $true
}

# Start the process
$p = [System.Diagnostics.Process]::Start($pInfo)

# Wait until process finishes
while( !$p.HasExited ) {
  Start-Sleep 10 # Number of seconds to sleep
}

# Get the standard output as a string
$output= $p.StandardOutput.ReadToEnd()
  1. If you can, use the call operator & to prefix your command and execute your program. It's much easier to use than Start-Process and unless you need to use one of the flags of Start-Process (like -Wait for GUI apps). The above section 3. can be simplified if we use & instead:
$output = & ping google.com

This works because the call operator does directly invoke the process, and the process output is able to be redirected. I have not tested this but I believe in this case STDERR can be redirected to the variable by redirecting your Error Stream to the Success Stream.

The caveat to using the & call operator is that you are stuck with how it works; there is no customizing the behavior. Fortunately, it should work for the majority of use cases.


If you're a keen observer you will notice that Process.Start returns the same Process type that Start-Process -Passthru .... returns. You may be asking yourself, "why can't we just use the convenient-to-use cmdlet to return the Process object and simply consume its StandardOutput property?

We need to ensure that $pInfo sets a few critical properties which you can't set with
Start-Process:

  • UseShellExecute = $false
    • Tells .NET to directly execute the FileName instead of invoking via a shell (e.g.
      cmd.exe /c FileName Arguments)
    • Required to redirect the STDOUT, STDERR, or STDIN streams for the child process
  • RedirectStandardOutput = $true

Since Start-Process doesn't let you provide a ProcessStartInfo argument, nor does it provide parameters to control either behavior, file redirection is the only option. Otherwise, the returned process object with Start-Process -Passthru .... is the same type. Trying to use the Process.StandardOutput StreamReader doesn't work in this case, because there is no redirected stream to read from.

Upvotes: 3

Related Questions