0

I've created an advanced function to send out e-mails based on the answer proposed by Mjolinor. However, I'm facing difficulties to have it accept the HTML code block. It's complaining about the System.String format that cannot be converted to System.Management.Automation.SwitchParameter.

For my HTML-code I'm using a here string, which works fine when used like Send-MailMessage -BodyAsHtml $HTML but not when I try it as below. And I can't seem to figure out how to convert it so it does work.

The error:

An error occurred while enumerating through a collection: Collection was modified; enumeration operation may not execute..
At S:\Test.ps1:168 char:9
+         $EmailParams.keys | Where {$EmailParams.$_ -eq $null} | foreach {$EmailP ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Collecti...tableEnumerator:HashtableEnumerator) [], RuntimeException
    + FullyQualifiedErrorId : BadEnumeration

Failed to send email to User@domain.net due to: Cannot convert 'System.String' to the type 'System.Management.Automation.SwitchParameter' required by parameter 'BodyAsHtml'. 

The code:

$HTML = @"
<!DOCTYPE html>
<html><head><style type="text/css">
body {font-family:verdana;background-color:white;}
h1 {background-color:black;color:white;margin-bottom:5px;}
h2 {background-color:lightGrey;margin-top:5px;}
h3 {margin-left:10px;font-size: 14px;}
p {font-size: 14px;margin-left:10px;}
p.italic {font-style: italic;font-size: 12px;}
table, td, th {font-size: 14px;border-collapse: collapse; border: 1px lightGrey; padding: 3px; text-align: left; padding-right:10px;}
li {font-size: 14px;}
base {target="_blank"}
</style></head><body>
<h1>&nbsp;$(if(!$ScriptName){"Test"}else{$ScriptName})</h1>&nbsp;
<h2>&nbsp;The following has been reported:</h2>
$Messages
</body></html>
"@

        $EmailParams = @{
 To          = $To
 Cc          = $Cc
 From        = $From
 Subject     = $Subject
 BodyAsHtml  = $HTML
 Priority    = $Priority
 SMTPServer  = $SMTPserver
 Attachments = $Attachment
 ErrorAction = 'Stop'
}
$EmailParams.keys | Where {$EmailParams.$_ -eq $null} | foreach {$EmailParams.Remove($_)}
Try { Send-MailMessage @EmailParams }
Catch { "Failed to send email to $($To) due to: $_"  }
Finally {}

This person had a similar problem, but that was only to come to a System.String for the -Body.

Thank you for your help.

There are 2 things wrong here:

  1. BodyAsHtml is a boolean ($True), so you need to use Body to provide your HTML code.
  2. You can't immediately remove stuff from a hashtable. So you need 2 foreach loops.

Full answer:

$HTML = @"
<!DOCTYPE html>
<html><head><style type="text/css">
body {font-family:verdana;background-color:white;}
h1 {background-color:black;color:white;margin-bottom:5px;}
h2 {background-color:lightGrey;margin-top:5px;}
h3 {margin-left:10px;font-size: 14px;}
p {font-size: 14px;margin-left:10px;}
p.italic {font-style: italic;font-size: 12px;}
table, td, th {font-size: 14px;border-collapse: collapse; border: 1px lightGrey; padding: 3px; text-align: left; padding-right:10px;}
li {font-size: 14px;}
base {target="_blank"}
</style></head><body>
<h1>&nbsp;$(if(!$ScriptName){"Test"}else{$ScriptName})</h1>&nbsp;
<h2>&nbsp;The following has been reported:</h2>
$Messages
</body></html>
"@

        $EmailParams = @{
             To          = $To
             Cc          = $Cc
             Bcc         = $Bcc
             From        = $From
             Subject     = $Subject
             Body        = $HTML
             BodyAsHtml  = $True
             Priority    = $Priority
             SMTPServer  = $SMTPserver
             Attachments = $Attachments
             ErrorAction = 'Stop'
        }

        $list = New-Object System.Collections.ArrayList

        foreach ($h in $EmailParams.Keys) {
            if ($($EmailParams.Item($h)) -eq $null) {
                $null = $list.Add($h)
            }
        }

        foreach ($h in $list) {
            $EmailParams.Remove($h)
        }

        Try { 
            Send-MailMessage @EmailParams
            Write-Verbose "Send-Mail: Sending mail to: $To"
        }
        Catch { 
            "Failed to send email to $($To) due to: $_"  
        }
Community
  • 1
  • 1
DarkLite1
  • 13,637
  • 40
  • 117
  • 214
  • 1
    Thanks for this. I was looking to dive right into sending email from powershell (using HTML templates) this was very helpful. I'd like to add, you could improve upon the final solution. The list chunk can be removed completely and replaced with this, cheers: [System.Collections.ArrayList]$EmailParams.keys | ? { $EmailParams.$_ -eq $null } | % {$EmailParams.Remove($_)} –  Dec 20 '16 at 00:47

1 Answers1

7

When in doubt, read the documentation. BodyAsHtml is a boolean value indicating whether or not the body contains HTML. You still need to pass the actual body via the Body parameter. Change this:

$EmailParams = @{
  To          = $To
  Cc          = $Cc
  From        = $From
  Subject     = $Subject
  BodyAsHtml  = $HTML
  Priority    = $Priority
  SMTPServer  = $SMTPserver
  Attachments = $Attachment
  ErrorAction = 'Stop'
}

into this:

$EmailParams = @{
  To          = $To
  Cc          = $Cc
  From        = $From
  Subject     = $Subject
  Body        = $HTML
  BodyAsHtml  = $true
  Priority    = $Priority
  SMTPServer  = $SMTPserver
  Attachments = $Attachment
  ErrorAction = 'Stop'
}
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
  • Thank you Ansgar, this indeed solved the problem with the `-BodyAsHtml`. So stupid I didn't see this myself. But the problem `An error occured while enumerating through a collection` is still there when I don't use an `Attachment`. Very strange.. – DarkLite1 Sep 22 '14 at 11:02
  • Apparently this error always pops-up when one of the values is left empty and `foreach {$EmailParams.Remove($_)}` is executed. The strange thing is that the empty parameter is indeed removed afterwards and the mail is sent, although it displays a red error. I can't even get rid of it with `Out-Null`. – DarkLite1 Sep 22 '14 at 11:24
  • @DarkLite1 Error messages are printed to the Error output stream, whereas `Out-Null` redirects the Success output stream. Use `2>$null` or `2>&1 | Out-Null`. See [here](http://blogs.technet.com/b/heyscriptingguy/archive/2014/03/30/understanding-streams-redirection-and-write-host-in-powershell.aspx) for more information. – Ansgar Wiechers Sep 22 '14 at 12:32
  • It's very bizarre that it gives an error by only deleting something from a hash table.. I don't understand why, as it's a simple `hashtable.remove('stuff')` command. – DarkLite1 Sep 22 '14 at 12:35
  • Fount the solution [here](http://social.technet.microsoft.com/Forums/scriptcenter/en-US/7a94a786-8a0d-4006-8707-cda7edf47e36/powershell-hashtable-deleting-items?forum=ITCG). It appears you can't remove immediately from a hashttable, but you have to collect the results you want to remove first and then delete them in a separate `foreach` loop. OP updated with full answer, thanks guys! – DarkLite1 Sep 22 '14 at 13:11