Add multiline body to powershell email - powershell

I´m trying to add multilines to the body, but can´t find the fix. I tried to do it with html but i don´t know how...
$Email = "x"
$Internal = "x"
$Subject = "New"
$Body = "Sending new files. Cheers!"
[array]$attachments = Get-ChildItem "\\ip\ftp$\new\Fac" *.pdf
if ([array]$attachments -eq $null) {
}
else {
$Msg = #{
to = $Email
cc = $Internal
from = "to"
Body = $Body
subject = "$Subject"
smtpserver = "server"
BodyAsHtml = $True
Attachments = $attachments.fullname
}
Send-MailMessage #Msg
}

If you want a multi-line string literal, use here-strings:
$Body = #'
Multiple
lines
go
here
'#
You can also construct a multi-line string from multiple individual strings with the -join operator:
$Body = 'Line1','Line2','Line3' -join [Environment]::NewLine
But since you want HTML, better join with <br /> instead:
$Body = 'Line1','Line2','Line3' -join '<br />'

Related

Add a Counter to the title of the Send-MailMessage

I want to add a counter, so the number of tickets send will increase by 1 every time an email is sent. I had it working on another powershell script but on this one, seems that something is wrong. Maybe someone can give me a hand with this:
Edit: The counter displays always #1, after every email sent.
if ($Counter -eq $null){
$Counter = 1
}
$Email = "test"
$Internal = ""
$Subject = "Error #$Counter - Test - $(Get-Date -format dd/MM/yy)"
[array]$attachments = Get-ChildItem "path"
if ([array]$attachments -eq $null) {
}
else {
$Msg = #{
to = $Email
cc = $Internal
from = "someone"
Body = 'body'
subject = "$Subject"
smtpserver = "server"
BodyAsHtml = $True
Attachments = $attachments.fullname
}
Send-MailMessage #Msg
$Counter++
}
If you want the counter to always increase, you can store it in the registry or store it in a text file like so:
if(-not(Test-Path C:\PathTo\Counter.txt)){
1 > C:\PathTo\Counter.txt #create the file with a default counter
}
$TicketCounter = [int](Get-Content C:\PathTo\Counter.txt)
#restOfYourScriptHere
$TicketCounter++ #as needed to increment
#end of your Script
$TicketCounter > C:\PathTo\Counter.txt #saves the new counter value

PowerShell Send-MailMessage format column in message body

Warning - I'm new to PowerShell. There are two outcomes I would like to achieve with this script. The first is to include the output in an email and format the columns in the message body so they align with the headers similar to Out-Host. Second is when out-csv, out-gridview or export-excel, how do I order the columns?
$VolArray = #();
$Volumes = Get-Ncvol | Where-Object {$_.VolumeMirrorAttributes.IsDataProtectionMirror -match 'False' -and $_.VolumeStateAttributes.IsVserverRoot -match 'False' -and -not $_.VolumeCloneAttributes.VolumeCloneParentAttributes}
ForEach ($Volume in $Volumes){
#get properties
$vol = Get-Ncvol $Volume
#create object with values
$volobj = New-Object -TypeName PSObject -Property #{
'Controller' = $vol.NcController
'Vserver' = $vol.Vserver
'Aggregate' = $vol.VolumeIdAttributes.ContainingAggregateName
'Name' = $vol.VolumeIdAttributes.Name
'Type' = $vol.VolumeIdAttributes.Type
'TotSizeGB'= $vol.VolumeSpaceAttributes.Size/1gb
'Used' = $vol.VolumeSpaceAttributes.SizeUsed/1gb
'%Used' = $vol.VolumeSpaceAttributes.PercentageSizeUsed
'AvailableGB' = $vol.VolumeSpaceAttributes.SizeAvailable/1gb
'SSResSizeGB' = $vol.VolumeSpaceAttributes.SnapshotReserveSize/1GB
'IsDPMirror' = $vol.VolumeMirrorAttributes.IsDataProtectionMirror
'IsReplicaVol' = $vol.VolumeMirrorAttributes.IsReplicaVolume
'IsDPSource' = $vol.VolumeMirrorAttributes.IsSnapmirrorSource
'DPInProgress' = $vol.VolumeMirrorAttributes.MirrorTransferInProgress
'SSPolicy' = $vol.VolumeSnapshotAttributes.SnapshotPolicy
'AutoSSEnabled' = $vol.VolumeSnapshotAttributes.AutoSnapshotsEnabled
'SSCount' = $vol.VolumeSnapshotAttributes.SnapshotCount
'%SSReserve' = $vol.VolumeSpaceAttributes.PercentageSnapshotReserve
'%SSResUsed' = $vol.VolumeSpaceAttributes.PercentageSnapshotReserveUsed
'SSSpaceUsed' = $vol.VolumeSpaceAttributes.SizeUsedBySnapshots/1GB;
}
#add to array outside opf for-loop
$VolArray += $volobj
}
#$VolArray | Export-Csv -Path c:\temp\file.csv
#$VolArray | Export-Excel -Path c:\temp\exceldump.xlsx
$VolArray | Out-String
#Send-MailMessage -To $mailto -Subject $subject -Body (-join $message) -Port $port -SmtpServer $smtp -from $emailfrom
Send-MailMessage -To $mailto -Subject $subject -Port $port -SmtpServer $smtp -from $emailfrom -Attachments c:\temp\file.csv
Message body:
Column ordering
In PowerShell, for performance reasons, there is no guarantee of order for properties of common hashtables. Thankfully you can use the [ordered] keyword to create ordered dictionaries (a hashtable is a form of dictionary) since version 3.
[PSCustomObject][ordered]#{
FirstColumn = 1
SecondColumn = 2
ThirdColumn = 3
}
This will ensure the order of the properties in subsequent operations like Export-Csv. Note that I also used the [PSCustomObject] accelerator, which is more efficient and than New-Object -TypeName PSObject.
Getting the data efficiently
In your code there are unnecessary calls to Get-Ncvol in the foreach loop. You already have the data you need form earlier:
$Volumes = Get-Ncvol |
Where-Object {
$_.VolumeMirrorAttributes.IsDataProtectionMirror -match 'False' -and
$_.VolumeStateAttributes.IsVserverRoot -match 'False' -and
-not $_.VolumeCloneAttributes.VolumeCloneParentAttributes
}
# Store results in a variable to use later
$reportData = foreach ($Volume in $Volumes) {
# Create object with values
[PSCustomObject][ordered]#{
'Controller' = $Volume.NcController
'Vserver' = $Volume.Vserver
'Aggregate' = $Volume.VolumeIdAttributes.ContainingAggregateName
'Name' = $Volume.VolumeIdAttributes.Name
'Type' = $Volume.VolumeIdAttributes.Type
'TotSizeGB' = $Volume.VolumeSpaceAttributes.Size / 1gb
'Used' = $Volume.VolumeSpaceAttributes.SizeUsed / 1gb
'%Used' = $Volume.VolumeSpaceAttributes.PercentageSizeUsed
'AvailableGB' = $Volume.VolumeSpaceAttributes.SizeAvailable / 1gb
'SSResSizeGB' = $Volume.VolumeSpaceAttributes.SnapshotReserveSize / 1GB
'IsDPMirror' = $Volume.VolumeMirrorAttributes.IsDataProtectionMirror
'IsReplicaVol' = $Volume.VolumeMirrorAttributes.IsReplicaVolume
'IsDPSource' = $Volume.VolumeMirrorAttributes.IsSnapmirrorSource
'DPInProgress' = $Volume.VolumeMirrorAttributes.MirrorTransferInProgress
'SSPolicy' = $Volume.VolumeSnapshotAttributes.SnapshotPolicy
'AutoSSEnabled' = $Volume.VolumeSnapshotAttributes.AutoSnapshotsEnabled
'SSCount' = $Volume.VolumeSnapshotAttributes.SnapshotCount
'%SSReserve' = $Volume.VolumeSpaceAttributes.PercentageSnapshotReserve
'%SSResUsed' = $Volume.VolumeSpaceAttributes.PercentageSnapshotReserveUsed
'SSSpaceUsed' = $Volume.VolumeSpaceAttributes.SizeUsedBySnapshots / 1GB;
}
}
Exporting & emailing
Since we already took care of the column ordering you just need to use $reportData | Export-Csv c:\temp\file.csv -NoTypeInformation or the Export-Excel equivalent.
Emailing is going to be a bit more difficult. Your best bet to convert the data to an HTML table and include it as the body of the email.
# The CSS is neccesary to make the table look nicer, adjust as needed
$css = #'
<style>
body {background-color: powderblue;}
h1 {color: red;}
p {color: blue;}
th, td {
padding: 15px;
text-align: left;
}
</style>
'#
$emailBody = $reportData | ConvertTo-Html -Head $css
# Use parameter splatting for redability
$emailParameters = #{
To = "jdoe#company.com"
Subject = "NetApp report for $(Get-Date -Format 'd')"
Body = $emailBody
BodyAsHtml = $true
SmtpServer = "smtp.company.com"
Credential = Get-Credential
}
Send-MailMessage #emailParameters
[Ordered] worked perfectly.
I modified the message body parameter:
Body = ($emailBody | Out-String)
Because of this error:
Send-MailMessage : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'Body'. Specified method is not supported.
Would you have any recommendations for setting the number of decimal places? ...124.994548797607421875

How to look for a string in a filename and send that file via email?

I have a folder that contains several PDF files. They have a unique identifier within the filename that is two characters. Example: XYZ_A1_123.pdf, XYZ_QQ_456.pdf, etc. A1 and QQ are the identifiers.
The identifiers match specific email addresses. I have a CSV file that has two columns, the ID and the matching email address. I can't get my script to look for that specific identifier and send the file. If the PDF file has only the identifier in the name, such as "A1.pdf", the script works fine. Here's my code so far. Also, I'm looking to add a progress bar but not sure how. Any help is appreciated. Thanks!
$csv = Import-Csv "," -path C:\Test\emails.csv -header "id","email"
foreach ($item in $csv) {
$filename = "$($item.id).pdf"
}
$emailSmtpServer = "smtp.gmail.com"
$emailSmtpServerPort = "587"
$emailSmtpUser = "email#address.com"
$emailSmtpPass = "mypassword"
$emailFrom = "me#address.com"
$emailTo = $item.email
$emailMessage = New-Object System.Net.Mail.MailMessage( $emailFrom , $emailTo )
$emailMessage.Subject = "This is the subject"
#$emailMessage.IsBodyHtml = $true #true or false depends
$emailMessage.Body = "This is the body."
$emailMessage.Attachment = $filename
$emailMessage.Attachments.add($filename)
$SMTPClient = New-Object System.Net.Mail.SmtpClient( $emailSmtpServer ,
$emailSmtpServerPort )
$SMTPClient.EnableSsl = $True
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential(
$emailSmtpUser , $emailSmtpPass );
$SMTPClient.Send( $emailMessage )
Creating a test file to mimic the file you described with codes and e-mail addresses:
echo "AF,a#p.com" > test.csv
echo "QC,q#p.com" >> test.csv
cat .\test.csv
The following snippet will pull the file's code, compare it to the address list, and retrieve the e-mail address if present:
$addresses = Import-Csv -Path .\test.csv -Header code, address
$files = ls .\*.pdf
foreach ($file in $files) {
$file_code = $file.name.Split('_')[1]
$addresses | where { $_.code -eq $file_code } | select address
# ...send email...
}
Looks like you've got the e-mail stuff on-lock so I won't rehash that here. I'd only do the e-mail stuff if you find a matching address.

Powershell - Combining Looped Results

I previously posted this script and had some help but later deleted it to avoid confusion. I have it almost working with the one exception. The content of the email sent to each manager only includes the data from the last direct report in the array. Do I need to restructure the script? I feel like I am chasing my tail if I had one :)
EDIT: I added the recommended changes, but now I get:
Email 1: Manager and direct reports
Email 2: Content of Email 1 and the next Manager and direct reports.
Email 3: Content of Email 1 & 2, the next Manager and direct reports.....
RESOLVED: Thanks TheMadTechnician. Modifying the += and moving the $body to the proper location in the loops was the key.
My script Updated script:
Import-Module -Name ActiveDirectory
$today = (Get-Date).ToString()
# Html
$a = "<style>"
$a = $a + "BODY{background-color:Lavender ;}"
$a = $a + "TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}"
$a = $a + "TH{border-width: 1px;padding: 5px;border-style: solid;border-color: black;background-color:thistle}"
$a = $a + "TD{border-width: 1px;padding: 5px;border-style: solid;border-color: black;background-color:PaleGoldenrod}"
$a = $a + "</style>"
# Email Variables
$smtp = "192.168.1.1"
$to = "User#Company.com"
$from = "User#company.com"
$subject = "Managers - Direct Report's Group Membership"
$managers = Get-ADUser -Filter * -Properties name, directreports, EmailAddress | where {$_.directreports -ne $Null}
foreach ($i in $managers)
{
$mgrname = $i.Name
$mgremail = $i.EmailAddress
$dreports = $i.directreports
$body = "Report Date $today ."
$body = "`n"
$body = "<H3>The direct reports for $mgrname<H3>"
foreach ($d in $dreports)
{
$user = get-aduser $d -properties *
$mems = $user.memberof -replace '^CN=(.+?),(?:OU|CN)=.+','$1' | %{New-Object PSObject -Property #{'Group Membership'=$_}} | convertTo-html -Head $a -Body "<H2>Group Membership.</H2>"
$dreport = $d -replace '^CN=(.+?),(?:OU|CN)=.+','$1'
$body += "`n"
$body += "<H3>Direct Report: $dreport</H3>"
$body += "`n"
$body += $mems
$body += "`n"
}
Send-MailMessage -SmtpServer $smtp -To $to -From $from -Subject $subject -Body $body -BodyAsHtml
}
Ok, resolution for this issue was to move the bulk of the email generation outside of the ForEach($d in $dreports) loop. The header section ($Body='Report for $today', and the following two lines) was moved up, and the line to actually send the mail was moved down outside of the loop. Updated code is already in the OP I think, so I won't bother to repost it again.
This line:
$body = "Report Date $today ."
Is resetting the $body variable for each direct report. So by the time you get to Send-MailMessage all you have is the last direct report.
Try moving that line above the foreach loop that iterates through the direct reports.
Assuming you want to send all reports as individual emails to all managers, move the Send-MailMessage call inside the report foreach loop.
Or if you want to send one email containing all reports to each manager, declare $body outside of you report loop and change the first assigment within the loop to `$body += "Report Date $today ."
Every iteration of foreach ($d in $dreports) resets $body. Change the line
$body = "Report Date $today ."
to
$body += "Report Date $today ."

How can I set a variable from a text file's content in PowerShell?

I know how to use cat file
cat file.txt
but I have variable
[string]$body = "Body message here"
I need help changing it to get the text content from file.txt
I tried, but it didn't work.
[string]$body = $cat file.txt
Here is full script, I am trying to change param section.
param
(
[string]$email = $(read-host "Enter a recipient email"),
[string]$subject = $(read-host "Enter the subject header"),
[string]$body = $(read-host "Enter the email text (optional)")
)
to
param
(
[string]$email = "my#email.com",
[string]$subject = "server",
[string]$body = gc C:\Users\myuser\Documents\somefolder\GnuWin32\bin\status.txt
)
# ==========================================================================
# Functions
# ==========================================================================
function Send-Email
(
[string]$recipientEmail = $(Throw "At least one recipient email is required!"),
[string]$subject = $(Throw "An email subject header is required!"),
[string]$body
)
{
$outlook = New-Object -comObject Outlook.Application
$mail = $outlook.CreateItem(0)
$mail.Recipients.Add($recipientEmail)
$mail.Subject = $subject
$mail.Body = $body
# For HTML encoded emails
# $mail.HTMLBody = "<HTML><HEAD>Text<B>BOLD</B> <span style='color:#E36C0A'>Color Text</span></HEAD></HTML>"
# To send an attachment
# $mail.Attachments.Add("C:\Temp\Test.txt")
$mail.Send()
Write-Host "Email sent!"
}
Write-Host "Starting Send-MailViaOutlook Script."
# Send email using Outlook
Send-Email -recipientEmail $email -subject $subject -body $body
Write-Host "Closing Send-MailViaOutlook Script."
Change:
[string]$body = ( gc C:\Users\myuser\Documents\somefolder\GnuWin32\bin\status.txt )
note the brackets.
Try the following
[string]$body = gc file.txt
The command gc is an alias for Get-Content which will get the content of the specified item and return it as a string
EDIT
As C.B. pointed out, in your example you are trying to use $cat instead of cat. $cat attempts to refer to a variable which isn't defined but cat is another alias for Get-Content
EDIT2
It looks like you are trying to initialize a param. If so you need to do the following
[string]$body = $(gc "C:\Users\myuser\Documents\somefolder\GnuWin32\bin\status.txt")