Robocopy sending email, attachment, appending file name - powershell

I am using robocopy to backup one hard drive to another below is the code that I am using:
robocopy "\\server_location\XXXX" "\\local_location\xxxx" /copyall /TEE /S /r:1 /ndl /xc /xo /xn /log+:"C:\desktop\log.TXT"
START mailto:myemail.job.com?subject=Ovl%%2Reportˆ&body=Test,%%0D%%0A%%0D%%0AHere%%20is%%20is%%the%%20ovl%%20report.%%0D%%0A%%0D%%0ABye
I need to be able to do a couple of things:
email the text file (powershell?) automatically through outlook.
When comparing files if the time stamp is different, append the destination file name, and copy over the file from the source.

So here is a script I've used for a lot of my robocopy jobs. I'm not sure if it fits your "send email through outlook" as it's just doing an SMTP, it's not actually hooking in to Outlook to do it. The script is a Powershell 2.0 script, and remember only Server 2008R2 and Windows Vista+ have the /MT option, which is bad to use with the /TEE option. Script is at the bottom of the post.
For the second point you have, I think you might want two different scripts, one that specifically ignores timestamps, and then another that would include the timestamp difference files, which if ran directly after the first should be about right. This would be with the /XO switch which is "/XO : eXclude Older - if destination file exists and is the same date or newer than the source - don’t bother to overwrite it."
Other options that might help you in scripting that out are:
/XC | /XN : eXclude Changed | Newer files
/XL : eXclude "Lonely" files and dirs (present in source but not destination) This will prevent any new files being added to the destination.
/XX : eXclude "eXtra" files and dirs (present in destination but not source) This will prevent any deletions from the destination. (this is the default)
#*=============================================
#* Base variables
#*=============================================
$SourceFolder = "C:\SourceFolder"
$DestinationFolder = "C:\DestinationFolder"
$Logfile = "C:\Robocopy.log"
$Subject = "Robocopy Results: Copy Purpose - Location to Location"
$SMTPServer = "smtp.server.com"
$Sender = "Server <email#address.com>"
$Recipients = "User1 <email#address.com>"
$Admin = "Admin <email#address.com>"
$SendEmail = $True
$IncludeAdmin = $True
$AsAttachment = $False
#*=============================================
#* GMAIL variables
#*=============================================
#$SMTPServer = "smtp.gmail.com"
#$cred = New-Object System.Net.NetworkCredential("username", "password");
# Add "-UseSsl -Credential $cred" to the Send-MailMessage
#*=============================================
#* SCRIPT BODY
#*=============================================
# Change robocopy options as needed. ( http://ss64.com/nt/robocopy.html )
Robocopy $SourceFolder $DestinationFolder /MIR /R:2 /W:5 /LOG:$Logfile /NP /NDL
# The following attempts to get the error code for Robocopy
# and use this as extra infromation and email determination.
# NO OTHER CODE BETWEEN THE SWITCH AND THE ROBOCOPY COMMAND
Switch ($LASTEXITCODE)
{
16
{
$exit_code = "16"
$exit_reason = "***FATAL ERROR***"
#$IncludeAdmin = $False
#$SendEmail = $False
}
8
{
$exit_code = "8"
$exit_reason = "**FAILED COPIES**"
#$IncludeAdmin = $False
#$SendEmail = $False
}
4
{
$exit_code = "4"
$exit_reason = "*MISMATCHES*"
$IncludeAdmin = $False
#$SendEmail = $False
}
2
{
$exit_code = "2"
$exit_reason = "EXTRA FILES"
$IncludeAdmin = $False
#$SendEmail = $False
}
1
{
$exit_code = "1"
$exit_reason = "Copy Successful"
$IncludeAdmin = $False
#$SendEmail = $False
}
0
{
$exit_code = "0"
$exit_reason = "No Change"
$SendEmail = $False
$IncludeAdmin = $False
}
default
{
$exit_code = "Unknown ($LASTEXITCODE)"
$exit_reason = "Unknown Reason"
#$SendEmail = $False
$IncludeAdmin = $False
}
}
# Modify the subject with Exit Reason and Exit Code
$Subject += " : " + $exit_reason + " EC: " + $exit_code
# Test log file size to determine if it should be emailed
# or just a status email
If ((Get-ChildItem $Logfile).Length -lt 25mb)
{
If ($IncludeAdmin)
{
If ($AsAttachment)
{
Send-MailMessage -From $Sender -To $Recipients -Cc $Admin -Subject $Subject -Body "Robocopy results are attached." -Attachment $Logfile -DeliveryNotificationOption onFailure -SmtpServer $SMTPServer
} Else {
Send-MailMessage -From $Sender -To $Recipients -Cc $Admin -Subject $Subject -Body (Get-Content $LogFile | Out-String) -DeliveryNotificationOption onFailure -SmtpServer $SMTPServer
}
} Else {
If ($AsAttachment)
{
Send-MailMessage -From $Sender -To $Recipients -Subject $Subject -Body "Robocopy results are attached." -Attachment $Logfile -DeliveryNotificationOption onFailure -SmtpServer $SMTPServer
} Else {
Send-MailMessage -From $Sender -To $Recipients -Subject $Subject -Body (Get-Content $LogFile | Out-String) -DeliveryNotificationOption onFailure -SmtpServer $SMTPServer
}
}
} Else {
# Creat the email body from the beginning and end of the $Logfile
$Body = "Logfile was too large to send." + (Get-Content $LogFile -TotalCount 15 | Out-String) + (Get-Content $LogFile | Select-Object -Last 13 | Out-String)
# Include Admin if log file was too large to email
Send-MailMessage -From $Sender -To $Recipients -Cc $Admin -Subject $Subject -Body $Body -DeliveryNotificationOption onFailure -SmtpServer $SMTPServer
#Exclude Admin if log file was too large to email
#Send-MailMessage -From $Sender -To $Recipients -Subject $Subject -Body $Body -DeliveryNotificationOption onFailure -SmtpServer $SMTPServer
}
#*=============================================
#* END OF SCRIPT: Copy-RobocopyAndEmail.ps1
#*=============================================

Related

Script to check if a backup ran

I am trying to write a script that checks folders to see if a backup has run for that particular installation.I have a number of folders and in these folders are the backup .zip files, the names of the files are the same for all folders so for example folder A has the file "Backup.zip" folder B will also have "Backup.zip" but from a different installation. At the moment I got it working fine but it's not looking very good and for example if a new folder gets created and a backup will be placed here I need to copy paste a whole block of text in the script and change the names. I'm looking for a more automated check that if I add a folder it will check that aswell.
Also the point of the checks is , if the file didn't change (or is not present at all) in the last 24 hours I should be notified by e-mail, which I also have working right now.
This is the code I have right now
if (Test-Path "<Path to file>")
{
if ( ((get-date) - (ls <Path to file>).LastWriteTime).days -lt 1)
{
$success++
}
else
{
Send-MailMessage -From $MailFrom -To $MailTo -Subject "!!Backup failed!!" -Body "!!Backup failed!!" -Port $SMTPPort -Credential $EmailCredential -UseSsl
$failed++
}
}
else
{
Send-MailMessage -From $MailFrom -To $MailTo -Subject "!!Backup not found!!" -Body "!!Backup not found!!" -Port $SMTPPort -Credential $EmailCredential -UseSsl
$failed++
}
The variables $success and $failed is just used for a general email that gets sent after it has ran all the checks to see how many ran successfull and how many failed.
But this code above is what I have to copy everytime a new folder for backups is created, so I manually have to edit the Path to file aswell. Can somebody help me or push me in the right direction to make it an automated thing? I'm extremely new to Powershell and I don't really know how to get it going. I do know there is a thing called 'foreach' but I have no clue how to implement it here.
Thanks in advance!
EDIT: I managed to get it working partly, for some reason about 8 folders can't get checked, I get this error on those folders. 37 other folders get checked fine, what can be the issue here? All the folders have the same properties.
Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.IO.FileInfo".
At line:25 char:6
+ if ( ([System.Io.fileinfo] $i).LastWriteTime.Date -ge [datetime]::Tod ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : ConvertToFinalInvalidCastException
the code it will be like that
#if there is more than one parent path mentioned all of them in array like $parents = "path1","path2", ..... and loop on them with the same foreach
$Parentpath = <Parent path which contain the backups>
foreach ($folder in $parentpath){
#check it is a folder not a file
if (($Folder.GetType().name) -eq "DirectoryInfo"){
$path = Convert-Path $folder.PSPath
}
if (Test-Path "$path")
{
if ( ((get-date) - (ls if (Test-Path "$path") ).LastWriteTime).days -lt 1)
{
$success++
}
else
{
Send-MailMessage -From $MailFrom -To $MailTo -Subject "!!Backup failed!!" -Body "!!Backup failed!!" -Port $SMTPPort -Credential $EmailCredential -UseSsl
$failed++
}
}
else
{
Send-MailMessage -From $MailFrom -To $MailTo -Subject "!!Backup not found!!" -Body "!!Backup not found!!" -Port $SMTPPort -Credential $EmailCredential -UseSsl
$failed++
}
}
$Parentpath = "\\x.x.x.x\FTPbackups\3CX\"
foreach ($i in $parentpath)
{
if ( ([System.Io.fileinfo] $i).LastWriteTime.Date -ge [datetime]::Today )
{
Write-Host "-=File Found for '$i'"
$success++
}
else
{
Write-Host "-=File found but not modified in the last 24 hours"
Send-MailMessage -From $MailFrom -To $MailTo -Subject "!!Backup van niet gelukt!!" -Body "!!Backup van niet gelukt!!" -Port $SMTPPort -Credential $EmailCredential -UseSsl
$failed++
}
}
I don't quite understand how it works, do I need to make an array with every path of every folder?

powershell winscp get local filename after transfer so i can attach to email

Hello I am trying to get the filename of the file just downloaded with the localpath ie C:\users\user\desktop\test.txt so i can attach it to an email.
I am using powershell with the winscp .net library
here is the code
param (
$localPath = "C:\upload\*",
$remotePath = "/home/user/",
$backupPath = "C:\backup\"
)
try
{
# Load WinSCP .NET assembly
Add-Type -Path "WinSCPnet.dll"
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions
$sessionOptions.Protocol = [WinSCP.Protocol]::Sftp
$sessionOptions.HostName = "example.com"
$sessionOptions.UserName = "user"
$sessionOptions.Password = "mypassword"
$sessionOptions.SshHostKeyFingerprint = "ssh-rsa 2048 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx"
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
# Upload files, collect results
$transferResult = $session.PutFiles($localPath, $remotePath)
# Iterate over every transfer
foreach ($transfer in $transferResult.Transfers)
{
# Success or error?
if ($transfer.Error -eq $Null)
{
Write-Host ("Upload of {0} succeeded, moving to backup" -f
$transfer.FileName)
Send-MailMessage -From $From -to $To -Cc $Cc -Bc $Bc -Attach $session.GetFileInfo(Filename) -Subject $Subject -Body $Body -SmtpServer $SMTPServer
# Upload succeeded, move source file to backup
Move-Item $transfer.FileName $backupPath
}
else
{
Write-Host ("Upload of {0} failed: {1}" -f
$transfer.FileName, $transfer.Error.Message)
}
}
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
exit 0
}
catch [Exception]
{
Write-Host $_.Exception.Message
exit 1
}
thank you in advance
If you're wanting to use $session.getFileInfo() from the WinSCP library, you're looking for $transfer.destination. I.e. $session.getFileInfo($transfer.Destination).
Otherwise if you're reading the properties of the file from the local path, you could just use Powershell's built in cmdlets as such: get-itemproperty $transfer.filename

Powershell applied to Outlook Com Object - Multiple Filters

ORIGINAL POST:
Goal: an email is sent to a service account every day from a source.
Take the csv file it gives me and put it in a folder for the DBA guy.
Without further adieu, here is my current script:
#define variables
$datestamp = (get-date).AddDays(-1).ToString("MMddyyyy")
$datestamp = $datestamp.ToString()
$path = "C:\MyPath"
$dest = "C:\MyPath\Archive"
$file = "MyFile.csv"
#create outlook session
$objOutlook = New-Object -Com Outlook.Application
$inbox = $objOutlook.Session.GetDefaultFolder(6)
$inbox.Items.Restrict("[UnRead] = True" -and "[SenderEmailAddress] = 'SomePlace#SomeDomain.net'") | select -Expand Attachments | %
{
for ($i = $_.Count; $i; $i--)
{
$_.Item($i).SaveAsFile("C:\MyPath\$($_.Item($i).FileName)")
$_.Parent.Unread = $false
}
}
if (Test-Path "C:\MyPath\*.csv")
{
if(((Get-ChildItem C:\MyPath | Measure-Object ).Count) -gt '1' )
{
Send-MailMessage –SmtpServer "server.domain.com" –From "PoorITGuy#domain.com" -To "PoorITGuy#domain.com" -Subject " FAIL" -Body "FAILED. Too many valid items from mailbox.
$objOutlook.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($objOutlook)
throw "Too many items to get."
}
else
{
Get-ChildItem $path\*.csv | foreach {Copy-Item $_ "C:\MyPath\$"}
Copy-Item C:\MyPath\*.csv "$path\$file"
Copy-Item C:\MyPath\*.csv "$dest\${datestamp}_$file"
if(Test-Path "$dest\$file")
{
Send-MailMessage –SmtpServer "server.domain.com" –From "PoorITGuy#domain.com" -To "PoorITGuy#domain.com" -Subject "some message”
#cleanup - remove all files from base directory, clean mailbox, close out com object.
Remove-Item "$path\*.csv"
$inbox.Items | select | %
{
for ($i = $_.Count; $i; $i--)
{
$_.Item($i).Delete
}
}
$objOutlook.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($objOutlook)
}
else
{
Send-MailMessage –SmtpServer "server.domain.com" –From "PoorITGuy#domain.com" -To "PoorITGuy#domain.com" -Subject " failure" -Body "File manipulation failure."
$objOutlook.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($objOutlook)
throw "File manipulation failure."
}
}
}
else
{
Send-MailMessage –SmtpServer "server.domain.com" –From "PoorITGuy#domain.com" -To PoorITGuy#domain.com -Subject "FAIL" -Body "No item mailbox."
$objOutlook.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($objOutlook)
throw "No item to get in inbox."
}
What doesn't work:
$inbox.Items.Restrict("[UnRead] = True" -and "[SenderEmailAddress] = 'SomePlace#SomeDomain.net'") | select -Expand Attachments | %
It seems that you cannot restrict the comObject Outlook.Application with more than one filter.
I've done a ton of searching on this and cannot find an answer on how to best perform this task in lieu of this. But I want my script to be spam-proof, so it needs to know that it is sent from the expected sender AND it needs to be unread only (see error catching right below)
In addition, I'm not certain if this would work:
$inbox.Items | select | %
{
for ($i = $_.Count; $i; $i--)
{
$_.Item($i).Delete
}
}
Also would like input on the script itself. Any input to make it more efficient would be appreciated.
The correct code (lots of syntax errors on the above prevented it from doing what I wanted to)
#define variables
$datestamp = (get-date).AddDays(-1).ToString("MMddyyyy")
$datestamp = $datestamp.ToString()
$path = "C:\MyPath"
$dest = "C:\MyPath\Archive"
$importpath = "C:\MyPath\Import"
$file = "MyFile.csv"
$folderExclude = "C:\MyPath\Archive"
#create outlook session
$objOutlook = New-Object -Com Outlook.Application
$inbox = $objOutlook.Session.GetDefaultFolder(6)
$inbox.Items.Restrict("[Unread] = True AND [SenderEmailAddress] = 'SomePlace#SomeDomain.net'") | select -Expand Attachments | % {
for ($i = $_.Count; $i; $i--) {
$_.Item($i).SaveAsFile("\\server\MyPath\$($_.Item($i).FileName)")
$_.Parent.Unread = $false
}
}
if (Test-Path "C:\MyPath\*.csv") {
if(((Get-ChildItem C:\MyPath -Include "*.csv" | Measure-Object ).Count) -gt '1' ) {
Send-MailMessage -SmtpServer smtpserver.mydomain.com -From Powershell#mydomain.com -To someguy#mydomain.com -Subject "FAIL" -Body "FAILED. Too many items from PSAutomationSrv mailbox."
$objOutlook.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($objOutlook)
throw "Too many items to get."
}
else {
Copy-Item C:\MyPath\*.csv "$importpath\$file"
Copy-Item C:\MyPath\*.csv "$dest\${datestamp}_$file"
if(Test-Path "$dest\${datestamp}_$file") {
Send-MailMessage -SmtpServer smtpserver.mydomain.com -From Powershell#mydomain.com -To someguy#mydomain.com -Subject " successful" -Body "Date is: ${datestamp} File name is: ${dest}\${file}"
#cleanup - remove all files from base directory, clean mailbox, close out com object.
Remove-Item $path\*.csv
$objOutlook.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($objOutlook)
}
else {
Send-MailMessage -SmtpServer smtpserver.mydomain.com -From Powershell#mydomain.com -To someguy#mydomain.com -Subject " import - failure" -Body "File manipulation failure."
$objOutlook.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($objOutlook)
throw "File manipulation failure."
}
}
}
else {
Send-MailMessage -To -SmtpServer smtpserver.mydomain.com -From Powershell#mydomain.com -To someguy#mydomain.com -Subject "FAIL" -Body "FAILED. No item in mailbox."
$objOutlook.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($objOutlook)
throw "No item to get in inbox."
}
Notes - I had some code in excess.
Get-ChildItem $path\*.csv | foreach {Copy-Item $_ "C:\MyPath\$"}
is incorrect syntax, and unnecessary since the script already knows it is just manipulating one file anyway.
For deleting the inbox I would need to use Export-Mailbox it seems, which I do not want to do; as the service account performing this function is NOT an exchange admin nor do I want it to be. I'm just going to manually delete the inbox periodically or have the exchange guy do it on his end.

How can I concatenate a string with a link that has spaces in powershell

Here is my code that checks when users passwords will expire and emails them if it will expire in less than 14 days. Since the link in the body of the email has spaces in it, the link does not encompass the whole file name.
#Add the Quest PowerShell snapin
Add-PsSnapIn Quest.ActiveRoles.ADManagement -ErrorAction SilentlyContinue
#Clear the placeholder log file
Clear-Content O:\logs\network\passwordchangeemails.txt
#Set Email Variables
$today = Get-Date
$logdate = Get-Date -format yyyyMMdd
$emailFrom = "my.name#mycompany.com"
$body = "Good Morning, `n`n"
$body += "As a courtesy reminder, your Company password will be expiring soon. `n`n"
$body += "If you allow your password to expire, you will be unable to access our network, mail or QAD until you recieve assistance from the Helpdesk. `n`n"
**$body += "To avoid this, please change your network password as soon as possible. (Remote users, please follow the password change procedure for remote users. Click Here -> " + "\\mydfsshare.net\share\helpdesk\guides\Passwords - Change procedure for remote users.pdf ) `n`n"**
$body += "Feel free to contact the Help Desk by phone at 555-555-5555 or by email at Helpdesk#mycompany.com `n"
$body += "Thanks!"
#Get Active Directory Information
Get-QADUser -SizeLimit 0 | Select-Object samAccountName,mail,PasswordStatus |
Where-Object {$_.PasswordStatus -ne "Password never expires" -and $_.PasswordStatus -ne "Expired" -and $_.PasswordStatus -ne "User must change password at next logon." -and $_.mail -ne $null} |
#For each user, get variables
ForEach-Object {
$samaccountname = $_.samAccountName
$mail = $_.mail
$passwordstatus = $_.PasswordStatus
$passwordexpiry = $passwordstatus.Replace("Expires at: ","")
$passwordexpirydate = Get-Date $passwordexpiry
$daystoexpiry = ($passwordexpirydate - $today).Days
#If days to expire is lessthan 14 days, send email to user
if ($daystoexpiry -lt 14) {
$emailTo = "$mail"
$subject = "Company IT Notification: Your Network password will expire in $daystoexpiry day(s). "
Send-MailMessage -To $emailTo -From $emailFrom -Subject $subject -Body $body -SmtpServer 192.168.1.191
Write-Host "Email was sent to $mail on $today"
Add-Content O:\logs\network\passwordchangeemails.txt "Email was sent to $mail on $today"
}
}
$recipients = "my.name#mycompany.com"
#Copy contents of log file to email and send to IT Department
$content = [IO.File]::ReadAllText("O:\logs\network\passwordchangeemails.txt")
Send-MailMessage -To $recipients -From "my.name#mycompany.com" -Subject "Password change log for $today" -Body "This is the log from $today, $content" -SmtpServer 111.111.111.111
Sorry I can't test this right now but if HTML is OK to use, try wrapping the link as follows...
"Text you want the users to see goes here<br>`n
Then set -BodyAsHtml on Send-Mailmessage:
Send-MailMessage -To $emailTo -From $emailFrom -Subject $subject -Body $body -SmtpServer 192.168.1.191 -BodyAsHtml

powershell - write errors to .txt file and in one line

I need to log the process in a text file and there are two problems:
1. How to write errors in a text file? Now when the e-mails are not sent, nothing is logged.
2. I need to write time, date and event in a single line
Now I have this in log.txt:
17. feb. 2012 10:47:34
Chyba: .gpg neexistuje
17. feb. 2012 10:57:28
Test.gpg existuje.
This is my code:
function write-log{
param(
[string]$mytext,
[string]$fgc
)
if(!$fgc){$fgc="Black"}
write-host $mytext -foregroundcolor $fgc
$myfile = "c:\gnupg\subor\log.txt"
Get-Date | Out-File $myfile -append
$mytext | Out-File $myfile -append
}
if(test-path "d:\vm\shared pre ws08R2+SQL05\SFRB\*.chk") {
echo "Subor .chk existuje a jeho nazov je "
get-childitem "d:\vm\shared pre ws08R2+SQL05\SFRB\*.chk" -Name
$a = get-childitem "d:\vm\shared pre ws08R2+SQL05\SFRB\*" -include *.chk -name | Foreach-Object {$a -replace ".chk", ""}
if(test-path d:\vm\"shared pre ws08R2+SQL05"\SFRB\$a.gpg) {
Write-Log $a".gpg existuje." Green
Write-Log "Presuvam do banky subor $a.gpg.." Green
Move-Item "d:\vm\shared pre ws08R2+SQL05\SFRB\$a.gpg" c:\gnupg\subor\$a.gpg
Write-Log "Presun ukonceny." Green
Write-Log "Presuvam do banky subor $a.chk.." Green
Move-Item "d:\vm\shared pre ws08R2+SQL05\SFRB\$a.chk" c:\gnupg\subor\$a.chk
Write-Log "Presun ukonceny. Subor je pripraveny na spracovanie." Green
Write-Log "Posielam notifikacne maily.." Green
$emailFrom = "sfrbControlMsg#primabanka.sk"
$emailTo = "msic#primabanka.sk"
$subject = "subject"
$body = "subor presunuty"
$smtpServer = "87.244.217.54"
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($emailFrom, $emailTo, $subject, $body)
# Write-Log "Maily odoslane." Green
}
else {
Write-Log " Chyba: $a.gpg neexistuje" Magenta
}
} else {
Write-log "V cielovom adresari neexistuje ziadny subor .chk."
}
Thank you.
Make use of the inbuilt Send-MailMessage cmdlet with parameter -ErrorAction Stop.
Use a try-catch around this (see about_try_catch_finally to pick up and handle any errors.
In the catch block $_ will be the error message (as would be shown on the console), and has an Exception property which is the (.NET) Exception (or subclass) instance (I use $_.Exception.GetType() below to specifically report this type as it is an important part of any diagnostics):
Eg. in a script I have:
try {
$trans = (MakeSummaryMessage) + "`r`n`r`n" + $trans
Send-MailMessage -From $emailFrom `
-To $emailTo `
-SmtpServer $emailServer `
-Subject $subject" `
-Body $trans `
-Encoding ([System.Text.Encoding]::UTF8) `
-ErrorAction Stop
$emailSent = $true
} catch {
Write-EventLog -LogName $eventLog -Source $eventSource -EntryType "Error" `
-EventId 100 -Message "Failed to send email: $($_.Exception.GetType()): $_"
}