Capture all errors in a powershell script and email - powershell

I have a script with many sections such as below that runs nightly. I would like to get it to email any/all errors so i can be alerted and review the errors. I'm having trouble with the first step which is to capture all/any errors... I assume to a file which i could email or capturing to some kind of buffer that i could then email would be even nicer. Any help with both steps would be appreciated - especially the capturing part.
#---- Set Exchange archive licnse for all users with an Office license ----
Get-MsolUser -ALL | where {($_.Licenses.accountskuID -contains
"Tennant:STANDARDWOFFPACK") -and ($_.Licenses.accountskuID -notcontains
"Tennant:EXCHANGEARCHIVE_ADDON")} | Set-MsolUserLicense -AddLicenses
"Tennant:EXCHANGEARCHIVE_ADDON"
#-------------------------- ENABLE LITIGATION HOLD ----------------------
Get-Mailbox -ResultSize Unlimited -Filter {RecipientTypeDetails -eq
"UserMailbox"} | Set-Mailbox -LitigationHoldEnabled $true -
LitigationHoldDuration 2555

Error messages should automatically be captured in the $error variable; use $error[0] for the latest message.
You could then use that as the body for your email in conjunction with the Send-MailMessage Cmdlet
example:
$body = "";
foreach ($e in $error) {
$body += "<hr /><pre>" + $e.ToString() + "</pre><hr />";
}
Send-MailMessage -BodyAsHtml -Body $body -SmtpServer "smtp_server_address" -From "alert#somedomain.tld" -To "your-email#somedomain.tld" -Subject "PowerShell Error Report"

In your code, you need to trap errors in order to log them, even write to your own event log...
https://blogs.technet.microsoft.com/heyscriptingguy/2013/02/01/use-powershell-to-create-and-to-use-a-new-event-log
https://blogs.technet.microsoft.com/heyscriptingguy/2013/06/20/how-to-use-powershell-to-write-to-event-logs
... or write your own log function.
Example:
https://gallery.technet.microsoft.com/scriptcenter/Write-Log-PowerShell-999c32d0
Or, start with using PowerShell logging.
Example:
Enable logging in Group Policy
https://gallery.technet.microsoft.com/scriptcenter/Write-Log-PowerShell-999c32d0
Use PowerShell transcript
Example:
https://technet.microsoft.com/en-us/library/ff687007.aspx?f=255&MSPPError=-2147217396
Then have those stored in a central share that you can pull into an email as an attachment.

Related

Powershell - Send email when number of days reaches a set interval

I'm at a blocker and need some help!
(New-Timespan -Start (Get-Date) -End (Get-AdfsCertificate -CertificateType Token-Signing | where-object { $_.IsPrimary -eq $true }).Certificate.NotAfter).Days
This returns a result of days until it is no longer a primary certificate. E.G simply "141"
How can I implement a way of looking at this result and sending an email when it reaches a certain day? E.G 20 days until it is no longer primary.
I can use a mail relay, so no credentials are required!
Many thanks in advance!
Put your code in a Task Schedule to run on a date and time, use an if/then code block to check the state, and send as needed when it meets your metric.
For example:
If ((New-Timespan -Start (Get-Date) -End (Get-AdfsCertificate -CertificateType Token-Signing |
where-object { $PSItem.IsPrimary -eq $true }).Certificate.NotAfter).Days -eq 20)
{
# Do something here
}
As for email, this is why the Send-MailMessage cmdlet exists.
Get-Help -Name Send-MailMessage -Full
Get-Help -Name Send-MailMessage -Examples
# Results
<#
NAME
Send-MailMessage
SYNOPSIS
Sends an email message.
# -- Example 1: Send an email from one person to another person --
Send-MailMessage -From 'User01 <user01#fabrikam.com>' -To 'User02 <user02#fabrikam.com>' -Subject 'Test mail'
The `Send-MailMessage` cmdlet uses the From parameter to specify the message's sender. The To parameter specifies the message's recipient. The Subject
parameter uses the text string Test mail as the message because the optional Body parameter is not included.
# ---------------- Example 2: Send an attachment ----------------
Send-MailMessage -From 'User01 <user01#fabrikam.com>' -To 'User02 <user02#fabrikam.com>', 'User03 <user03#fabrikam.com>' -Subject 'Sending the
Attachment' -Body "Forgot to send the attachment. Sending now." -Attachments .\data.csv -Priority High -DeliveryNotificationOption OnSuccess,
OnFailure -SmtpServer 'smtp.fabrikam.com'
The `Send-MailMessage` cmdlet uses the From parameter to specify the message's sender. The To parameter specifies the message's recipients. The
Subject parameter describes the content of the message. The Body parameter is the content of the message.
The Attachments parameter specifies the file in the current directory that is attached to the email message. The Priority parameter sets the message
to High priority. The -DeliveryNotificationOption parameter specifies two values, OnSuccess and OnFailure . The sender will receive email
notifications to confirm the success or failure of the message delivery. The SmtpServer parameter sets the SMTP server to smtp.fabrikam.com .
# ----------- Example 3: Send email to a mailing list -----------
Send-MailMessage -From 'User01 <user01#fabrikam.com>' -To 'ITGroup <itdept#fabrikam.com>' -Cc 'User02 <user02#fabrikam.com>' -Bcc 'ITMgr
<itmgr#fabrikam.com>' -Subject "Don't forget today's meeting!" -Credential domain01\admin01 -UseSsl
The `Send-MailMessage` cmdlet uses the From parameter to specify the message's sender. The To parameter specifies the message's recipients. The Cc
parameter sends a copy of the message to the specified recipient. The Bcc parameter sends a blind copy of the message. A blind copy is an email
address that is hidden from the other recipients. The Subject parameter is the message because the optional Body parameter is not included.
The Credential parameter specifies a domain administrator's credentials are used to send the message. The UseSsl parameter specifies that Secure
Socket Layer (SSL) creates a secure connection.
#>

Is there a way to execute a command from within a string or have more than one body in a powershell email?

I'm trying to send an email that contains both some information that I type out as well as the contents of a text file that was generated in earlier code. I was wondering if there is a convenient way of doing so without sending two emails? The code below didn't work but I can get the message to send if I only have the text or only have the contents of the txt file. Thanks!
Send-MailMessage -SMTPServer localhost -To myemail#email.com -From myemail#email.com -Subject "TESTING Active Domain Replication Failure TESTING" -Body "The following Domain Controller has had a replication failure. Please see attached txt files for more information. `n"(Get-Content -Path .\causeOfFailure.txt | out-string) -attachment error.zip
So I edited the statement to look like this:
$fileContent = (Get-Content -Path .\causeOfFailure.txt)
compress-archive -Path causeOfFailure.txt,dnsInformation.txt -update -DestinationPath error.zip
Send-MailMessage -SMTPServer localhost -To email#email.com -From email#email.com -Subject "TESTING Active Domain Replication Failure TESTING" -Body "The following Domain Controller has had a replication failure. Please see attached txt files for more information. `n & $ExecutionContext.InvokeCommand.ExpandString($fileContent)" -attachment error.zip
The output in the email shows this message
"System.Management.Automation.EngineIntrinsics.InvokeCommand.ExpandString(Source Controller:".
Is there a better way to have a string followed by a function both be part of the body of an email with the send-mailmessage command?
If you are trying to add the contents of a file to the body of the email, you can simply use $() to include functions within the quotes.
Send-MailMessage -SMTPServer localhost -To email#email.com -From email#email.com -Subject "TESTING Active Domain Replication Failure TESTING" -Body "The following Domain Controller has had a replication failure. Please see attached txt files for more information. `n $fileContent" -attachment error.zip
If you want to include the function within your script, you can do this,
Send-MailMessage -SMTPServer localhost -To email#email.com -From email#email.com -Subject "TESTING Active Domain Replication Failure TESTING" -Body "The following Domain Controller has had a replication failure. Please see attached txt files for more information. `n $(Get-Content -Path .\causeOfFailure.txt)" -attachment error.zip
The problem is because of the way you are constructing the body string.
You already have the content of the causeOfFailure.txt in a variable $fileContent, so there is no need to do a Get-Content on the same file again.
I would suggest you create your body in a separate variable first, to make the code more readable.
There are several options for this, like
Make use of a Here-String
$body = #"
The following Domain Controller has had a replication failure. Please see attached txt files for more information.
$fileContent
"#
Or insert a NewLine followed by the $fileContent using the -f Format operator
$body = 'The following Domain Controller has had a replication failure. Please see attached txt files for more information.{0}{1}' -f
[Environment]::NewLine, $fileContent
Or simply create as one double-quoted string
$body = "The following Domain Controller has had a replication failure. Please see attached txt files for more information.`r`n$fileContent"
Whichever you find is the most readable/maintainable method for your code.
Then for the part where you use the Send-MailMessage cmdlet.
Here again, writing out all parameters as one long line can make the code less readable and by doing that, mistakes are easily made.
There is a better way to use a cmdlet with lots of parameters, known as Splatting.
Applied to your code, this looks like:
# create a hashtable object with all parameters you want to use for Send-MailMessage
$mailParams = #{
SMTPServer = 'localhost'
To = 'myemail#email.com'
From = 'myemail#email.com'
Subject = "TESTING Active Domain Replication Failure TESTING"
Body = $body
Attachments = 'error.zip'
}
Send-MailMessage #mailParams
Hope this helps

Capture the Keyword FAILED from a file and send out a email notification

I require a PowerShell script to capture the error FAILED from an output file and, if the status is FAILED, then send the email to the DL list with the subject as failed and in the body of the email should have output file format as it is. I am stuck in writing the if statement. Please help me.
The location of the output file is D:\logs.
Format of the output file:
process_id : 1
STATUS : FAILED
RULE_ID : 44
RULE_NAME : OEBS-1
LAST_UPDATE_DATE : 1/4/2017 11:37:02 AM
import-csv d:\logs\logfile.csv | where-object { $_.STATUS -eq "FAILED" } |
send-mailmessage -from "User01 <user01#example.com>" -to "User02 <user02#example.com>",
"User03 <user03#example.com>" -subject "Sending the Attachment" -body
"Forgot to send the attachment. Sending now." -Attachments "data.csv"
-priority High -dno onSuccess, onFailure -smtpServer smtp.fabrikam.com
I have used the send-mailmessage from Example 2 of here: https://msdn.microsoft.com/en-us/powershell/reference/3.0/microsoft.powershell.utility/send-mailmessage?f=255&MSPPError=-2147217396. This gives the examples of using many of the send-mailmessage paramaters. You can use the ones you require, or modify to add what you would like.
** The formatting of the code might be out. I tried to keep it reasonably tidy and on one page without scrolling. I haven't used ` to indicate a new line as you would in actual PowerShell code.
$failed = import-csv d:\logs\logfile.csv | where-object {$_.STATUS -eq "FAILED"}
send-mailmessage -from failedjob#fabrikam.com -to dl#fabrikam.com -subject Failed -body $failed -smtpserver mailserverthatwillaccept.fabrikam.com
That's another option.
** Edited to import-csv as per comments from Ansgar
Thanks, Tim.

Powershell Script Gives Error But Still Returns "Successfully"

I'm writing a program to create an AD account and enable an Exchange mailbox and I'm getting some strange behaviour from it.
First, although it creates an AD user successfully, the mailbox cannot be enabled because "MyPath/Mr. Example could not be found". I assume this is because of a time lag between the AD server and the Exchange server (the code is run on the Exchange server). While inserting a sleep period seems to fix this, is there any other possibility I may be missing?
Second, I get this error message in the "red error text" that Powershell uses. It seems that my try/catch statement is letting the error slip by, which causes my script to erroneously inform me that the task completed successfully. How could I force the error to be caught within the script rather than being displayed on the console? (I was running it dot-sourced during testing, which is how I noticed this.)
I've included the relevant parts below. Any help is appreciated! Thanks =)
Import-Module ActiveDirectory
Add-PSSnapin Microsoft.Exchange.Management.Powershell.Admin
$errorLog = #()
$masterLog = #()
$time = Get-Date
New-ADUser -SamAccountName "example" -Name "Mr. Example" -Path "DC=MyPath" -Enabled 1 -Server ADServer
try{
Enable-Mailbox -Identity "MyPath/Mr. Example" -Alias "example" -Database "DatabaseName"
}
catch{
$myError = $Error[0]
$errorLog = $errorLog + "[$time] Error enabling mailbox for $fullName`: $myError"
}
if($errorLog.length -eq 0){
echo "An Active Directory account and Exchange mailbox for Mr. Example has been successfully created!"
}
else{
foreach($event in $errorLog){
$masterLog = $masterLog + $event
}
}
There are terminating and non-terminating errors in PowerShell. Only the former are caught by try..catch. You can force errors to be terminating by setting
... -ErrorAction Stop
for a specific command, or
$ErrorActionPreference = Stop
for the entire script. See the Scripting Guy blog for more information.

How to execute an external console application with PowerShell and send the result to email?

I need to execute some periodic console applications that give me some results (on the console)... How can I execute it and have its return data sent to my email?
I tried to use [Diagnostics.Process]::Start() and it launches my application but i don't know how to get the return... I do not want the exitCode, I want the text that the application prints on screen.
Using PS V2 CTP3.
*** UPDATE
The solutions presented worked fine but i have a problem... this application that i need to execute is gfix from the firebird database and now i discovered a thing, I can't redirect the output of gfix to a file, if i execute on command prompt the line:
gfix.exe -v -f dabatase.gdb > c:\test.txt
it print the output on the screen and the file is empty.
Same thing if I try to assign it to a variable... I don't know what difference gfix has from the other console apps that I use, but looks like its output can't be redirected.
Has someone seeing this?
*** UPDATE 2
Even if I use Start-transcript /Stop-Transcript, although on the screen I see the gfix output, on the file there is only the commands :/
*** UPDATE 3
Found the solution here
http://edn.embarcadero.com/br/article/25605
Something like this could work:
# temporary file
$f = [io.path]::GetTempFileName()
# start process and redirect its output to the temp file
ping localhost > $f
# create and send email
$emailFrom = "user#yourdomain.com"
$emailTo = "user#yourdomain.com"
$subject = "results"
$body = (Get-Content $f) -join "`r`n"
$smtpServer = "your smtp server"
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($emailFrom, $emailTo, $subject, $body)
# delete the file
remove-item $f
I think in this case [Diagnostics.Process]::Start() is not needed. Besides that there is a cmdlet Start-Process that does almost the same.
PowerShell v2 is now out, so you could consider upgrading.
Then, you can simply try:
PS > [string]$ipconfig=ipconfig
PS > send-mailmessage -to some_email -from from_email -subject PowerShell -body $ipconfig -bodyashtml -smtp my_smtp_server
Now, it depends on how complicated your command-line output is, because the above method will collapse multiple lines into a single on.