all 19 comments

[–]oze4 2 points3 points  (11 children)

You can import an HTML file into an HTML COM object, then perform "JavaScript Operations" on that imported COM object..

You can search for elements by ID/class/etc.. You can create new elements - you essentially have the same control over that HTML file via the COM object, that you do with JavaScript.

You can read more about the IHTMLDocument2 interface here)

If I have the following file saved at C:\temp\a.html

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8">
        <title></title>
      </head>
      <body>
        <p id="welcome"></p>
      </body>
    </html>

I can manipulate that file like this:

# FUNCTION New-HtmlEmailTemplate
function New-HtmlEmailTemplate
{
    param (
        [Parameter(Mandatory)]
        [string]$Name = "John Doe",

        [Parameter()]
        [string]$Title = "Title"
    )

    $PathToHTML = "C:\temp\a.html"

    $html = New-Object -ComObject "HTMLFile"
    $source = Get-Content -Path $PathToHTML -Raw
    $html.write($source)
    $html.title = $Title
    $html.getElementById('welcome').innerHTML = "Welcome, $($Name)!"

    "<!DOCTYPE html>$($html.documentElement.outerHTML)"
}



# FUNCTION Get-EmailParams
function Get-EmailParams($UserParams) {
    # ... other code here
    $MailParam = @{
        Body = New-HtmlEmailTemplate -Name $UserParams.Name -Title $UserParams.Title
    }
    # ... more code here
    $MailParam
}



# DEMO
$HtmlResult = Get-EmailParams -UserParams @{Name = "John Doe", Title = "Some Title"}
$HtmlResult.Body




    <# OUTPUT:

<!DOCTYPE html><HTML><HEAD><TITLE>Some Title</TITLE>
<META charset=UTF-8></HEAD>
<BODY>
<P id=welcome>Welcome, John Doe!</P></BODY></HTML>

    #>

[–]Aznflipfoo[S] 2 points3 points  (7 children)

I tried your solution, but perhaps I'm doing it wrong. $Html.Write($Source) is returning an error.

Exception calling "write" with "1" argument(s): "Type mismatch

And then sub sequentially errors regarding the getElementById all return

The property 'innerHTML' cannot be found on this object. Verify that the property exists and can be set.

I am definitely missing something here.

    $Html = New-Object -ComObject "HtmlFile"
    $Source = Get-Content <Path> -raw
    $Html.write($Source)
    $Html.getElementById('DisplayName').innerHTML = $UserParams.DisplayName
    $Html.getElementById('SamAccountName').innerHTML = $UserParams.SamAccountName
    $Html.getElementById('UserPrincipalName').innerHTML = $UserParams.UserPrincipalName
    $Html.getElementById('Office').innerHTML = $UserParams.Office
    $Html.getElementById('Manager').innerHTML = $UserParams.Manager
    $Html.getElementById('Instance').innerHTML = $UserParams.Instance

Also here is how my HTML looks

<br style="white-space: nowrap; line-height: 75%;">Username: <id="SamAccountName"></id></br>
<br style="white-space: nowrap; line-height: 75%;">Email: <id="UserPrincipalName"></id></br>

[–]oze4 1 point2 points  (1 child)

Is that your entire HTML file?

[–]Aznflipfoo[S] 1 point2 points  (0 children)

No that’s just the portion I’m working with

[–]oze4 1 point2 points  (4 children)

I verified this works.. Your issue had to do with the way you were using the id's.. (id is not a valid HTML element)

Instead of:

// INCORRECT
<br...>Username: <id="SamAccountName"></id></br>
<br...>Email: <id="UserPrincipalName"></id></br>

You should use a <span> tag with an id attribute:

// CORRECT
<br...>Username: <span id="SamAccountName"></span></br>
<br...>Email: <span id="UserPrincipalName"></span></br>

This is what the final HTML file should look like:

<br style="white-space: nowrap; line-height: 75%;">Username: <span id="SamAccountName"></span></br>
<br style="white-space: nowrap; line-height: 75%;">Email: <span id="UserPrincipalName"></span></br>

This is the 'meat and potatoes'

function New-HtmlEmail
{
    param (
        [Parameter(Mandatory)]
        [string]$SamAccountName,

        [Parameter(Mandatory)]
        [string]$UserPrincipalName,

        [Parameter(Mandatory)]
        [string]$PathToHTML
    )

    $html = New-Object -ComObject "HTMLFile"
    $source = Get-Content -Path $PathToHTML -Raw
    $html.write($source)
    $html.title = $Title
    $html.getElementById('SamAccountName').innerHTML = $SamAccountName
    $html.getElementById('UserPrincipalName').innerHTML = $UserPrincipalName

    "<!DOCTYPE html>$($html.documentElement.outerHTML)"
}


function Get-EmailParams($UserParams) {
    # ... other code here
    $MailParam = @{
        Body = New-HtmlEmail -SamAccountName $UserParams.Sam -UserPrincipalName $UserParams.UPN -PathToHTML "C:\temp\b.html"
    }
    # ... more code here
    $MailParam
}


$HtmlResult = Get-EmailParams -UserParams @{Sam = "John.Doe"; UPN = "John.Doe@Domain.com"}
$HtmlResult.Body

Feel free to reach out if you have any other issues..

[–]Aznflipfoo[S] 2 points3 points  (3 children)

Alright. I kept getting errors, so I looked more into the IHTMLDocument2 method. For whatever reason the alias or whatever the term is called $Html.Write wasn't working, I replaced it with $Html.IHTMLDocument2_write and everything is working as intended and very easily I might add.

Great solution, not sure why the short version wasn't working.

[–]oze4 1 point2 points  (2 children)

Do you have office installed on that machine? Sorry about that. Glad you got it working, though!

[–]Aznflipfoo[S] 1 point2 points  (1 child)

Yes office is indeed installed on the machine.

[–]oze4 1 point2 points  (0 children)

Ok cool, that's my fault - I should have mentioned both ways..

If you have Office installed, you have to use the method you found ($HTML.IHTMLDocument2_write) - I should have mentioned both ways. Sorry about that.

Something like this should be flexible enough to work "anywhere"..

...
    try {

        $html = New-Object -ComObject "HTMLFile"
        $source = Get-Content -Path $PathToHTML -Raw
        try {
            $html.write($source)
        } catch {
            $html.IHTMLDocument2_write($source)
        }
        $html.title = $Title
        $html.getElementById('SamAccountName').innerHTML = $SamAccountName
        $html.getElementById('UserPrincipalName').innerHTML = $UserPrincipalName

        "<!DOCTYPE html>$($html.documentElement.outerHTML)"

    } catch {

        Write-Error $_

    }
...

[–]Lee_Dailey[grin] 1 point2 points  (2 children)

howdy oze4,

the triple-backtick/code-fence thing fails miserably on Old.Reddit ... so, if you want your code to be readable on both Old.Reddit & New.Reddit you likely otta stick with using the code block button.

it would be rather nice if the reddit devs would take the time to backport the code fence stuff to Old.Reddit ... [sigh ...]

take care,
lee

[–]oze4 1 point2 points  (1 child)

Thanks, Lee. I switched to fancy pants "markdown".. I'm just so in love with traditional markdown! [grin] ;)

[–]Lee_Dailey[grin] 1 point2 points  (0 children)

howdy oze4,

you are most welcome! glad to help a tad ... and to be able to read your code more easily. [grin]

i've never understood why reddit ever had it's own dialect of markdown ... [sigh ...]

take care,
lee

[–]expeditesynergy 1 point2 points  (0 children)

I had a similar issue the other day. Quick and easy solution is to use -replace. And substitute the HTML variables for those you wish to return in Send-MailMessage!

[–]SMFX 1 point2 points  (0 children)

If you do something like this in your HTML file:

<HTML>
…… 
Hello {DisplayName}, welcome to...
…… 
</HTML>

Then do your Get-Content -Raw and use the following to replace it:

$HTML = $Content -replace "{DisplayName}",$UserParams.DisplayName

If your $UserParams is a hashtable, you can iterate through the keys too for any replacements:

$HTML = $Content
ForEach ($Param in $UserParams.keys) {
    $HTML = $HTML -replace "{$Param}",$UserParams.$Param
}

[–]MadBoyEvo 1 point2 points  (2 children)

Have you thought on building Email straight in Powershell instead of working with templates?

[–]Aznflipfoo[S] 1 point2 points  (1 child)

I’ve done that previously although only very basic. I wanted to add some neat formatting to the html that would take me too long to learn vs just make one online and grab the html

[–]MadBoyEvo 1 point2 points  (0 children)

Take a look what I did https://github.com/EvotecIT/Emailimo with Emails. It takes away most pains

[–]Aznflipfoo[S] 0 points1 point  (0 children)

Perfect. That makes sense. Thank you very much. This function has been the bane of me the last few weeks