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.
- 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 ....
- Redirect the output to a file then read it, but this isn't scalable:
Start-Process -RedirectStandardOutput stdout.txt ....
$stdout = Get-Content stdout.txt
- 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()
- 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.