Html code in Powershell script

I am writing a script that sends a html mail message. Although i have done this many times in the past, i cannot get it work now. it keeps failing on the formatting of the body.

            $Body = 
                "
                <p>Dear $AdManagerobj.GivenName,<br>
                <p><br>
                "

It keeps failing on the ‘,’ or “<”.

Missing Expression after ','  in pipeline element
the '<' operator is reserved for future use

For string variables spanning more than one line you should use a here string. And if you want to expand variables inside this strings you should use the subexpression operator.

$Body = @"
<p>Dear $($AdManagerobj.GivenName),<br>
<p><br>
"@
1 Like

Thanks Olaf. But i tried that before opening this topic.
My original code was=

$Body =
"
<p> Dear $($AdManagerObj.GivenName),<br>
<p><br>
"

and ran into the described issue. Searched around on the internet and indeed came across the here string, wherefore i added the @ signs to the string.

$Body =
@"
<p> Dear $($AdManagerObj.GivenName),<br>
<p><br>
"@

As that did not resolve it, i decided to ask for some help from the experts.
As always, something that looks quite straightforward, turns up to be a total scramble…

So you may explain a little more detailed what issue you actually have. … the bigger picture … Please share all relevant code and the complete error messages you get.

At C:\System\Scripts\PasswordExpiry\PasswordExpiery.ps1:128 char:36
+ <p>Dear $($AdManagerobj.GivenName),<br>
+                                    ~
Missing expression after ',' in pipeline element.
At C:\System\Scripts\PasswordExpiry\PasswordExpiery.ps1:128 char:36
+ <p>Dear $($AdManagerobj.GivenName),<br>
+                                    ~
            $Body = 
@"
<p>Dear $($AdManagerobj.GivenName),<br>
<p><br>
<p>The password of your account $($Obj.Name) has expired. Therefore you will no longer be able to use this account to log on.<br>
<p>You can reset the password via $($link), by logging in with your admin credentials.<br>
<p>After modifying the account's password, you will be able to use the account again.<br>
<p><br>
<p>Kind Regards,<br>
<p><br>
<p>ICT Systems<br>
"@
            }

I had to remove the indent because of the whitespace issue in here strings.

Your posts confuse me more and more … does that mean you solved your issue?

… and yes - at least for the closing here string marks there are no other characters allowed to be in front of it. Just like the documentation states …

A here-string:

  • spans multiple lines
  • begins with the opening mark followed by a newline
  • ends with a newline followed by the closing mark
  • includes every line between the opening and closing marks as part of a single string

Reindent Lines breaks here-strings

My original script followed the indent as this is running in a foreach loop.

            # Retrieving Manager
            $AdManagerObj = Get-ADUser $Obj.Manager -Properties Mail
            #$MesTitle = "Your Admin account's password has expired"
            If ($Null -eq $Obj.AdminDescription) {
            $Body = 
@"
<p>Dear $($AdManagerobj.GivenName),<br>
<p><br>
<p>The password of your account $($Obj.Name), has expired. Therefore you will no longer be able to use this account to log on.<br>
<p>You can reset the password via $($link), by logging in with your admin credentials.<br>
<p>After modifying the account's password, you will be able to use the account again.<br>
<p><br>
<p><br>
<p>Kind Regards,<br>
<p><br>
<p>ICT Systems<br>
"@
            }
            Else {
                $Body = 

Have you solved your issue?

No, still the same error although placing the body in a here-string and using Subexpression Operators.
And still fails on the coma “,” in the here-string:

At C:\System\Scripts\PasswordExpiry\PasswordExpiery.ps1:128 char:36
+ <p>Dear $($AdManagerobj.GivenName),<br>
+                                    ~
Missing expression after ',' in pipeline element.
At C:\System\Scripts\PasswordExpiry\PasswordExpiery.ps1:128 char:36
+ <p>Dear $($AdManagerobj.GivenName),<br>
+   

OK. Final call: Show your code.

… and the code you show should have at least 128 lines!! :point_up:t3:

Start-Transcript -Path C:\System\Scripts\PasswordExpiry\Log\Transcript.txt -Append
$Date = Get-Date -Format dd-M-yyy
function Write-Log {
    [CmdletBinding()]
    param(
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$Message,
 
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('Information','Warning','Error')]
        [string]$Severity = 'Information'
    )
 
    [pscustomobject]@{
        Time = (Get-Date -f g)
        Message = $Message
        Severity = $Severity
    } | Export-Csv -Path "C:\System\Scripts\PasswordExpiry\Log\LogFile_$($Date).csv" -Append -NoTypeInformation
 }
# Script Mode Test or Else
# If scriptmode is test, then the recipient is set to fish.marien@contoso.com
$ScriptMode = "Test"
$Type = "HTML"
$Save = "True"
$MesTitle = "Password expiry Notification"
$SearchOU = "OU=ICT,DC=contoso,DC=com"
Write-Log -Message "$($Date) Script Started" -Severity Information
# MGGraph Connection Parameters
$TenantID = "tenantid"
$ApplicationID = "applicationid"
$ApplicationSecret = "applicationSecret"
$Link = '<a href="https://myaccount.microsoft.com">MyAccount</a>'
# Convert the client secret to a secure string
$ClientSecretPass = ConvertTo-SecureString -String $ApplicationSecret -AsPlainText -Force
Write-Log -Message "Converting Application secret to secure string" -Severity Information
# Create a credential object using the client ID and secure string
$ClientSecretCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $ApplicationID, $ClientSecretPass
Write-Log -Message "Creating Cloud credentials" -Severity Information
# Connect to Microsoft Graph with Client Secret
Write-Log "Connection to Mg Graph" -Severity Information
Connect-MgGraph -TenantId $TenantId -ClientSecretCredential $ClientSecretCredential
# Gettiong Target users
Write-Log -Message "Retrieving users from OU"
$TargetUsers = Get-ADUser -filter {Enabled -eq $True -and PasswordNeverExpires -eq $False} -SearchBase $SearchOU –Properties "DisplayName", "msDS-UserPasswordExpiryTimeComputed", "Manager", "PwdLastSet", "AdminDescription" |
Select-Object -Property "Displayname",@{Name="ExpiryDate";Expression={[datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")}}, "Manager", "DistinguishedName"
$TargetUsersCount = ($TargetUsers | Measure-Object).Count
Write-Log -Message "Retrieved $($TargetUsersCount) users from OU: $($SearchOU)" -Severity Information
# Checking users which have a valid timestamp for Expiry date
$ValidatedUsers = $TargetUsers | Where-Object {$_.ExpiryDate -notlike "*1601*"}
$ValidatedUsersCount = ($ValidatedUsers | Measure-Object).Count
$UnvalidatedUsers = $TargetUsers | Where-Object {$_.ExpiryDate -like "*1601*"}
$UnvalidatedUsersCount = ($UnvalidatedUsers | Measure-Object).Count
Write-Log -Message "Retrieved $($UnvalidatedUsersCount) users, which are missing an ExpiryDate value. This users are not handled by the script" -Severity Warning
Write-Log -Message "Unvalidated users" -Severity Information
foreach ($obj in $UnvalidatedUsers) {
    Write-Log -Message "$($Obj.Name)" -Severity Information
}
Write-Log -Message "Retrieved $($ValidatedUsersCount) users, which are subject to the script" -Severity Information
foreach ($obj in $ValidatedUsers) {
    Write-Log -Message "$($Obj.Name)" -Severity Information
}
# retrieving the managers and peronalizing mail
Write-Log -Message "Getting validated users which password's will or has expired. Timeperiod = 21 days" -Severity Information
$Notify = $ValidatedUsers | Where-Object {$_.ExpiryDate -lt (Get-Date).AddDays(+21)}
Foreach ($Obj in $Notify) {
    if ($Obj.expirydate -lt (get-date)) {
        Write-Log -Message "The password of account $($Obj.name) has already expired" -Severity Information
        # Retrieving Manager info
        If ($null -eq $Obj.manager) {
            Write-Log -Message "The Account $($Obj.name) has no manager value defined, wherefore a mailconnect be send" -Severity Warning
            #$MesTitle = "The Password of account $($Obj.Name) has expired"
            $Body = 
@"
<p>Dear ServiceDesk,<br>
<br>
The password of account $($Obj.Name) has expired.<br>
The account does not have a manager specified wherefore the message is sent to the ServiceDesk.<br>
As the password has been expired, the account will no longer be able to log in.<br>
The account is located here: $($Obj.DistinguishedName)<br>
<br>
Kind Regards,<br>
<br>
ICT Systems<br></p>
"@
            If ($ScriptMode -eq "Test") {
                $Sender = "fish.marien@contoso.com"
                $Recipient = "fish.marien@contoso.com"
            }
            Else {
                $Sender = "ict-systems@contoso.com"
                $Recipient = "servicedesk@contoso.com"
            }
            $Params = @{
                Message         = @{
                    Subject       = $MesTitle
                    Body          = @{
                        ContentType = $type
                        Content     = $body
                    }
                    ToRecipients  = @(
                        @{
                            EmailAddress = @{
                                Address = $recipient
                            }
                        }
                    )
                }
                SaveToSentItems = $save
            }
            Send-MgUserMail -UserId $Sender -BodyParameter $Params
            Write-Log -Message "$($Obj.Name) has no manager defvined, wherefore the message is sent to the ServicDesk" -Severity Information
        }
        Else {
            # Retrieving Manager
            $AdManagerObj = Get-ADUser $Obj.Manager -Properties Mail
            #$MesTitle = "Your Admin account's password has expired"
            If ($Null -eq $Obj.AdminDescription) {
            $Body = 
@"
<p>Dear $($AdManagerobj.GivenName),<br>
<p><br>
<p>The password of your admin account $($Obj.Name), has expired. Therefore you will no longer be able to use this account to log on.<br>
<p>You can reset the password via $($link), by logging in with your admin credentials.<br>
<p>After modifying the account's password, you will be able to use the account agaoin.<br>
<p><br>
<p>If you feel this mail is inappropriate, or does not apply to you, please contact the service desk for assistance.<br>
<p><br>
<p>Kind Regards,<br>
<p><br>
<p>ICT Systems<br>
"@
            }
            Else {
                $Body = 
@"
<p>Dear $($AdManagerObj.GivenName),<br>
<br>
<p>The password of your account $($Obj.Name), has expired. Therefore you will no longer be able to use this account to log on.<br>
<p>Please contact the ServiceDesk, they will create a support ticket and assign it to the propper team(s).<br>
<p><br>
If you feel this mail is inappropriate, or does not apply to you, please contact the ServiceDesk for assistance.<br>
<br>
Kind Regards,<br>
<br>
ICT Systems</p>
"@
            }
            If ($ScriptMode -eq "Test") {
                $Sender = "fish.marien@contoso.com"
                $Recipient = "fish.marien@contoso.com"
            }
            Else {
                $Sender = "ict-systems@contoso.com"
                $Recipient = $AdManagerObj.mail
            }
            $Params = @{
                Message         = @{
                    Subject       = $MesTitle
                    Body          = @{
                        ContentType = $type
                        Content     = $body
                    }
                    ToRecipients  = @(
                        @{
                            EmailAddress = @{
                                Address = $recipient
                            }
                        }
                    )   
                }
                SaveToSentItems = $save
            }
            Send-MgUserMail -UserId $Sender -BodyParameter $Params
            Write-Log -Message "$($Obj.name) has a manager defined. The notification message was sent to $($ADManagerObj.Name)" -severity Information
        }
    }
    Else {
        # Calculating Days the password remains valid
        $EndDate = $Obj.ExpiryDate
        $StartDate = (Get-Date)
        $DaysToGo = New-Timespan -Start $StartDate -End $EndDate
        # Retrieving Manager
        $AdManagerObj = Get-ADUser $Obj.Manager -Properties Mail
        if ($Null -eq $Obj.AdminDescription) {
            $Body = 
@"
<p>Dear $($AdManagerObj.GivenName),<br>
<br>
The password of your admin account $($Obj.Name) will expire on $($Obj.ExpiryDate), which is wwithin $($DaysToGo.days) days.<br>
When the password has effectively has expired, you will no longer be able to use this account untill the password is updated.<br>
You can update the password via $Link, by logging in with your admin credentials.<br><br>
<br>
If you feel this mail is inappropriate, or does not apply to you, please contact the ServiceDesk for assistance.<br>
<br>
Kind Regards,<br>
<br>
ICT Systems</p>
"@
            Write-Log -Message "Users $($Obj.Name) is synced to Entgra AD" -Severity Information
        }
        Else {
            $Body = 
@"
<p>Dear $($AdManagerObj.GivenName),<br>
<br>
<p>The password of your admin account $($Obj.Name) will expire on $($Obj.expirydate), which is wwithin $($DaysToGo.days) days.<br>
<p>When the password has effectively has expired, you will no longer be able to use this account untill the password is updated.<br>
<p>Please update your admin's account password or contact the ServiceDesk for assistance, they will create a support ticket and assign it to the propper support team(s).<br>
<p><br>
<p>If you feel this mail is inappropriate, or does not apply to you, please contact the ServiceDesk for assistance.<br>
<p><br>
<p>Kind Regards,<br>
<p><br>
<p>ICT Systems</p>
"@
            Write-Log -Message "User $($Obj.Name) is not synced with Entra AD" -Severity Information
        }
        If ($ScriptMode -eq "Test") {
            $Sender = "fish.marien@contoso.com"
            $Recipient = "fish.marien@contoso.com"
        }
        Else {
            $Sender = "ict-systems@contoso.com"
            $Recipient = $AdManagerObj.mail
        }
        $Params = @{
            Message         = @{
                Subject       = $MesTitle
                Body          = @{
                    ContentType = $type
                    Content     = $body
                }
                ToRecipients  = @(
                    @{
                        EmailAddress = @{
                            Address = $recipient
                        }
                    }
                )   
            }
            SaveToSentItems = $save
        }
        Send-MgUserMail -UserId $Sender -BodyParameter $Params
        Write-Log -Message "$($Obj.name) has a manager defined. The notification message was sent to $($ADManagerObj.Name)" -Severity Information
    }
}
Write-Log -Message "$($Date) Script ended" -Severity Information
Stop-Transcript

Of course I cannot reproduce your error because I don’t have your environment and I don’t even have all the code you’re using.

In your situation I’d try the following:

Instead of using variable substitution in strings I’d try to use the format operator -f.

So instead of

$Body = 
@"
<p>Dear $($AdManagerobj.GivenName),<br>
<p><br>
<p>The password of your admin account $($Obj.Name), has expired. Therefore you will no longer be able to use this account to log on.<br>
<p>You can reset the password via $($link), by logging in with your admin credentials.<br>
"@

I’d try this:

$Body = 
@'
<p>Dear {0},<br>
<p><br>
<p>The password of your admin account {1}, has expired. Therefore you will no longer be able to use this account to log on.<br>
<p>You can reset the password via {2}, by logging in with your admin credentials.<br>
'@ -f $($AdManagerobj.GivenName), $($Obj.Name), $($link)

If that does not help I’d try to put quotes around the variable name when called … so instead of

$Params = @{
    Message         = @{
        Subject      = $MesTitle
        Body         = @{
            ContentType = $type
            Content     = $body
        }
        ToRecipients = @(
            @{
                EmailAddress = @{
                    Address = $recipient
                }
            }
        )   
    }
    SaveToSentItems = $save
}

I’d call it like this:

$Params = @{
    Message         = @{
        Subject      = $MesTitle
        Body         = @{
            ContentType = $type
            Content     = "$body"
        }
        ToRecipients = @(
            @{
                EmailAddress = @{
                    Address = $recipient
                }
            }
        )   
    }
    SaveToSentItems = $save
}

Besides this I’d recommend considering refactoring your script to get rid of some repetitive code. I’d probably even “outsource” the HTML fragments into external files. For example PowerShell data files. This way you don’t have to deal with “non PowerShell code” inside your PowerShell scripts.

The variable should be in front of the @", not a line above it.

$Body = @"
<p>Dear $($AdManagerobj.GivenName),<br>
<p><br>
<p>The password of your account $($Obj.Name), has expired. Therefore you will no longer be able to use this account to log on.<br>
<p>You can reset the password via $($link), by logging in with your admin credentials.<br>
<p>After modifying the account's password, you will be able to use the account again.<br>
<p><br>
<p><br>
<p>Kind Regards,<br>
<p><br>
<p>ICT Systems<br>
"@

if this code by itself doesn’t cause an error, and you are getting an error in your entire script in this specific portion, then it means there is some unclosed quote/double quote prior to it. I would troubleshoot this by taking each little section of the script starting from the beginning into a new script. Add it and run, check for proper results or errors. Keep adding sections until you find the one that is causing you trouble.

I think that detail does not make any difference. It works both ways. :wink:

Amen to that. :pray:t3: :slightly_smiling_face:

Well it’s ugly :slight_smile:

1 Like