This question already has an answer here:
Send-MailMessage: Cannot validate argument on parameter 'Subject'
(1 answer)
Closed 5 years ago.
The emails will not send. Do i need some sort of a Try /Catch loop? What would be the best method of error checking this in Powershell?
I need it to check for each and if one location is not found that it still sends out the others and displays a message saying which Reports were sent and which were not depending on whether the report was found in the folder
Send-MailMessage : Cannot validate argument on parameter 'Subject'. The argument is null or empty. Provide an argument that is not null or empty,
and then try the command again.
#Defines Directory
$dir = "C:\Users\user\Desktop\reprts\Todays"
#Sets STMP server
$SMTPServer = "10.0.0.46"
#Declares todays time and formats
$Time = (Get-Date).ToString('MM/dd/yyyy hh:mm tt')
$japan = #{
Name = 'japan'
From = "me#me.com"
To = "you#you.com"
Cc = "him#him.com"
}
$ireland = #{
Name = 'ireland'
From = "me#me.com"
To = "you#you.com"
Cc = "her#her.com"
}
$spain = #{
Name = 'spain'
From = "me#me.com"
To = "you#you.com"
Cc = "her#her.com"
}
$_Regions = #()
$_Regions += New-Object PSObject -Property $japan
$_Regions += New-Object PSObject -Property $ireland
$_Regions += New-Object PSObject -Property $spain
ForEach ($_Region in $_Regions) {
#Searches dir for list , formats
$Attachment = Get-ChildItem -Path $dir -Filter "*$($_Region.name)*" -Recurse
$AttachmentName = $Attachment.BaseName
$Subject = "$AttachmentName"
$Body = "Please find attached the Missing Image Report for $($_Region.name).
Produced # $Time
Regards,
John Doe
"
#Actions Email
Send-MailMessage -From $_Region.From -To $_Region.To -CC $_Region.Cc -Subject $Subject -Body $Body -SmtpServer $SMTPServer -Attachments $Attachment.FullName
$Attachment | Move-Item -Destination "C:\Users\user\Desktop\reprts\old"
}
I added some error handling.
The region-objects will be returned, if it send successfully or not, but with a message, where/why it failed
#Defines Directory
$dir = "C:\Users\user\Desktop\reprts\Todays"
#Sets STMP server
$SMTPServer = "10.0.0.46"
#Declares todays time and formats
$Time = (Get-Date).ToString('MM/dd/yyyy hh:mm tt')
$_Regions = #(
#{
Name = 'japan'
From = "me#me.com"
To = "you#you.com"
Cc = "him#him.com"
},
#{
Name = 'ireland'
From = "me#me.com"
To = "you#you.com"
Cc = "her#her.com"
},
#{
Name = 'spain'
From = "me#me.com"
To = "you#you.com"
Cc = "her#her.com"
}
)
ForEach ($_Region in $_Regions) {
$null = $_Region.add('action','Starting work')
#Searches dir for list , formats
try {
$Attachment = Get-ChildItem -Path $dir -Filter "*$($_Region.name)*" -Recurse -ErrorAction Stop
$AttachmentName = $Attachment.BaseName
$Subject = "$AttachmentName"
$Body = "Please find attached the Missing Image Report for $($_Region.name).
Produced # $Time
Regards,
John Doe
"
#Actions Email
Send-MailMessage -From $_Region.From -To $_Region.To -CC $_Region.Cc -Subject $Subject -Body $Body -SmtpServer $SMTPServer -Attachments $Attachment.FullName -ErrorAction Stop
$Attachment | Move-Item -Destination "C:\Users\user\Desktop\reprts\old" -ErrorAction Stop
$_Region.action = 'success'
}
catch {
$_Region.action = $_ #catching the error
}
finally {
$_Region | Select-Object name, action
}
}
Related
How can I configure this script so that I can repeat this mailing process for multiple addresses eg Europe, Asia without copying the code multiple times?
For example could I do something like this?:
IF $keyword "Europe" found THEN
$europe
$europe = Does the Europe stuff
THEN
IF $keyword "Asia" found THEN
$asia
etc.
Here is my code:
# Email Automation
#Defines Directory
$dir = "C:\Users\user\Desktop\myfolder"
#Sets STMP server
$SMTPServer = "10.0.0.1"
#Declares todays time and formats
$Time = (Get-Date).ToString('MM/dd/yyyy hh:mm tt')
# Europe #
#Declares the keyword used to find List
$keywordEur = "Europe"
#Searches dir for list , formats
$AttachmentEur = Get-ChildItem -Path $dir -Filter "*$keywordEur*" -Recurse
$AttachmentNameEur = $AttachmentEur.BaseName
#Defines mailing list
$FromEur = "me#email.com"
$ToEur = "you#europeemail.com"
$CcEur = "him#email.com", "her#email.com"
$SubjectEur = "$AttachmentName # $Time"
$BodyEur = "Please find attached the file needed for Europe.
Regards,
Me
"
#Actions Email
Send-MailMessage -From $FromEur -To $ToEur -CC $CcEur -Subject $SubjectEur -Body $BodyEur -SmtpServer $SMTPServerEur -Attachments $AttachmentEur.FullName
# Asia #
#Declares the keyword used to find List
$keywordAs = "Asia"
#Searches dir for list , formats
$AttachmentAs = Get-ChildItem -Path $dir -Filter "*$keywordAs*" -Recurse
$AttachmentNameAs = $AttachmentAs.BaseName
#Defines mailing list
$FromAs = "me#email.com"
$ToAs = "you#asiaemail.com"
$CcAs = "him#email.com", "her#email.com"
$SubjectAs = "$AttachmentNameAs # $Time"
$BodyAs = "Please find attached the file needed for Asia.
Regards,
Me
"
#Actions Email
Send-MailMessage -From $FromAs -To $ToAs -CC $CcAs -Subject $SubjectAs -Body $BodyAs -SmtpServer $SMTPServerAs -Attachments $AttachmentAs.FullName
Here's how you could repeat it with a ForEach loop and an object built with two hashtables for the regions:
# Email Automation
#Defines Directory
$dir = "C:\Users\user\Desktop\myfolder"
#Sets STMP server
$SMTPServer = "10.0.0.1"
#Declares todays time and formats
$Time = (Get-Date).ToString('MM/dd/yyyy hh:mm tt')
$Europe = #{
Name = 'Asia'
From = "me#email.com"
To = "you#email.com"
Cc = "him#email.com", "her#email.com"
}
$Asia = #{
Name = 'Asia'
From = "me#email.com"
To = "you#email.com"
Cc = "him#email.com", "her#email.com"
}
$Regions = #()
$Regions += New-Object PSObject -Property $Asia
$Regions += New-Object PSObject -Property $Europe
ForEach ($Region in $Regions) {
#Searches dir for list , formats
$Attachment = Get-ChildItem -Path $dir -Filter "*$($Region.name)*" -Recurse
$AttachmentName = $Attachment.BaseName
$Subject = "$AttachmentName # $Time"
$Body = "Please find attached the file needed for $($Region.name).
Regards,
Me
"
#Actions Email
Send-MailMessage -From $Region.From -To $Region.To -CC $Region.Cc -Subject $Subject -Body $Body -SmtpServer $SMTPServer -Attachments $Attachment.FullName
}
Yes there is. Since the keywords themselves are the only thing that seems to change, put your keywords in an array and wrap the entire thing in a loop:
# Email Automation
#Define Directory
$dir = "C:\Users\user\Desktop\myfolder"
#Set STMP server
$SMTPServer = "10.0.0.1"
#Declare todays time and formats
$Time = (Get-Date).ToString('MM/dd/yyyy hh:mm tt')
#Define mailing list
$From = "me#email.com"
$To = "you#email.com"
$Cc = "him#email.com", "her#email.com"
#Declares the keywords used to find attachments
$keywords = 'Europe','Asia'
foreach($keyword in $keywords){
#Searches dir for list , formats
$Attachment = Get-ChildItem -Path $dir -Filter "*$keyword*" -Recurse
$AttachmentName = $Attachment.BaseName
$Subject = "$AttachmentName # $Time"
$Body = "Please find attached the file needed for $keyword.
Regards,
Me
"
#Actions Email
Send-MailMessage -From $From -To $To -CC $Cc -Subject $Subject -Body $Body -SmtpServer $SMTPServer -Attachments $Attachment.FullName
}
This error appears when running the script below:
Send-MailMessage : Cannot validate argument on parameter 'Subject'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
The email still sends successfully and the subject appears correctly.
$dir = "C:\Users\user\Desktop\Lists\TodaysLists"
$SMTPServer = "192.168.1.111"
$Time = (Get-Date).ToString('MM/dd/yyyy hh:mm tt')
$japan = #{
Name = 'Japan'
From = "me#me.com
To = "you#me.com"
Cc = "him#me.com"
}
$ireland = #{
Name = 'Ireland'
From = "me#me.com
To = "you#me.com"
Cc = "him#me.com"
}
$Regions = #()
$Regions += New-Object PSObject -Property $japan
$Regions += New-Object PSObject -Property $ireland
foreach ($Region in $Regions) {
$Attachment = Get-ChildItem -Path $dir -Filter "*$($Region.Name)*" -Recurse
$AttachmentName = $Attachment.BaseName
$Subject = "$AttachmentName"
$Body = "Please find attached the Report for $($Region.Name).
Produced # $Time
Regards,
John Doe
"
Send-MailMessage -From $Region.From -To $Region.To -CC $Region.Cc -Subject $Subject -Body $Body -SmtpServer $SMTPServer -Attachments $Attachment.FullName
$Attachment | Move-Item -Destination "C:\Users\user\Desktop\Lists\oldLists"
}
My guess would be that $Attachment = Get-ChildItem -Path $dir -Filter "*$($Region.Name)*" -Recurse is not returning any files for one or more of the regions, so $Subject ends up being $null.
You could either check for that state and perhaps throw a warning instead of attempting to send the mail, or another way to work around the error (and get an email sent but with a blank subject) would be to add some other (guaranteed) text to $subject. E.g:
$Subject = "$($Region.Name): $AttachmentName"
Although then I suspect it would complain about -Attachments being null.
To add a check/throw warning, you could do the following:
foreach ($Region in $Regions) {
$Attachment = Get-ChildItem -Path $dir -Filter "*$($Region.Name)*" -Recurse
If ($Attachment) {
$AttachmentName = $Attachment.BaseName
$Subject = "$AttachmentName"
$Body = "Please find attached the Report for $($Region.Name).
Produced # $Time
Regards,
John Doe
"
Send-MailMessage -From $Region.From -To $Region.To -CC $Region.Cc -Subject $Subject -Body $Body -SmtpServer $SMTPServer -Attachments $Attachment.FullName
$Attachment | Move-Item -Destination "C:\Users\user\Desktop\Lists\oldLists"
} Else {
Write-Warning "One or more files named $($Region.Name) were not found in $dir. Mail not sent."
}
}
So I created the script below:
$target = "c:\$(get-date -F 'yyyy-MM')"
if (!(Test-Path $target)) {md $target}
gci 'c:\test\' -Filter *.xml -recurse | ?{!$_.PSIsContainer -and $_.CreationTime -ge (get-date "01.$((get-date).Month)")} | copy-item -Destination $target -Force
Could somebody point me to the right direction of how to send a email to a specific address after this script was successfully executed ?
As arco444 noted in his comment, send-mailmessage will allow you to send email from any machine running Powershell v2 or higher, as long as you have an accessible SMTP server.
you can try below solution in PowerShell
$EmailFrom = "someone1#company.com"
$EmailTo = "someone2#company.com"
$ccUsers="someone3#company.com"
$SMTPServer = "smtp.office365.com"
$port=587
$smtp = New-Object Net.Mail.SmtpClient($SMTPServer,$port)
$smtp.EnableSsl = "true"
$smtp.Credentials =New-Object System.Net.NetworkCredential($userName, $password);
$msg = New-Object Net.Mail.MailMessage
$msg.From = $EmailFrom
$msg.To.Add($EmailTo)
$msg.CC.Add($ccUsers)
$msg.IsBodyHTML = $true
$msg.Subject = "Test Subject";
#if you want to send some html in your email
$html = "<html><body><table width='100%'><tr bgcolor='#CCCCCC'><td height='25' align='center'> <font face='tahoma' color='#003399' size='4'><strong>Test Email from PowerShell</strong></font></td> </tr> </table></body></html>" $msg.Body = $html; $smtp.Send($msg);
If your using outlook, here is a example
[system.reflection.assembly]::loadwithpartialname("Microsoft.office.interop.outlook")
$OL=New-Object -ComObject OUTLOOK.APPLICATION
$ns =$OL.GETNAMESPACE("MAPI")
$mail =$ol.createitem(0)
$mail.recipients.add("something#live.nl")
$mail.CC=("something#ziggo.nl")
$MAIL.Subject= "$shortname"
#gets a signature
$mail.htmlbody= get-content $env:appdata\Microsoft\Signatures\*.txt
#for attachments..
$mail.Attachments.Add($file.FullName)
$lines=get-content $filename
$lines+=get-content $env:appdata\Microsoft\Signatures\*.txt
clear-variable text
foreach ($line in $lines)
{
$text += $line + "`r`n"
}
$mail.body= $text
$mail.display()
I am wanting to get the row count from each iteration of my Function and assign it to a variable so I can send the results in an email at the end. My syntax produced an error of this which says to me the $rowcount is never actually being assigned to it?
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.
#Declaring Global Variable
$myArray = $null
$GoodSyntax = "Select * From tableunknown"
$extractFile = "C:\Test.csv"
$dirName = "C:\Completed\"
$date = Get-Date -f 'MM.dd.yy'
#Call function
Execute-SQLquery
if (Execute-SQLquery $GoodSyntax)
$EmailBody = $myArray | Out-String
send-mailmessage -to "abc123#gmail.com" -from "barkbarksalon123#gmail.com" -Body $EmailBody -BodyAsHtml:$true -subject "Testing Through Powershell"
Function Execute-SQLquery {
param ($GoodSyntax)
$server = "Server01"
$database = "database01"
$connectionTemplate = "Data Source={0};Integrated Security=SSPI;Initial Catalog={1};"
$connectionString = [string]::Format($connectionTemplate, $server, $database)
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
$command = New-Object System.Data.SqlClient.SqlCommand
$command.CommandText = $QueryString
$command.Connection = $connection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $command
$DataSet = New-Object System.Data.DataSet
$rowCount = $SqlAdapter.Fill($DataSet)
if ($rowCount -gt 0)
{
[System.IO.Directory]::CreateDirectory($dirName)
$filename = [IO.Path]::GetFileNameWithoutExtension($extractFile) + "_$date" + [IO.Path]::GetExtension($extractFile)
$extractFile = Join-Path $dirname $filename
$DataSet.Tables[0] | Export-Csv $extractFile -NoTypeInformation
$myArray += $rowCount
}
$connection.Close()
}
$rowCount isn't global ... global variables are listed at the top of your code ... it should read like this ...
#Declaring Global Variable
$myArray = $null
$rowCount = 0
$GoodSyntax = "Select * From tableunknown"
...
Make sense?
EDIT:
My bad! I see you are adding $rowCount to $myArray ... that wasn't clear from your question.
Try this ...
#Declaring Global Variable
$rowCountTotal = 0
$GoodSyntax = "Select * From tableunknown"
...
$EmailBody = $rowCountTotal | Out-String
...
$DataSet.Tables[0] | Export-Csv $extractFile -NoTypeInformation
$rowCountTotal += $rowCount
}
I need to find the most recent file in a folder/dir and send that file as an attachment in a email, so far i have this code that find the most recent file in my windows SO, but i need to specify a route a find the most recent file there and then send that file in a email so far i have this:
EDIT 1:
So far i have this:
This part gives me the last file created in a dir/folder:
$dir = "D:\Users\myUser\Desktop\dirTest"
$latest = Get-ChildItem -Path $dir | Sort-Object LastAccessTime -Descending | Select-Object -First 1
$latest.Fullname
$attachment = $latest.Fullname
And this send the email (i'm using yahoo accounts):
$emailSmtpServer = "smtp.mail.yahoo.com"
$emailSmtpServerPort = "587"
$emailSmtpUser = "yahooAccountThatSendsTheEmail#yahoo.com"
$emailSmtpPass = "passForThisquestion"
$emailMessage = New-Object System.Net.Mail.MailMessage
$emailMessage.From = "yahooAccountThatSendsTheEmail#yahoo.com"
$emailMessage.To.Add( "yahooAccountThatRECIEVESTheEmail#yahoo.com" )
$emailMessage.Subject = "Testing e-mail"
$emailMessage.Body = "email from power shell"
$emailMessage.Attachments.Add( $attachment ) <---- this part gives me problems
$SMTPClient = New-Object Net.Mail.SmtpClient($emailSmtpServer, $emailSmtpServerPort)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential($emailSmtpUser, $emailSmtpPass);
$SMTPClient.Send($emailMessage)
It works now, this is my final script.
This script search the most recent file created in a Dir and it sends that file created to a email account.
Here is my script it works for me but it takes a few minutes to send the email, thanks for the help
This script do what i wanted, it find the most recent file and send tha file in a email.
$dir = "d:\Users\myUser\Desktop\testDir"
$latest = Get-ChildItem -Path $dir | Sort-Object LastAccessTime -Descending | Select-Object -First 1
$latest.Fullname
$attachment = $latest.Fullname
$emailSmtpServer = "smtp.mail.yahoo.com"
$emailSmtpServerPort = "587"
$emailSmtpUser = "test_sender_mail_account#yahoo.com"
$emailSmtpPass = "MyPassword"
$emailMessage = New-Object System.Net.Mail.MailMessage
$emailMessage.From = "test_sender_mail_account#yahoo.com"
$emailMessage.To.Add( "test_receiver_mail_account#gmail.com" )
$emailMessage.Subject = "My Subject"
$emailMessage.Body = "My body message"
$emailMessage.Attachments.Add($attachment)
$SMTPClient = New-Object Net.Mail.SmtpClient($emailSmtpServer, $emailSmtpServerPort)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential($emailSmtpUser, $emailSmtpPass);
$SMTPClient.Send($emailMessage)
With PowerShell, something like this should do alright:
function Send-RecentFile {
param(
[ValidateScript({Test-Path $_ })]
[String] $Path
)
$file = Get-ChildItem -Path $Path -File | Sort CreationTime | Select -Last 1
Write-Output "The most recently created file is $($file.Name)"
$messageParameters = #{
From = "myaccount#mydomain.com"
To = "destinationAccount#theirdomain.com"
Subject = "title"
Body = "message body"
SMTPServer = "mail.mydomain.com"
Attachments = $file.FullName
}
Send-MailMessage #messageParameters -Credential (Get-Credential "peopleo#anotherDomain.com")
}
If you do want to store the credentials in the file, you might have to do something slightly different with it.
Please check this powershell command.
$Body = “get the information here to show the data with attachement”
$dir = "Path\of\the\folder(C:\..\..)"
$latest = Get-ChildItem -Path $dir | Sort-Object LastAccessTime -Descending | Select-Object -First 1
$latest.Fullname
$file = $latest.Fullname
$EmailFrom = “sender#gmail.com”
$EmailTo = “receiver#gmail.com”
$SMTPServer = “smtp.gmail.com”
$EmailSubject = “Enter Your Subject”
$att = new-object Net.Mail.Attachment($file)
$mailmessage = New-Object system.net.mail.mailmessage
$mailmessage.from = ($EmailFrom)
$mailmessage.To.add($EmailTo)
$mailmessage.Subject = $EmailSubject
$mailmessage.Body = $Body
$mailmessage.IsBodyHTML = $true
$mailmessage.Attachments.Add($att)
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential(“sender_username”, “sender_password”);
$SMTPClient.Send($mailmessage)
$att.Dispose()