Adding a variable to a -Body when sending an email - powershell

I'm creating a PowerShell script to make our starters and leavers process smoother. We have a separate team who needs to add certain accounts.
What I'd like to do is take the variable that is declared at the start of the script, the new users name, and put it in an email asking for this other department to set them up.
$username = "joe"
Send-MailMessage -SmtpServer smtp.office365.com -From "it#support.com" -To "other#department.com" -Subject 'Starter/Leaver ' -Body "Hi department, `n `nPlease can you add/remove" $username "from the necessary account please `n `nThanks"
I get an error saying:
Send-MailMessage : A positional parameter cannot be found that accepts argument "joe"

The issue here is that the string object sent to -Body is broken because of the quoting. You can just surround the entire body with one set of quotes to achieve the desired result.
$username = "joe"
Send-MailMessage -SmtpServer smtp.office365.com -From "it#support.com" -To "other#department.com" -Subject 'Starter/Leaver ' -Body "Hi department, `n`nPlease can you add/remove $username from the necessary account please `n`nThanks"
Neater Alternative:
I know this answer is not as concise, but it is more readable and adds some flexibility. It uses a here-string to create the body, which doesn't require having to add all of the line feed characters. It also uses splatting to execute the command. With splatting, you can just update the hash table when you need to change something.
$Body = #"
Hi department,
Please can you add/remove $username from the necessary account please
Thanks
"#
$Message = #{ SmtpServer = 'smtp.office365.com'
From = "it#support.com"
To = "other#department.com"
Subject = 'Starter/Leaver'
Body = $Body
}
Send-MailMessage #Message
When running a PowerShell command, parameters are space-delimited. In the case of Send-MailMessage positional parameters are also allowed, which means you don't have to provide the parameter name when passing in a value. In your attempt, your first quote pair was passed to the -Body parameter. After the first closing quote, a space followed by $username is interpreted. Because positional parameters are enabled for the command, PowerShell attempts to assign $username to a parameter and fails. Of course this also means that if you intend to include a space in your string, it must be surrounded by quotes.
Additional Reading:
See About Parameters for an overview of how parameters work.
See About Splatting for information on splatting.

Related

Send emails with Powershell mailto with formated text

My current script creates (after an account modification) a .ps1 file that is send to another computer and there it is executed opening a new Gmail tab with some information hosted in several variables. I need this email to have format like bold, hyper-link, etc.
Im using the start-process 'mailto' for this but i can not find the way to give this email a format (believe me, i have tried), is this even possible?
I appreciate any insights on this.
My current script creates (after an account modification) a .ps1 file that is send to another computer and there it is executed opening a new Gmail tab with some information hosted in several variables. I need this email to have format like bold, hyper-link, etc.
Im using the start-process 'mailto' for this but i can not find the way to give this email a format (believe me, i have tried), is this even possible?
Additional information:
Code:
$outPut = 'Start-Process'
$outPut+= '"mailto:'+$userMail+"?Subject=Password Reset"+"&Body=Hi, your password is $Password"
$outPut+= '";'
$mailFile = "Path" + $user.SAM + ".ps1"
$outPut | Out-File $mailFile
So, this takes the information this way and stored it in a ps1 file, then executed, opening a new Gmail tab with proper data.
I need that some words has format, bold for the password or hyper-link for guideness link...
Regards!
You haven't provided any indication of what you are doing. But the way to send emails via PowerShell is with the Send-MailMessage cmdlet. If you are using Send-MailMessage and formatting the body of the message with HTML, you just need to make sure you are using the -BodyAsHtml argument.
Here's an example:
$html = "<body><h1>Heading</h1><p>paragraph.</p></body>"
Send-MailMessage -To "bob#fake.com" -From "me#fake.com" -Subject "Test" -Body $html -BodyAsHtml

Using pipeline object to populate mail -to and -attachment

First ever Powershell script so any advice or recommendations are appreciated. I'm parsing a .csv into smaller .csv's to send out information about servers to recipients and i'm running into a problem in my foreach. How do I get this to work?
One interesting thing is that in Send-MailMessage, -to should not accept pipeline objects, It still throws an error, but it still sends the emails. However the attachment will never send.
#had to set this as a variable because # was throwing splatting errors
$Mail = "#Email.com"
#Import csv and split information, exports UID.csv
Import-csv C:\path\info.csv | Group-Object UID |
ForEach-Object {
$_.Group | Export-csv "C:\path\$($_.Name).csv" -NoTypeInformation
}
#Import file again to get unique list of UID and send mail with
#respective UID.csv
Import-csv C:\path\info.csv | Group-Object UID |
ForEach-Object {
$_.UID | Send-MailMessage -From "<Me#email.com>" -To "<$($_.Name)$Mail>" `
-Attachments "C:\path\$($_.Name).csv" `
-Subject "Testing" -Body "Please Ignore This" -Priority High `
-SmtpServer smtp.server.com
}
in Send-MailMessage, -to should not accept pipeline objects
In principle it does, namely if the pipeline objects have a .To property (which is not the case for you).
However, with your current approach, you don't need pipeline input at all, given that you're supplying all input as arguments.
Additionally, your pipeline input is incorrect, because $_.UID sends $null through the pipeline, given that $_ - a group-info object output by Group-Object - doesn't have a .UID property.
Using delay-bind script blocks ({ ... }), you can simplify your command as follows, obviating the need for a ForEach-Object call:
Import-csv C:\path\info.csv | Group-Object UID |
Send-MailMessage -From "<Me#email.com>" -To { "<$($_.Name)#Email.com>" } `
-Attachments { "C:\path\$($_.Name).csv" } `
-Subject "Testing" -Body "Please Ignore This" -Priority High `
-SmtpServer smtp.server.com
In short, the script blocks passed to -To and Attachments are evaluated for each input object, and their output determines the parameter value in each iteration. In the script block, $_ represents the pipeline object at hand, as usual.
Note that such delay-bind script blocks can only be used with parameters that are designed to accept pipeline input (irrespective of whether by value (whole object) or by a specific property's value).

Powershell using string and file content in email message body without breaking formatting

I have a Powershell script that has a part which needs to email some information. I have a requirement to include 2 pieces of information inside the message body, but I am having issues with formatting. The first part will be a line of text which I would like to simply store inside the powershell script as a variable, and the second part will be information from a text file. Below is a simplified version of what I want to do:
$INFO1="The following fruits are ready to eat:
"
$INFO2=get-content "info2.txt"
$BODY=$INFO1 + $INFO2
$BODY=$BODY | out-string
send-mailmessage -To myname#mydomain.com -Subject "Fruit Status" -From
fruit#mydomain.com -Body $BODY -Smtpserver smtp.mydomain.org -UseSsl
The file info2.txt contains the following info. 3 columns with a list of fruits with tab separators:
Col1 Col2 Col3
Apple Orange Pear
Banana Grape Strawbery
Kiwi Peach Apricot
The above code works but I get an email which looks like this:
The following fruits are ready to eat:
Col1 Col2 Col3 Apple Orange Pear Banana Grape
Strawbery Kiwi Peach Apricot
It keeps the line spaces in the $INFO1 stored variable string, but then places the content of the info2.txt file all in one continuous line, with no line breaks or tabbed formatting.
I tested this with the below changes whereby the line of text from the $INFO1 variable, is instead obtained by doing a get-content on the same piece of text from inside a file named "info1.txt". When I then append both pieces of information into the message body, the formatting is maintained and the email looks ok.
$INFO1=get-content "info1.txt"
$INFO2=get-content "info2.txt"
$BODY=$INFO1 + $INFO2
$BODY=$BODY | out-string
send-mailmessage -To myname#mydomain.com -Subject "Fruit Status" -From
fruit#mydomain.com -Body $BODY -Smtpserver smtp.mydomain.org -UseSsl
The following fruits are ready to eat:
Col1 Col2 Col3
Apple Orange Pear
Banana Grape Strawbery
Kiwi Peach Apricot*
I also notice that if I do the below using the first method, grabbing the file content first and THEN the string from variable, the formatting is also kept fine:
$BODY=$INFO2 + $INFO1
It is only when I start with the string from variable first, that it mucks up the formatting in the email for the file content part.
I would prefer to keep the script more compact and minimize writing out to files on disk and used stored variables. Is there a way I can use method 1 appending a string or multiple strings, followed by file content where formatting is kept? I do not wish to use HTML for this as it will overly complicate matters with the text file content I am working with so the body is just -body rather than -bodyashtml unless this is the only way to sort this.
I believe I have found the answer. I played with the get-content -RAW argument before but I think I was having trouble using it to get content stored in the pipeline. When I use it as below directly on a file with no other where clauses, it fixes the issue:
$INFO1="The following fruits are ready to eat:
"
$INFO2=get-content "info2.txt" -raw
$BODY=$INFO1 + $INFO2
$BODY=$BODY | out-string
send-mailmessage -To myname#mydomain.com -Subject "Fruit Status" -From
fruit#mydomain.com -Body $BODY -Smtpserver smtp.mydomain.org -UseSsl
Get-Content gets the content of the item at the location specified,
such as the text in a file. By default PowerShell will read the
content one line at a time and return a collection of System.String
objects. Carriage returns (CR/LF) are stripped from the original file
but when PowerShell displays a collection of strings, it will
re-insert carriage returns so that the display will look just like the
original file.
If you specify -raw, this behaviour is reversed and the entire file
will be read at once.

Define To: field from from various values

In a CSV file I have various columns and two of those are for emails. Each row within these email columns have a group of emails.
I want to be able to send to these email addresses from a script.
I have everything setup and working, except for the TO:.
The idea of the script is that it loops each line of the csv and generates an email grabbing values from the cells of that row into various parts of the body. Then it sends of an email and loops back to the next line of the CSV to do the same, and so on until it reaches the end of the CSV file.
I'm having issues to plug a variable for the email columns, I'm guessing because the emails don't have "quotations".
How do I bring these in?
In a nutshell for the code
data is imported CSV
a loop is created foreach line of imported data
smtp, from, to, subject, attachments, body variables are defined
then the sendmail-message command is provided.
close the loop
##For the purpose of this, the emaildata.csv looks like this sample:
"NameGroup","emailGroupA","emailGroupB"
"Groupabc","a.b#b.com;c.a#b.com","xyv#b.com;xxd#b,com"
"Grouptrd","ca.r#b.com;as.b#b.com","aaa#a.com;bbb#b.com"
"Groupghd","dd.r#b.com;dd.b#b.com","dddaa#a.com;ddddddb#b.com"
$DataDir = "C:\Users\acastrellon\Documents"
$Data= import-csv $DataDir\emaildata.csv
foreach ($i in $Data) {
$NameGroup = $i.NameGroup
$TeamA = $i.emailGroupA.replace(';',"`n")
$TeamB = $i.emailGroupB.replace(';',"`n")
function send-email {
$smtpserver = " server.smtp"
$from = "myemail.com"
$to = $TeamA,$TeamB
send-MailMessage -From $from -To $to -Subject $subject -Body $body -SmtpServer $smtpServer
}
[string] $subject = "some text here: $NameGroup"
#[string] $attachment = "not here yet"
[string] $body = "
Text here that calls out $NameGroup
This also lists: $TeamA
This here lists $TeamB
Done"
send-email -subject $subject -attachment $attachment -body $body
}
#this should loop to get data from next line of csv and generate a new email with data.
Unlike a batch file where environment variables have a global impact to the current session, PowerShell isolates variables in different scopes.
You're referencing the two $TeamA & $TeamB variables inside a function but you set their values outside the function. Because the function scope (where they are read) is different to the script scope (where you set them) those variables will be empty inside the send-email function in your script.
Have a read on PowerShell scopes as you'll need to make some changes to your script functions; to either read the variables from the script scope ($script:TeamA) or to pass them into the function as a parameter

How important is parameter order when calling powershell cmdlets?

In PowerShell (PS), is it important to use cmdlet parameters in the order in which they're defined in the official syntax?
I'm adding the -cc parameter to send-mailMessage in an existing script. I added -cc immediately after -To and it works just fine, so I'm inclined to leave well enough alone. But the cmdlet's only parameter set is defined as follows in the help text:
Send-MailMessage [-To] <String[]> [-Subject] <String> [[-Body] <String> ] [[-SmtpServer] <String> ] -From <String> [-Attachments <String[]> ] [-Bcc <String[]> ] [-BodyAsHtml] [-Cc <String[]> ] [-Credential <PSCredential> ] [-DeliveryNotificationOption <DeliveryNotificationOptions> ] [-Encoding <Encoding> ] [-Port <Int32> ] [-Priority <MailPriority> ] [-UseSsl] [ <CommonParameters>]
So I assume best practice would be to run a command like this (cc after subject, body, smtpserver, and from, following the help text):
send-mailmessage -to $mailTo -subject $mailSubj -body $msgbody -smtpserver smtp.domain.tld -from $mailFrom -cc $mailCC
...instead of like this (cc before many of those other parameters):
send-mailmessage -to $mailTo -cc $mailCC -subject $mailSubj -body $msgbody -smtpserver smtp.domain.tld -from $mailFrom
In general I haven't been super careful about this, and stuff works. So surely it'd be overkill (not to mention error-prone) to go back and adjust functional existing scripts along these lines. But maybe it's worth respecting the parameter order going forward, in future scripts? Or not worth the trouble? What say you?
Of course you don't want to make a bad assumption about which parameter is the default and then omit the parameter name; I can also imagine this sort of thing getting messy with custom functions or etc. But my question is about the simpler case, when the parameters of an in-the-box cmdlet are explicitly named, as with the send-mailMessage examples above.
If you are naming your parameters when invoking a cmdlet (e.g.Get-ChildItem -Path *.txt) then it doesn't matter what order you specify them in. Everything is exactly specified by name, so the order in which params were provided is not needed to resolve the arguments.
If you are NOT naming your parameters (e.g. Get-ChildItem *.txt) then it does matter. The cmdlet author can specify that certain parameters can/should be expected, without names, in a certain order. The Powershell engine will try its best to honor that, and in general it will try to pair un-named arguments to any parameters which have not yet been assigned.
Check out this page on parameter types for some more technical info.