question

LeoJunek-3082 avatar image
0 Votes"
LeoJunek-3082 asked LeoJunek-3082 published

Powershell - sending email with multiple attachments

Hello,

in my Powershell script some logs are detected to be wrong, hence I want them to be sent via email. Every time the script runs the list of logs can be different, so I store the log names in an array. Then I create a long comma separated string with file names and paste the output string after "-Attachment" when sending an email.

However Powershell detects the comma separated string as a single filename / filepath and throws an error. I could overcome the problem by writing single filenames separated with comma, but it does not fit in my script because filenames and number of files can be different every run of the script.

Here is my code, rn ... relative name (path), fn ... full name (path)


         $rn1="imp_qn03.log"
         $rn2="imp_ssp1.log"
         $rn3="imp_ssw2.log"
         $rn4="imp_t2ns01.log"
         $rn_all=$rn1+','+$rn2+','+$rn3+','+$rn4
         $base_dir='D:\TaskScheduler\PadrSQL\ZMENY'
         $fn1=$base_dir+'\'+$rn1
         $fn2=$base_dir+'\'+$rn2
         $fn3=$base_dir+'\'+$rn3
         $fn4=$base_dir+'\'+$rn4
         $fn_all=$fn1+','+$fn2+','+$fn3+','+$fn4
         Write-Debug "rn_all=$rn_all"
         Write-Debug "fn_all=$fn_all"
        
         # Email 1 
         Send-MailMessage -To $to -From $from -Subject "more variables after Attachment rn1..rn4" -Body $body -SmtpServer $smtp_server -Port 25 -BodyAsHtml -Priority Normal -Attachments $rn1,$rn2,$rn3,$rn4
         # Email 2 
         Send-MailMessage -To $to -From $from -Subject "single variable after Attachment rn_all" -Body $body -SmtpServer $smtp_server -Port 25 -BodyAsHtml -Priority Normal -Attachments $rn_all
         # Email 3
         Send-MailMessage -To $to -From $from -Subject "more variables after Attachment fn1..fn4" -Body $body -SmtpServer $smtp_server -Port 25 -BodyAsHtml -Priority Normal -Attachments $fn1,$fn2,$fn3,$fn4
         # Email 4
         Send-MailMessage -To $to -From $from -Subject "single variable after Attachment fn_all" -Body $body -SmtpServer $smtp_server -Port 25 -BodyAsHtml -Priority Normal -Attachments $fn_all
        
         # debug Error email 2 with DEBUG info
         DEBUG: rn_all=imp_qn03.log,imp_ssp1.log,imp_ssw2.log,imp_t2ns01.log
         DEBUG: fn_all=D:\TaskScheduler\PadrSQL\ZMENY\imp_qn03.log,D:\TaskScheduler\PadrSQL\ZMENY\imp_ssp1.log,D:\TaskScheduler\PadrSQL\ZMENY\imp_ssw2.log,D:\TaskScheduler\PadrSQL\ZMENY\imp_t2ns01.log
         Send-MailMessage : Could not find file 'D:\TaskScheduler\PadrSQL\ZMENY\imp_qn03.log,imp_ssp1.log,imp_ssw2.log,imp_t2ns01.log'.
         At D:\TaskScheduler\PadrSQL\kontrola_logu1.ps1:210 char:5
         +     Send-MailMessage -To $to -From $from -Subject "single variable af ...
         + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
             + CategoryInfo          : NotSpecified: (:) [Send-MailMessage], FileNotFoundException
             + FullyQualifiedErrorId : System.IO.FileNotFoundException,Microsoft.PowerShell.Commands.SendMailMessage
        
         # Error email 4 with DEBUG info
         DEBUG: rn_all=imp_qn03.log,imp_ssp1.log,imp_ssw2.log,imp_t2ns01.log
         DEBUG: fn_all=D:\TaskScheduler\PadrSQL\ZMENY\imp_qn03.log,D:\TaskScheduler\PadrSQL\ZMENY\imp_ssp1.log,D:\TaskScheduler\PadrSQL\ZMENY\imp_ssw2.log,D:\TaskScheduler\PadrSQL\ZMENY\imp_t2ns01.log
         Send-MailMessage : The given path's format is not supported.
         At D:\TaskScheduler\PadrSQL\kontrola_logu1.ps1:212 char:5
         +     Send-MailMessage -To $to -From $from -Subject "single variable af ...
         + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
             + CategoryInfo          : NotSpecified: (:) [Send-MailMessage], NotSupportedException
             + FullyQualifiedErrorId : System.NotSupportedException,Microsoft.PowerShell.Commands.SendMailMessage


Email 1 and Email 3 were sent and delivered without problems.

Email 2 error message reveals PowerShell prefixed the whole string with current directory

Email 4 error message does not reveal the problem, it only complains about wrong path format.


Update 1:
Though I mentioned array in the text above, it was not used in mentioned code block, because the root of the problem could be shown with string variables rn1... rn4, fn1...fn4. In my original script I select wrong logs with [Regex]::Matches into variable $rn_files. It is a list of filenames separated by space. Then I create an array $rn_array from $rn_files with Split. Then I form variable $attachment by concatenating array items together with comma as delimiter. However variable $attachment cannot be used after -Attachments when sending emails.

   $rn_files=[Regex]::Matches((Select-String -Path *.log -Pattern $regex),'(imp_[A-Z a-z 0-9 _]*.log):[0-9]*:[0-3][0-9].[0-1][0-9].20[2-9][0-9]-[0-9]*:[0-5][0-9]:[0-5][0-9] import with errors')|ForEach-Object {$_.Groups[1].Value}
   $rn_array=$rn_files.Split(" ")
   $attachment=$rn_array -join ','
   ...
   Send-MailMessage -To ... -Attachments $attachment #throws an error

My goal is to find a way how to use $rn_files or its derivates (e.g. array $rn_array) to send multiple logs as an attachment.

Update 2
Passing filenames with pipe does not solve the problem.

     # No error
     $rn1,$rn2 | Send-MailMessage -To $to -From $from -Subject "more variables after Attachment rn1..rn2" -Body $body-SmtpServer $smtp_server -Port 25 -BodyAsHtml -Priority Normal
        
     # An error is thrown
     $rn_all | Send-MailMessage -To $to -From $from -Subject "single variable after Attachment rn_all" -Body $body -SmtpServer $smtp_server -Port 25 -BodyAsHtml -Priority Normal
    
     # Error content  
     Send-MailMessage : Could not find file 'D:\TaskScheduler\PadrSQL\ZMENY\imp_qn03.log,imp_ssp1.log,imp_ssw2.log,imp_t2ns01.log'.
     At D:\TaskScheduler\PadrSQL\kontrola_logu.ps1:210 char:22
      +     [array]$rn_all | Send-MailMessage -To $to -From $from -Subject "s ...
      +    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         + CategoryInfo          : NotSpecified: (:) [Send-MailMessage], FileNotFoundException
         + FullyQualifiedErrorId : System.IO.FileNotFoundException,Microsoft.PowerShell.Commands.SendMailMessage

Solution
Send-MailMessage accepts array of strings, not a single string. Here is a functional code:

   # Get a single string of filenames separated with space
   $rn_files=[Regex]::Matches((Select-String -Path *.log -Pattern $regex),'(imp_[A-Z a-z 0-9 _]*.log):[0-9]*:[0-3][0-9].[0-1][0-9].20[2-9][0-9]-[0-9]*:[0-5][0-9]:[0-5][0-9] import with errors')|ForEach-Object {$_.Groups[1].Value}
    
   # Create an array of strings using space as delimiter for array items - each item of array contains one filename 
   $rn_array=$rn_files.Split(" ")
      
   # Send email with attachments
   Send-MailMessage ... -Attachments $rn_array


Leos

windows-server-powershell
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

AndreasBaumgarten avatar image
0 Votes"
AndreasBaumgarten answered LeoJunek-3082 commented

In your script the variable $rn_all is a string variable not an array variable
You can verify this: $rn_all.GetType()

You can try to create an array variable instead:

 $rn1="imp_qn03.log"
 $rn2="imp_ssp1.log"
 $rn3="imp_ssw2.log"
 $rn4="imp_t2ns01.log"
 [array]$rn_all=$rn1,$rn2,$rn3,$rn4


Same for the $fn_all:

 $fn1=$base_dir+'\'+$rn1
 $fn2=$base_dir+'\'+$rn2
 $fn3=$base_dir+'\'+$rn3
 $fn4=$base_dir+'\'+$rn4
 [array]$fn_all=$fn1,$fn2,$fn3,$fn4

Maybe this is helpful.


(If the reply was helpful please don't forget to upvote and/or accept as answer, thank you)

Regards
Andreas Baumgarten



· 4
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Thank you, Andreas, for your post. Unfortunately it did not help. When retyping rn_all to array and trying to send an email I got

 Send-MailMessage : Could not find file 'D:\TaskScheduler\PadrSQL\ZMENY\imp_qn03.log,imp_ssp1.log,imp_ssw2.log,imp_t2ns01.log'.
 At D:\TaskScheduler\PadrSQL\kontrola_logu.ps1:211 char:5
 +     Send-MailMessage -To $to -From $from -Subject "single variable af ...
 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     + CategoryInfo          : NotSpecified: (:) [Send-MailMessage], FileNotFoundException
     + FullyQualifiedErrorId : System.IO.FileNotFoundException,Microsoft.PowerShell.Commands.SendMailMessage

Doc to Send-MailMessage says -Attachments should be followed with String[].

0 Votes 0 ·

Using a file simple name instead of a full path name(e.g. "D:\TaskScheduler\PadrSQL\ZMENY\imp_qn03.log") is just asking for trouble.

I'd like to see the code you used when you retyped "$rn_all" to an array. It looks more like you just created a one-element array of four simple file names separated by commas as a string.

0 Votes 0 ·

You are true, thank you!
I did not notice the syntax of Andreas, so my retyping was just prefixig with [array], this way
[array]$rn_all=$rn1+','+$rn2+','+$rn3+','$rn4

So it really was a single item array, not four item. After correction to code Andreas sent it started to work.

0 Votes 0 ·

Thank you, Andresas. Your solution works. First time I retyped only with prefixing with [array]. After reading RichMatheisen-8856 post I double checked it and found that your answer was different.

0 Votes 0 ·
RichMatheisen-8856 avatar image
0 Votes"
RichMatheisen-8856 answered

See if this works any better for you:

 $rn1="imp_qn03.log"
 $rn2="imp_ssp1.log"
 $rn3="imp_ssw2.log"
 $rn4="imp_t2ns01.log"
 $rn_all = $rn1, $rn2, $rn3, $rn4
 #Or, if you don't need $rn1..$rn4, why not this:
 #$rn_all = "imp_qn03.log", "imp_ssp1.log", "imp_ssw2.log", "imp_t2ns01.log"
    
 $base_dir='D:\TaskScheduler\PadrSQL\ZMENY'
 $fn1= Join-Path $base_dir $rn1
 $fn2= Join-Path $base_dir $rn2
 $fn3= Join-Path $base_dir $rn3
 $fn4= Join-Path $base_dir $rn4
 $fn_all=$fn1, $fn2, $fn3, $fn4
 #Or, if you don't need $fn1..$fn4, why not this:
 #$fn_all = $rn_all | ForEach-Object{Join-Path $base_dir $_}
    
 Write-Debug "rn_all=$rn_all"
 Write-Debug "fn_all=$fn_all"
    
 # Use "splatting" to supply long series of parameters
 $Params=@{
     To = $to
     From = $from
     Body = $body
     SmtpServer = $smtp_server
     Port = 25
     BodyAsHtml = $true
     Prority = Normal
     Subject = ""            # filled in later
     Attachments = ""        # filled in later
 }
 # Email 1
 $Params.Subject = "more variables after Attachment rn1..rn4"
 $Params.Attachments = $rn1,$rn2,$rn3,$rn4                       # same as $rn_all
 Send-MailMessage @Params
 # Email 2 
 $Params.Subject = "single variable after Attachment rn_all"
 $Params.Attachments = $rn_all
 Send-MailMessage @Params
 # Email 3
 $Params.Subject = "more variables after Attachment fn1..fn4"
 $Params.Attachments = $fn1,$fn2,$fn3,$fn4                       # same as $fn_all
 Send-MailMessage @Params
 # Email 4
 $Params.Subject = "single variable after Attachment fn_all"
 $Params.Attachments = $fn_all
 Send-MailMessage @Params

In addition to creating the variables properly, you can make the code easier to read by using "splatting". I added a few lines of code (they're commented) to neaten things, too.

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

AndreasBaumgarten avatar image
0 Votes"
AndreasBaumgarten answered

Does this $attachment string work?

 $fn1=$base_dir+'\'+$rn1
 $fn2=$base_dir+'\'+$rn2
 $fn3=$base_dir+'\'+$rn3
 $fn4=$base_dir+'\'+$rn4
 [array]$fn_all="$fn1","$fn2","$fn3","$fn4"
 $attachment = '"{0}"' -f ($fn_all -join '","')
 $attachment

The result will look like this; "C:\test\imp_qn03.log","C:\test\imp_ssp1.log","C:\test\imp_ssw2.log","C:\test\imp_t2ns01.log"


(If the reply was helpful please don't forget to upvote and/or accept as answer, thank you)

Regards
Andreas Baumgarten


5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.