Send-MailMessage Argument Null or empty - powershell

Can someone please help me figure this part out.
#Mail Configuration
$smtpUser = "email#domain.com"
$smtppass = ConvertTo-SecureString "PasswordAsPlainText" -AsPlainText -Force
$psCred = New-Object System.Management.Automation.PSCredential -ArgumentList ($smtpuser, $smtppass)
$ToAddress = "toaddress#domain.com"
$FromAddress = "send#domain.com"
$SMTPSERVER = "smtp.office365.com"
$SMTPPORT = "587"
$MailParam = #{
To = $ToAddress
From = $FromAddress
Subject = $subject
Body = $Mail
SMTPServer = $SMTPServer
Port = $SMTPPORT
Credential = $pscred
}
#Send Email
$GetChildItem = Get-ChildItem -Path C:\Temp
if ($GetChildItem -ne $Null)
{
Write-Host "Backup Success"
$subject = "$env:COMPUTERNAME SQL Backup Success"
$Mail = "SQL Backup Succeded on the server, please see the attached report for more details"
$attachment = $reportfile
Send-MailMessage #MailParam -BodyAsHtml -usessl
#Exit
}
if ($GetChildItem -eq $Null)
{
Write-Host "Backup Failed"
$subject = "$env:COMPUTERNAME SQL Backup Failed"
$Mail = "SQL Backup Failed on the server, please see the attached report for more details"
$attachment = $reportfile, $debuglog
Send-MailMessage #MailParam -usessl -BodyAsHtml
#Exit
}
Now I don't think anything is wrong with the code above, I am no expert coder but I can pretty much copy and paste :) and take stuff from there to get it working for me.
The issue with the above code is that when run it, it doesn't work for the 1st time but when you run it again it works fine. The error you get the first time is
Send-MailMessage : Cannot validate argument on parameter 'Body'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
At C:\Users\Aasim\Desktop\Untitled1.ps1:29 char:26
+ Send-MailMessage #MailParam -BodyAsHtml -usessl
+ ~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Send-MailMessage], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.SendMailMessage
I am not sure what the issue is and why it doesn't work only the first time but works every other subsequent time until you close and reopen the script.
Basically I am creating a SQL backup script that backs up databases to our Network Share and then emails me whether it was successful or not. so far the rest of the script works just fine.

In your If and Else blocks, instead of creating a variable $subject, or $Mail, update the hashtable, $MailParam. Also, you seem to be missing the Attachment variable in your Send-MailMessage
if ($GetChildItem -eq $Null)
{
Write-Host "Backup Failed"
$MailParam.Subject = "$env:COMPUTERNAME SQL Backup Failed"
$MailParam.Body = "SQL Backup Failed on the server, please see the attached report for more details"
$attachment = $reportfile, $debuglog
Send-MailMessage #MailParam -usessl -BodyAsHtml -Attachments $attachment
#Exit
}

Related

Restart IIS proces when is memory usage

I have a web application and I have one worker process set up. I need to ensure that IIS restarts when the value is exceeded. I am attaching a script below that works for me, but only when I have only one application pool. I am also attaching an error that appears on a server where there are multiple application pools.
$process_dump32 = get-process w3wp
$process_dump32_output = [Math]::Round($process_dump32.privatememorysize64, 2)
Write-Output $process_dump32_output
#Email Settings
$EmailBody = 'IIS has more than 800'
$subject_mail = "IIS has more than 800 - $Date"
$MailFrom = 'email#example.com'
$MailTo = 'email2#example.com'
$smtpServer = '10.0.0.1'
$smtpPort = '587'
$secpasswd1 = ConvertTo-SecureString "Paassword" -AsPlainText -Force
$mycreds1 = New-Object System.Management.Automation.PSCredential ("notification", $secpasswd1)
$encoding = [System.Text.Encoding]::UTF8
If ($process_dump32_output -lt 800000000)
{
#Do Nothing if RAM is less then 800MB
}
Else
{
#Stop/Start
Stop-Service W3SVC
Start-Sleep -seconds 5
Start-Service W3SVC
Send-MailMessage -from $MailFrom -to $MailTo -subject $subject_mail -body $EmailBody -Encoding $encoding -smtpServer $smtpServer -Port $smtpPort -credential $mycreds1
}
ERROR
Cannot find an overload for "Round" and the argument count: "2".
At I:\MONITOR\check-memory-exception-iis.ps1:7 char:1
+ $process_dump32_output = [Math]::Round($process_dump32.privatememorys ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
Thanks for help
Gild

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?

Adding attachments to an email generated in powershell

I'm attempting to create a PS script that does the following:
Run 2 programs located on the server
Check for error files
Attach the error files to the email
Wait for 5 minutes, rinse and repeat
I've got the below so far, I can get it working without the attachments, it just sends a link in the email. However I'd rather they be attachments.
Currently the error I'm getting is "Powershell.exe : A positional parameter cannot be found that accepts argument '$null'.".
But I've no idea why this is being generated?
Any advice would be appreciated.
#Hide powershell
Powershell.exe -windowstyle hidden {
#Make sure the program isn't running before starting the script
stop-process -Name WorksOrderCompletionImport
stop-process -Name WorksOrderIssuesImport
#Loop condition
While ($true)
{
$i++
#Bind location to string
$files=Get-ChildItem "E:\Production Siemens Interface Files\error"
#Binding the path to the string
$filechecker = 'E:\Production Siemens Interface Files\error\*.csv'
# Sender and Recipient Info for email
$MailFrom = "myemail#domain.com"
$MailTo = "myemail#domain.com"
# Sender Credentials
$Username = "myemail#domain.com"
$Password = "BadBoiPassword"
# Server Info
$SmtpServer = "smtp.office365.com"
$SmtpPort = "587"
# Message content
$Link = "\\SERVER1\E Drive\Production Siemens Interface Files\error"
$MessageSubject = "Errors have been generated in the Production Siemens Interface Files Folder"
$Message = New-Object System.Net.Mail.MailMessage $MailFrom,$MailTo
$Message.IsBodyHTML = $true
$Message.Subject = $MessageSubject
$Message.Body = #'
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p>Hello,</p>
<p>Please be advised that errors have been generated in the Siemens Integration Production Folder.</p>
<p>Please follow this link to check : Link to error files</p>
</body>
</html>
'#
#Start the programs
start-process -filepath "C:\Program Files\Datel Computing\Siemens Integration PRODUCTION\WorksOrderIssuesImport.exe"
start-process -filepath "C:\Program Files\Datel Computing\Siemens Integration PRODUCTION\WorksOrderCompletionImport.exe"
Start-Sleep -s 10
#If statement to control email
if (Test-Path $filechecker)
# Construct the SMTP client object, credentials, and send
{$Smtp = New-Object Net.Mail.SmtpClient($SmtpServer,$SmtpPort)
$Smtp.EnableSsl = $true
$Smtp.Credentials = New-Object System.Net.NetworkCredential($Username,$Password)
$attachment = New-Object System.Net.Mail.Attachment –ArgumentList 'E:\Production Siemens Interface Files\error\'$file
$smtp.Attachments.Add($attachment)
$Smtp.Send($Message)}
else { Start-Sleep -s 10
}
#Stop the program
stop-process -Name WorksOrderCompletionImport
stop-process -Name WorksOrderIssuesImport
# Sleep for 300 seconds then loop back
Start-Sleep -s 300
}
}
You're using a lot .net references. I think you can simplify this by using the existing cmdlet Send-MailMessage. For example:
$PSEmailServer = "smtp.office365.com"
$smtpPort = "587"
$mailFrom = "myemail#domain.com"
$mailTo = "myemail#domain.com"
$messageSubject = "Errors have been generated in the Production Siemens Interface Files Folder"
$attachment = "E:\Production Siemens Interface Files\error\$file"
$body = #'
<p>Hello,</p>
<p>Please be advised that errors have been generated in the Siemens Integration Production Folder.</p>
<p>Please follow this link to check : Link to error files</p>
'#
Send-MailMessage -From $mailFrom -To $mailTo -Subject $messageSubject -Body $body -BodyAsHtml -Attachments $attachment -Port $smtpPort -UseSsl
See also: MS Reference Send-MailMessage
Ofcourse you need to supply your credentials for authentication. If you look at the documentation you'll see -Credential is one of the parameters. What you can do is:
# Define your Credentials
$username = 'your username'
$password= 'your password'
# Crete a credential Object
[SecureString]$secureString = $password | ConvertTo-SecureString -AsPlainText -Force
[PSCredential]$credentialObject = New-Object System.Management.Automation.PSCredential -ArgumentList $username, $password
Add the -Credential parameter to the Send-Mailmessage command like -Credential $credentialObject
This is just to show you a way to accomplish what you want, but be careful with this method, because this way you store your password directly in the Script. There are solutions to encrypt your password in external files. You can find a good explanation on: https://pscustomobject.github.io/powershell/howto/Store-Credentials-in-PowerShell-Script/

Send-MailMessage returns 4.4.1 connection timed out every 10 minutes

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

PowerShell error on sending e-mail

I have the following Powershell script that I would use to send an e-mail:
$smtpServer = "smtp.live.com"
$smtpPort = "465"
$credential = [Net.NetworkCredential](Get-Credential)
$smtpClient = New-Object System.Net.Mail.SmtpClient $smtpServer, $smtpPort
$smtpClient.EnableSsl=$true
$smtpClient.UseDefaultCredentials=$false
$smtpClient.Credentials= $credential
$smtpTo = "existing_email#hotmail.com"
$smtpFrom = "email#hotmail.com"
$messageSubject = "Testing Mail"
$messageBody = "Hi, This is a Test"
$message = New-Object System.Net.Mail.MailMessage $smtpFrom, $smtpTo, $messageSubject, $messageBody
try
{
$smtpClient.Send($message)
Write-Output "Message sent."
}
catch
{
$_.Exception.Message
Write-Output "Message send failed."
}
But I receive this error:
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,mail.ps1
Exception calling "Send with "1" arguments" Failure Sending mail
What is wrong whit this code?
You haven't defined $smtpFrom (address from which the e-mail is sent). Also, just a suggestion, why don't you use Send-MailMessage cmdlet, it's available from PowerShell 2.0
This code works for me without any issues with smtp.live.com and port 587 with SSL
$SmtpServer = 'smtp.live.com'
$SmtpUser = 'your_username#outlook.com'
$smtpPassword = 'your_password'
$MailTo = 'your_to_mail#example.com'
$MailFrom = 'your_username#outlook.com'
$MailSubject = "Test using $SmtpServer"
$Credentials = New-Object System.Management.Automation.PSCredential -ArgumentList $SmtpUser, $($smtpPassword | ConvertTo-SecureString -AsPlainText -Force)
Send-MailMessage -To "$MailTo" -from "$MailFrom" -Subject $MailSubject -SmtpServer $SmtpServer -Credential $Credentials -UseSsl -Port 587
I solved changing port to 25. But why I can't use IMAP ?