Quick question;
I want to make a log of bad stuff happening to the servers, but that's not the question.
The log should be deleted upon send, and if there is no log at the time the script is being run it should send a different message, without an attachment.
Here's the script so far:
<#
Set-ExecutionPolicy unrestricted -Force
iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))
cinst BetterCredentials
#>
Import-Module BetterCredentials
$Mailcred = Get-Credential -user user#gmail.com -pass 12345password
Try{
$File = C:/path/errorfile.log
}
Catch [System.Management.Automation.PSArgumentException]
{
"invalid object"
}
Catch [system.exception]
{
"caught a system exception"
}
Finally
{
If ($File -eq ){
then (send-mailmessage -ErrorAction SilentlyContinue -from "Server <Server#company.com>" -to "IT Dept. <Itdept#company.com>" -subject "Log of the day." -body "Good Morning,
Fortuneatly, nothing bad to report today!" -priority High -dno onSuccess, onFailure -smtpServer smtp.company.com -credential ($Mailcred) -usessl)
else (send-mailmessage -ErrorAction SilentlyContinue -from "Servers <Server#company.com>" -to "IT Dept. <ITDept#company.com>" -subject "Log of the day." -body "Good Morning,
Here is your daily report." -Attachments $File -priority High -dno onSuccess, onFailure -smtpServer smtp.company.com -credential ($Mailcred) -usessl)
}
}
What AM I doing wrong here?
Some parts of your code do not make any sense. Let's review those.
$File = C:/path/errorfile.log
...
If ($File -eq ){
The first part is about assignment. In order to find out wether errorfile.log exists, use the Test-Path cmdlet like so,
$File = test-path 'C:/path/errorfile.log'
Always use quotes around file name. If you got spaces within path, unquoted path doesn't work.
The second part is the actual test.
If ($File){
... # file was on disk, send alert and delete it
} else {
... # no file found, send ok
}
Related
Send-MailMessage -From "test#xxx.com" -To "aaa#xxx.com","bbb#xxx.com","ccc#xxx.com","ddd#xxx.com" -SmtpServer "xx.xx.xx.xx" -Subject "Test email subject" -Body "Test email body"
I want to write a script using above command in loop say 100 times and run every 2 seconds and write an output to a log file which later can be used for analysis.
Issue: My severer gives intermittent issue while sending emails. I want to see logs to see how many emails go through out of 100 and how many failed and with what error message.
Running multiple times would require some sort of loop. A for loop seems most appropriate for each loop of 100, and a while loop seems appropriate for looping over each set.
Since you are running commands very quickly you will want some sort of parallelism. Jobs, Runspaces, and workflows are the three most common forms. Jobs are the easiest to work with but the least efficient.
Then the Start-Sleep command can be used for the delay between trials.
There are many ways to write out to a file, Out-File -Append is one way. Try\catch can be used to catch the errors and output those to the log as well.
$Iterations = 100
$Delay = 2
$StopTime = (Get-Date).AddMinutes(5)
$Command = {Send-MailMessage -From "test#xxx.com" -To "aaa#xxx.com","bbb#xxx.com","ccc#xxx.com","ddd#xxx.com" -SmtpServer "xx.xx.xx.xx" -Subject "Test email subject" -Body "Test email body"}
while ((Get-Date -le $StopTime)) {
try {
Remove-Job -State Completed
for ($i = 0; $i -lt $Iterations; $i++) {
Start-Job -ScriptBlock $command
}
"Jobs created at (Get-Date)" | Out-File "C:\example\log.txt" -Append
Start-Sleep $delay
}
catch {
$error[0] | Out-File "C:\example\log.txt" -Append
}
}
I have a PowerShell script that generates emails from a list of contacts imported from a CSV file:
# Get Credential
$Credential = & "C:\Powershell Scripts\Windows\Get-CredentialFromWindowsCredentialManager.ps1" ms.outlook.15:my.email#domain.com
# Get Contacts
$contacts = Import-Csv -Path "C:\Powershell Scripts\Email\Contacts.csv"
# Compose Email for each contact
foreach( $contact in $contacts )
{
Write-Output "Creating email for: $($contact.FirstName) $($contact.LastName)"
$To = "$($contact.FirstName) $($contact.LastName) <$($contact.Email)>"
$From = "My Email <my.email#domain.com>"
$Subject = "$($contact.FirstName), I have a suggestion for you!"
$Body = "<html></html>"
$SMTPServer = "smtp.office365.com"
$Port = 587
Send-MailMessage -To $To -From $From -Subject $Subject -SmtpServer $SMTPServer -Credential $Credential -UseSsl -Body $Body -BodyAsHtml -Port $Port
# Due to the Message Send rate limit (30 per minute) I added this to slow the rate down
Start-Sleep -Seconds 10
}
Every 10 minutes I get the following SMTP Exception:
Send-MailMessage : Service not available, closing transmission channel. The
server response was: 4.4.1 Connection timed out. Total session duration:
00:10:08.3716645
At C:\Powershell Scripts\Email\SendEmail.ps1:17 char:2
+ Send-MailMessage -To $To -From $From -Subject $Subject -SmtpServer $SMTPServer ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.Mail.SmtpClient:SmtpClient) [Send-MailMessage], SmtpException
+ FullyQualifiedErrorId : SmtpException,Microsoft.PowerShell.Commands.SendMailMessage
Are there any settings that I can modify or changes in code that will prevent this?
Don't take this personally, this is just a general rant, but you've got a script that performs an action against a resource that is out of your control. As such, you can't just expect an SMTP connection to succeed and wonder what you should do to prevent it from failing. Deep breath. The answer is to consider edge cases and actually cater for them in your code. Granted, you've got a sleep in there to try to not fall foul of rate limiting, but that's not a robust solution. In this instance, a simple exception handler around the Send-MailMessage call would suffice. You could include a number of retries and small sleep delays.
A "maximum number of acceptable failures" threshold could be used to break out of the retry loop and cause some kind of inward alerting, Etc.
Long story short, don't just throw spaghetti at the wall with your eyes closed.
</rant>
An example, but not necessarily the tidiest solution:
[Int32] $maxAttempts = 5;
[Int32] $failureDelay = 2;
[Int32] $interMessageDelay = 10;
[Int32] $numAttempts = 0;
[Boolean] $messageSent = $false;
:CONTACT foreach ( $contact in $contacts ) {
$numAttempts = 1;
$messageSent = $false;
while ( ($numAttempts -le $maxAttempts) -and (! $messageSent) ) {
try {
Write-Host -Object ( 'Sending message, attempt #{0} of #{1}...' -f $numAttempts, $maxAttempts );
Send-MailMessage <blah>;
Write-Host -Object "Ok.`n";
$messageSent = $true;
} #try
catch [System.Exception] {
# ^^^^^ This exception type needs changing, but I don't know the full
# type of your failure.
Write-Host -Object 'Failed.';
if ( $numAttempts -ge $maxAttempts ) {
Write-Host -Object "ERROR : Maximum attempts reached - aborting.`n";
continue CONTACT;
} else {
Write-Host -Object ( 'Sleeping for {0} second(s)...' -f $failureDelay );
Start-Sleep -Seconds $failureDelay;
$numAttempts++;
} #else-if
} #catch
} #while
Write-Host -Object ( 'Sleeping for {0} second(s)...' -f $interMessageDelay );
Start-Sleep -Seconds $interMessageDelay;
} #foreach
I'm using the Send-MailMessage cmdlet to send a copy of a log file. However, I'd like to add a line to the log file with the status of the Send.
Code:
$To = "toaddress#email.com"
$Subject = "Test Email Attachment"
$From = "fromaddress#email.com"
$Body = "Please find log file attached"
$SMTPServer = "smtp.server.com"
$LogFile = "Testlog.log"
Add-Content -Path $LogFile -Value "Trying to email log..."
Try
{
send-mailmessage -to $To -subject $Subject -From $From -body $Body -smtpserver $SMTPServer -attachments $LogFile -ErrorAction Stop
Add-Content -Path $LogFile -Value "Successfully emailed log to: $To"
}
Catch [system.exception]
{
Add-Content -Path $LogFile -Value "Error emailing log to: $To"
}
Finally {}
Error:
PS E:\> . .\TestMail.ps1
Add-Content : The process cannot access the file 'E:\Testlog.log' because it is being used by another process.
At E:\TestMail.ps1:16 char:14
+ Add-Content <<<< -Path $LogFile -Value "Error emailing log to: $To"
+ CategoryInfo : WriteError: (E:\Testlog.log:String) [Add-Content], IOException
+ FullyQualifiedErrorId : GetContentWriterIOError,Microsoft.PowerShell.Commands.AddContentCommand
This works fine if the email succeeds, but if it fails (say the smtp server is unavailable), and erroraction is set to "Stop", then the file lock on the log file doesn't get released. If erroraction is not set to "Stop", then the log file gets released, but the Catch block doesn't get triggered.
I found a reference to the "dispose" method if you're rolling your own mail function, but I'm not sure how it could be used with the Send-MailMessage cmdlet: http://petermorrissey.blogspot.com.au/2013/01/sending-email-messages-with-powershell.html
Is there a command I should run to release the lock, or is this a bug, or am I going about this the wrong way?
Seems to be a bug: https://connect.microsoft.com/PowerShell/feedback/details/785417/out-file-cannot-access-logfile-references-in-attachments-of-failed-send-mailmessage-call
Email a copy of the file or create your own email function using PowerShell 1 methods (One example: how to send an email with attachement using powershell v1?)
I have created a script to provision Lync users, important details (such as assigned LineURI) for new provisions need to be emailed. Also any errors need to be sent (fluffed up with some friendly error messages of course :)).
So I created a few CSVs with all relevant data..
Then I created a function:
Function Send-Email ($attachArray) {
# Get a list of to addresses
$toAddresses = "foo#corp.local","bar#corp.local"
# Process replacments
Replace-EmailMasks
# Send conditionaly
Switch ($attachArray) {
$null {
Send-MailMessage -SmtpServer "internalrelay.corp.local" `
-From "test#andylab.local" -To $toAddresses `
-Subject "There should really be something more informative here" `
-BodyAsHTML $SCRIPT:htmlBody
}
Default {
Send-MailMessage -SmtpServer "internalrelay.corp.local" `
-From "test#andylab.local" -To $toAddresses `
-Subject "There should really be something more informative here" `
-BodyAsHTML $SCRIPT:htmlBody
-Attachments $attachArray
}
}
}
Here's how I invoke it:
# Logic, then send
If (($npSuccess -gt 0) -AND ($errorsExist -gt 0)) {
# Attaching both
# Heres the summary paragraph
$SCRIPT:customSummary = '<p>Success and errors :|</p>'
# Now I'm sending it.
Send-Email "$($tempPlace.fullname)\NewProviSsion_Output.csv","$($tempPlace.fullname)\Errors_Output.csv"
} ElseIf ($npSuccess -gt 0) {..} # output-generating Success
ElseIf ($errorsExist -gt 0) {..} # Failed somewhere
Else {..} # no output-generating Success, no overall fails
Now this works; Email looks nice, goes to who it should, files attached etc..
Problem is:
For however many files I specify in $attachArray, that's how many emails get sent. The emails are all exactly the same, going to all the same people n many times.
It's as if i'm doing this:
ForEach ($item in $attachArray) {
Send-Email "$($tempPlace.fullname)\NewProviSsion_Output.csv","$($tempPlace.fullname)\Errors_Output.csv"
}
Except i'm not..
To clarify my objective, I want the email to be sent to all in $toAddresses only once.
Can anyone enlighten me as to what's going on here?
Maybe I've just had a bad Monday morning..
The switch statement fires for each element of the array. This behavior is documented (check Get-Help about_Switch):
If the test value is a collection, such as an array, each item in the collection is evaluated in the order in which it appears.
Use a regular conditional instead (since you have only 2 cases anyway):
if ($attachArray -eq $null) {
Send-MailMessage -SmtpServer "internalrelay.corp.local" `
-From "test#andylab.local" -To $toAddresses `
-Subject "There should really be something more informative here" `
-BodyAsHTML $SCRIPT:htmlBody
} else {
Send-MailMessage -SmtpServer "internalrelay.corp.local" `
-From "test#andylab.local" -To $toAddresses `
-Subject "There should really be something more informative here" `
-BodyAsHTML $SCRIPT:htmlBody
-Attachments $attachArray
}
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()): $_"
}