Powershell HTML email formatting - powershell

The below script puts database backup details into a nicely formatted table with borders and saves as a .htm. It then emails the report, but when the report is emailed the 'table' has no borders and no gap between the 'LastBackupDate' column and the 'LastLogBackupDate' column - it basically looks the same as the results would on the powershell console. Can anyone tell me how to format the email so all the html is used from the file? P.s. I can't use send-mailmessage due to issues with gmail ssl. Thanks.
#HTML
$a = "<style>"
$a = $a + "TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}"
$a = $a + "TH{border-width: 1px;padding: 10px;border-style: solid;border-color: black;}"
$a = $a + "TD{border-width: 1px;padding: 10px;border-style: solid;border-color: black;}"
$a = $a + "</style>"
#Set Date
$date = ( get-date ).ToString('yyyyMMdd')
#Locate DB
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | out-null
$s = New-Object ('Microsoft.SqlServer.Management.Smo.Server') "LOCALHOST\SQLX64"
#Retrieves the last backup dates - for full and log backups
$dbs=$s.Databases
$backups = $dbs | SELECT Name,LastBackupDate, LastLogBackupDate | Sort-Object LastBackupDate | ConvertTo-HTML -head $a -body "<H2>DB01 Database Backup Details $date </H2>" | Out-File $("D:\SQL_Backup_Log_Script\Logs\Backup_Log_Temp.htm")
#Email Report
$EmailFrom = "someone#domain.com"
$emailto = "me#gmail.com"
$Subject = "Database Backup Log $date"
$Body = Get-Content D:\SQL_Backup_Log_Script\Logs\Backup_Log_Temp.htm
$SMTPServer = "smtp.gmail.com"
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("someone#domain.com", "password here");
$message = New-Object Net.Mail.MailMessage($EmailFrom, $EmailTo, $Subject, $Body)
$message.IsBodyHtml = $true;
$SMTPClient.Send($message)
#Rename file to include today's date
Rename-Item -path D:\SQL_Backup_Log_Script\Logs\Backup_Log_Temp.htm -newname ($date +"_DB01_Backup_Log.htm")

Try change this line (can't test if is this the problem):
$Body = [System.IO.File]::ReadAllText("D:\SQL_Backup_Log_Script\Logs\Backup_Log_Temp.htm")
the body property in Net.Mail.MailMessage accept [string], get-content return [string[]].

Related

Need to append this powershell script, not sure what to do

I need some help expanding on this script. Basically, I need to add when the $inputFileName = 'Report.txt' is not in its default dir when the script runs every morning the job spits out an e-mail saying no file to process, and then the job stops. What I am seeing is when there is no file in the source dir every now and then, the job runs attaching a blank .xls file that is of no use. I appreciate ANY help in advance!
$ErrorActionPreference="SilentlyContinue"
Stop-Transcript | out-null
$ErrorActionPreference = "Continue"
Start-Transcript -path C:\PATH\Logoutput.txt -append
$emailFilePath = 'C:\PATH\PATH\'
$inputFilePath = 'C:\PATH\PATH\Upload'
$inputFileName = 'Report.txt'
$inputFile = Join-Path $inputFilePath $inputFileName
$outputFileName = 'Report.csv'
$outputFilePath = 'C:\PATH\PATH\Send'
$OutputFile = Join-Path $outputFilePath $outputFileName
$folder = 'C:\PATH\PATH\Upload'
$filter = 'Report.txt'
$destination = $outputFilePath
$dateTime1 = Get-Date -Format s
Write-Host "The file was received: $dateTime1"
Import-CSV $inputFile -Delimiter "`t" -Header "Value 1" , "Value 2" , "Value 3" , "Value 4" , "Value 5" | Tee-Object -Variable Report.txt | Export-CSV $OutputFile -Delimiter "," -NoTypeInformation
$xl = new-object -comobject excel.application
$xl.visible = $false
$Workbook = $xl.workbooks.open("$OutputFile")
$Worksheets = $Workbooks.worksheets
$Workbook.SaveAs("$outputFilePath\Report.xls",1)
$Workbook.Saved = $True
$xl.Quit()
$objExcel = new-object -comobject excel.application
$objExcel.Visible = $false
$objWorkbook = $objExcel.Workbooks.open("$outputFilePath\Report.xls")
$objWorksheet = $objWorkbook.Worksheets.Item(1)
$objRange = $objWorksheet.UsedRange
[void] $objRange.EntireColumn.Autofit()
$objWorkbook.SaveAs("$outputFilePath\Report.xlsx",51)
$objWorkbook.Saved = $True
$objExcel.Quit()
$fromaddress = "user#domain.com"
$toaddress = "user#domain.com"
$bccaddress = ""
$CCaddress = "user#domain.com"
$Subject = "Here Is The Report"
$body = get-content $emailFilePath\content.htm
$attachment = "$outputFilePath\Report.xlsx"
$smtpserver = "smtpdomain"
$message = new-object System.Net.Mail.MailMessage
$message.From = $fromaddress
$message.To.Add($toaddress)
$message.CC.Add($CCaddress)
#$message.Bcc.Add($bccaddress)
$message.IsBodyHtml = $True
$message.Subject = $Subject
$attach = new-object Net.Mail.Attachment($attachment)
$message.Attachments.Add($attach)
$message.body = $body
$smtp = new-object Net.Mail.SmtpClient($smtpserver)
$smtp.Send($message)
$dateTime2 = Get-Date -Format s
Write-Host "The file was parsed and emailed at $dateTime2"
Start-Sleep -Seconds 60
$message.Dispose()
Start-Sleep -Seconds 60
kill -processname Excel
Start-Sleep -Seconds 60
Remove-Item "C:\PATH\PATH\Send\*.*"
$filenameFormat = "Report" + "" + (Get-Date -Format "yyyy-MM-dd") + ".txt"
$updatedFile = "C:\PATH\PATH\Upload\" + $filenameFormat
Rename-Item -Path "C:\PATH\PATH\Upload\Report.txt" -NewName $filenameFormat
Move-Item -Path $updatedFile -Destination C:\PATH\PATH\ArchivedReportData
Stop-Transcript
exit
You need to test for the file before processing as follows:
If ( -not (Test-Path -Path "inputFile")) {
#Write your file not found logic/messages here
Exit
}
Import-CSV $inputFile -Delimiter "`t" -Header "Value 1" , "Value 2" , "Value 3" , "Value 4" , "Value 5" | Tee-Object -Variable Report.txt | Export-CSV $OutputFile -Delimiter "," -NoTypeInformation
$dateTime1 = Get-Date -Format s
Write-Host "The file was received: $dateTime1"
...
HTH

How to concatenate a value to Select-Object's output

I have a script which finds snapshots that are older than 3 days and below are my code and output. However I want to add a new column for each VM displaying the actual age of that snapshots, say for example 5 days or 4 days. I have tried getting age by subtracting created date from today's date. But I am not sure how to add it as a column to my output.
I used this to calculate age:
$StartDate = Get-Date
$created = Get-VM |
Get-Snapshot |
Where {$_.Created -lt (Get-Date).AddDays(-1)} |
Select-Object Created
$age = New-Timespan -Start $StartDate -End $created
Full code:
Add-PSSnapin VMware.VimAutomation.Core
# HTML formatting
$a = "<style>"
$a = $a + "BODY{background-color:white;}"
$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;foreground-color: black;background-color: LightBlue}"
$a = $a + "TD{border-width: 1px;padding: 5px;border-style: solid;border-color: black;foreground-color: black;background-color: white}"
$a = $a + "</style>"
Connect-VIServer -Server ***** -User ****** -Password ******
# Main section of check
Write-Host "Checking VMs for for snapshots"
$date = Get-Date
$datefile = Get-Date -UFormat '%m-%d-%Y-%H%M%S'
$filename = "C:\Temp\snaps_older_than_3\" + $datefile + ".htm"
$created = Get-VM |
Get-Snapshot |
Where {$_.Created -lt (Get-Date).AddDays(-1)} |
Select-Object Created
$age = New-Timespan -Start $StartDate -End $created
$ss = Get-VM |
Get-Snapshot |
Where {$_.Created -lt (Get-Date).AddDays(-1)} |
Select-Object vm, name, SizeGB, SizeMB, Created, powerstate + $age |
ConvertTo-HTML -Head $a -Body "<H2>VM Snapshot Report </H2>"|
Out-File $filename
Write-Host " Complete " -ForegroundColor Green
Write-Host "Your snapshot report has been saved to:" $filename
$SMTPServer = "*******"
$SMTPPort = 25
$username = "vcenter#mmmm.com"
#Define the receiver of the report
$to = "mmmmm#hcl.com"
$subject = "VM Snapshot Report"
$body = "VM Snapshot Report"
$attachment = New-Object Net.Mail.Attachment($filename)
$message = New-Object System.Net.Mail.MailMessage
$message.Subject = $subject
$message.Body = $body
$message.To.Add($to)
$message.From = $username
$message.Attachments.Add($attachment)
$smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort);
$smtp.EnableSSL = $false
$smtp.Send($message)
Write-Host "Mail Sent"
Output:
I want to add a new column named "age".
Use a calculated property:
Get-VM |Select-Object VM,Name,SizeGB,SizeMB,Created,PowerState,#{Name='Age';Expression={New-TimeSpan -Start $StartDate -End $_.Created}}
From the help text for Select-Object:
-Property
Specifies the properties to select. Wildcards are permitted.
The value of the Property parameter can be a new calculated property.
To create a calculated property, use a hash table. Valid keys are:
Name (or Label) <string>
Expression <string> or <script block>

How to attach excel files in an email using powershell

I have the following select statement that extract data from a SQL Server database table:
$QUERY = "SELECT * FROM Table1"
$DataVariable = Invoke-Sqlcmd -ServerInstance "MyInstance" -Database "MyDB" -Query $QUERY
How can I attach the $DataVariable as an excel file in my email?
I am working on the code below:
$Date = get-date -format "dd-MMM-yyyy"
$Day = $Date.DayOfWeek
$DateTested = $Date
$filename = "MYReport_$(get-date -f yyyy-MM-dd).XLS"
$OutputFile = "\\server\c$\Output\$filename"
I don't seem to go anywhere with this. Can anyone assist please?
Thanks
Your code snippets don't seem to be related to one another
there is no attempt to mail anything
I suggest to save $Datavarable to csv (xlsx is also possible)
this raw example script uses gmail and splatting to fill the parameters
$QUERY = "SELECT * FROM Table1"
$DataVariable = Invoke-Sqlcmd -ServerInstance "MyInstance" -Database "MyDB" -Query $QUERY
$filename = "MYReport_$(get-date -f yyyy-MM-dd).csv"
$OutputFile = "\\server\c$\Output\$filename"
$DataVariable | Export-Csv $OutPutFile -NoTypeInformation
$emailAcc = "youraccount#gmail.com"
$Cred = Get-Credential -UserName $emailAcc -Message "Enter gmail password for $emailAcc"
$MsgParam = #{
From = $emailAcc
To = "recipient#domain.com"
Subject = $filename
Body = 'blah blah sending $filename'
Attachments = $OutPutFile
SmtpServer = "smtp.gmail.com"
}
Send-MailMessage #MsgParam -UseSSL -cred $Cred
I have managed to build the code below and it worked for me !!!!!!!!!!!!!!:
$DataVariable = Invoke-Sqlcmd -ServerInstance $ResultsInstance -Database $ResultsDatabase -Query $SQL
$Date = get-date -format "dd-MMM-yyyy"
$Day = $Date.DayOfWeek
$DateTested = $Date
$filename = "MyReport_$(get-date -f yyyy-MM-dd).csv"
$OutputFile = "\\serverName\c$\Output\$filename"
$DataVariable | Export-csv $OutPutFile -NoTypeInformation
$to = "Support"
$body = "Dear <b><font color=black>$to</b></font> <br>"
$body += "<p>Please find attached report.</p> <br>"
$body += "Kind Regards<br>"
$body += "Dev Team <br>"
$fromaddress = "xyz#xyz.com"
#$toaddress = "x#ytm.com"
$toaddress = "x#ytm.com"
$bccaddress = "x#ytm.com"
$CCaddress = "x#ytm.com"
$Subject = "My weekly report"
$attachment = $OutputFile
$smtpserver = "xyzmail"
####################################
$message = new-object System.Net.Mail.MailMessage
$message.From = $fromaddress
$message.To.Add($toaddress)
$message.CC.Add($CCaddress)
$message.Bcc.Add($bccaddress)
$message.IsBodyHtml = $True
$message.Subject = $Subject
$attach = new-object Net.Mail.Attachment($attachment)
$message.Attachments.Add($attach)
$message.body = $body
$message.Priority = [System.Net.Mail.MailPriority]::High
$smtp = new-object Net.Mail.SmtpClient($smtpserver)
if ($DataVariable) {
$smtp.Send($message)
Write-Host "The variable is not null" }

PowerShell multiple email recipients (attachment) from csv / empty cell issue

I recently started to work on PS script for email automation.
Manage to put something together where I have CSV file from where PowerShell is extracting data (multiple recipients, attachment, etc).
The problem is, when some of the cells are empty, script won`t work.
There is one similar question, but I am just not sure how to implement solution on my case:
$importFile = "Users.csv"
$users = import-csv $importFile | select *
foreach($user in $users)
{
$Name = $user.'Name'
$to = $user.'To'
$to2 = $user.'To2'
$Attachment = $user.'Attachment'
$Attachment2 = $user.'Attachment2'
$write = "Emailing account " + $to + " ..."
Write-Host $write
$body = [string]::join([environment]::newline, (Get-Content -path $emailFile))
$body = $body.Replace('[Name]', $Name)
$mail = New-Object System.Net.Mail.MailMessage
$mail.From = $emailFrom
$mail.To.Add($to)
$mail.To.Add($to2)
$mail.Subject = $emailSubject
$mail.IsBodyHtml = $true
$mail.Body = $body
$mail.Attachments.Add($Attachment)
$mail.Attachments.Add($Attachment2)
$smtp = New-Object System.Net.Mail.SmtpClient
$smtp.Host = $smtpServerHost
$smtp.Port = $smtpServerPort
$smtp.EnableSsl = $smtpServerSsl
$smtp.UseDefaultCredentials = $false
$smtp.Credentials = $credentials
$smtp.Send($mail)
This is how looks CSV file
Any suggestion is more than welcome :)
Try Where-Object to filter out rows.
$users = Import-Csv $importFile |
Where-Object {$_.Name -ne ""} |
Select-Object *
For what it is worth: if the whole "row" is blank you can shortcut this to simply be
...
Where-Object {$_} |
...
Implementing IF function resolved the case. For example if ($to2 -ne '') is checking if $to2 cell is empty. If no, then $mail.To.Add($to2). The same situation for $Attachment. Here is the code:
$mail = New-Object System.Net.Mail.MailMessage
$mail.From = $emailFrom
$mail.To.Add($to)
if ($to2 -ne '') {
$mail.To.Add($to2)
}
$mail.Subject = $emailSubject
$mail.IsBodyHtml = $true
$mail.Body = $body
$mail.Attachments.Add($Attachment)
if ($Attachment2 -ne '') {
$mail.Attachments.Add($Attachment2)
}

Email Value From Variable & Hyperlink

From within PowerShell, I know how to send a basic email. But with my syntax below, how could I append to the body of the email each $QueryName and each $RowCount and add a hyperlink to the value contained in $FPath\$FormattedDate\so the body of email would look like this:
$QueryName - $RowCount
(or with actual data)
Santa - 14
Mickey - 12
Mars - 2
Here is my current PS script
Function Execute-SQLquery {
param ($QueryName, $QueryString)
$server = "Server"
$database = "DB1"
$FPath = "C:\Testing"
#Setting additional variables
$extension = ".csv"
$date = Get-Date -f 'MM.dd.yy'
$FormattedDate = Get-Date -f 'MM.dd.yy'
$connectionTemplate = "Data Source={0};Integrated Security=SSPI;Initial Catalog={1};"
$connectionString = [string]::Format($connectionTemplate, $server, $database)
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
$command = New-Object System.Data.SqlClient.SqlCommand
$command.CommandText = $QueryString
$command.Connection = $connection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $command
$DataSet = New-Object System.Data.DataSet
$rowCount = $SqlAdapter.Fill($DataSet)
if(!(Test-Path -path "$FPath\$FormattedDate\")){New-Item "$FPath\$FormattedDate\" -type directory}
if ($rowCount -gt 0)
{
if ($QueryName -eq "Santa")
{
$extractFile = "C:\Testing\TemplateFiles\Santa.csv"
[System.IO.Directory]::CreateDirectory("$FPath\$FormattedDate\Santa\")
Write-Host $rowCount -fore Red
$dirName = "$FPath\$FormattedDate\Santa\"
$filename = [IO.Path]::GetFileNameWithoutExtension($extractFile) + "_$date" + [IO.Path]::GetExtension($extractFile)
$extractFile = Join-Path $dirname $filename
}
if ($QueryName -eq "Mickey")
{
$extractFile = "C:\Testing\TemplateFiles\Mickey.csv"
[System.IO.Directory]::CreateDirectory("$FPath\$FormattedDate\Mickey\")
Write-Host $rowCount -fore Red
$dirName = "$FPath\$FormattedDate\Mickey\"
$filename = [IO.Path]::GetFileNameWithoutExtension($extractFile) + "_$date" + [IO.Path]::GetExtension($extractFile)
$extractFile = Join-Path $dirname $filename
}
if ($QueryName -eq "Mars")
{
$extractFile = "C:\Testing\TemplateFiles\Mickey\Mars.csv"
[System.IO.Directory]::CreateDirectory("$FPath\$FormattedDate\Mars\")
Write-Host $rowCount -fore Red
$dirName = "$FPath\$FormattedDate\Mars\"
$filename = [IO.Path]::GetFileNameWithoutExtension($extractFile) + "_$date" + [IO.Path]::GetExtension($extractFile)
$extractFile = Join-Path $dirname $filename
}
$DataSet.Tables[0] | Export-Csv $extractFile -NoTypeInformation
}
$connection.Close()
}
First up, since the only thing that changes based on $QueryName are direct references to the value in $QueryName and the $extractFile, you'd be better off not repeating that entire block.
For the mail message, you can use Send-MailMessage.
To add a link to a local file resource, use the file:/// scheme prefix and change all backslashes (\) to forward slashes (/), ie. file:///C:/Document/Path.ext, or in your example "file:///$("$FPath\$FormattedDate" -replace '\','/')":
Function Execute-SQLquery {
param ($QueryName, $QueryString)
# up to this point no change is required
if ($rowCount -gt 0)
{
$extractFile = switch($QueryName){
"Santa" { "C:\Testing\TemplateFiles\Santa.csv" }
"Mickey" { "C:\Testing\TemplateFiles\Mickey.csv" }
"Mars" { "C:\Testing\TemplateFiles\Mars\Mickey.csv" }
default { throw "Illegal QueryName" }
}
[System.IO.Directory]::CreateDirectory("$FPath\$FormattedDate\$QueryName\")
Write-Host $rowCount -fore Red
$dirName = "$FPath\$FormattedDate\$QueryName\"
$filename = [IO.Path]::GetFileNameWithoutExtension($extractFile) + "_$date" + [IO.Path]::GetExtension($extractFile)
$extractFile = Join-Path $dirname $filename
$DataSet.Tables[0] | Export-Csv $extractFile -NoTypeInformation
$EmailBody = #'
Here are the results:
{0} - {1}
Find the documents here
'# -f $QueryName,$rowCount,$("$FPath\$FormattedDate" -replace '\','/')
Send-MailMessage -From "me#company.example" -To "you#company.example" -Body $EmailBody -BodyAsHtml:$true -Subject "Data extracted!" -SmtpServer "your.mail.server.company.example"
}
$connection.Close()
}