text $computername $username text "@ $html | ou" /> text $computername $username text "@ $html | ou" /> text $computername $username text "@ $html | ou"/>

placing get-content into array for html email

69 views Asked by At

Html portion


$html = @($htm)

$html = @"

<!doctype html>

<html lang="en">
<head>
<body>
text
$computername
$username
text
</body>
</html>
"@

 $html | out-file c:\scripts\temp\Report.html

I have html language in the $html variable. When I decide to change the HTML code I need to go back into the PowerShell script. I'd rather just have the html in a separate file. How can I use get-content to place the contents of separate file into the $html = @"..."@ variable.
I am using powershell $variables in the html portion to make dynamic emails!

3

There are 3 answers

3
MovGP0 On

Dependent on your use case, a simple string replacement might work:

# read the content of the file
[string]$html = Get-Content `
    -Path 'c:\path\to\template.html' `
    -Encoding 'UTF8' `
    -Raw;

# replace the strings
$html = $html.Replace('$computername', $computername);
$html = $html.Replace('$username', $username);

# write the result to the output file
$html | Out-File 'c:\path\to\report.html';
0
Theo On

If you need to have a HTML template where several variables need to be inserted, I would rather use a structured CSV file than a simple text file to read the variable values needed.

Lets say your csv file looks like this:

"ComputerName","UserName"
"PC0123","Roger Rabbit"
"PC8769","Daffy Duck"
"PC5544","Yosemite Sam"

Option 1: Use a HTML template with numeric placeholders to use with the -f Format operator

$html = @"
<!doctype html>

<html lang="en">
<head>
<body>
text
{0}
{1}
text
</body>
</html>
"@

Then use like

Import-Csv -Path 'X:\Somewhere\UsersAndComputers.csv' | ForEach-Object {
    # replace the placeholder strings {0} and {1} etc. with values from the csv
    $newHtml = $html -f $_.ComputerName, $_.UserName
    # create a path and filename for the filled-in html
    $newFile = 'C:\scripts\temp\Report_{0}.html' -f $_.UserName
    # write the file
    $newHtml | Set-Content -Path $newFile -Encoding UTF8
}

Option 2: Use a HTML template with string placeholders to use with .Replace() or -replace

$html = @"
<!doctype html>

<html lang="en">
<head>
<body>
text
@@COMPUTERNAME@@
@@USERNAME@@
text
</body>
</html>
"@


Import-Csv -Path 'X:\Somewhere\UsersAndComputers.csv' | ForEach-Object {
    # replace the placeholder strings with values from the csv
    $newHtml = $html.Replace('@@COMPUTERNAME@@', $_.ComputerName).Replace('@@USERNAME@@', $_.UserName)
    # create a path and filename for the filled-in html
    $newFile = 'C:\scripts\temp\Report_{0}.html' -f $_.UserName
    # write the file
    $newHtml | Set-Content -Path $newFile -Encoding UTF8
}

Note: Instead of the string .Replace() method you could also use -replace or -creplace regex operators like

    $newHtml = $html -replace '@@COMPUTERNAME@@', $_.ComputerName -replace '@@USERNAME@@', $_.UserName

where -replace is a case-insensitive operator. If you need it to work case-sensitively, then use -creplace

When you need to insert many variables, option 1 would be my preferred way..

2
mklement0 On

You're looking for string templating, i.e. the ability to expand a string with placeholders on demand, based on the then-current values that the placeholders (variables) represent.

PowerShell offers such a feature via the (little-known) .InvokeCommand.ExpandString() method of the automatic $ExecutionContext variable, which treats a string value as if it were an expandable (double-quoted) string ("..."), i.e. performs string interpolation on it:

  • Save your HTML template string via a verbatim string ('...'), i.e., a single-quoted one, so as to prevent instant expansion of embedded variable references such as $computer:
# Note the use of *single* quotes, to ensure
# that $computername and $username *aren't* expanded.
@'
<!doctype html>
<html lang="en">
<head>
<body>
text
$computername
$username
text
</body>
</html>
'@ | Out-File c:\scripts\temp\Report.html
  • Then use the template as follows:
# Read the template in full (-Raw)
$htmlTemplate = Get-Content -Raw c:\scripts\temp\Report.html

# Perform sample instantiations of the template.
1..2 | ForEach-Object {
  # Set the variable values to use in the template.
  $computername = "computer$_"
  $username = "user$_"
  # Now instantiate the template
  $ExecutionContext.InvokeCommand.ExpandString($htmlTemplate)
  "---`n" 
}

This outputs the following - note how $computer and $username were expanded to the then-current values of these variables:

<!doctype html>
<html lang="en">
<head>
<body>
text
computer1
user1
text
</body>
</html>

---

<!doctype html>
<html lang="en">
<head>
<body>
text
computer2
user2
text
</body>
</html>

---

Caveat:

  • The above assumes that you either fully control or implicitly trust the content of your HTML template file, given that it is possible to inject arbitrary commands into the template, using $(...), the subexpression operator

  • See below for a way to prevent expansion of subexpressions.


Preventing expansion of subexpressions ($(...)), to prevent code injection:
  • The following variation ensures that any (unescaped) $(...) sequences are exempt from expansion, i.e. they are retained verbatim.

  • This is achieved by `-escaping the $ in every $( sequence, but only if that $ isn't already escaped. It is the latter requirement that requires the nontrivial regex below. In a nutshell, the $ in $( must only be escaped if it is either preceded by no or an even number of ` chars. (the latter form a sequence of escaped escape characters, which implies that the following $ is unescaped). For an explanation of the regex and the option to experiment with it, see this regex101.com page.

# Read the template in full (-Raw) and escape the $ of unescaped
# $( sequences with ` (backtick), which will prevent expansion of any
# $(...) subexpressions.
$htmlTemplate = 
  (Get-Content -Raw t.html) -replace '(?<!`)((?:``)*)\$\(', '$1`$$('

# Perform sample instantiations of the template.
1..2 | ForEach-Object {
  # Set the variable values to use in the template.
  $computername = "computer$_"
  $username = "user2"
  # Now instantiate the template.
  $ExecutionContext.InvokeCommand.ExpandString($htmlTemplate)
  "---`n" 
}