Powershell email output not formatting correctly - email

I have spent the last few hours trying various ways to solve my problem below, adding parts from other working scripts that I have and researching on here, yet have reached the point where I need a fresh set of eyes to point the obvious out to me.
This is my script, which does the following:
Lists all folders in a certain location, puts the folder paths in a text file
In a loop, uses the text file to check the most recent file in each folder, then output the most recent 10 timestamps (denoted by '[')
Emails the results
All of the above works, I have the email with the correct information. The problem is that the email arrives all on the same line with no formatting.
I've taken my style tags from a previous working script that I had, so I'm not sure where I've gone wrong. Any ideas?
$style = "<style>BODY{font-family: Arial; font-size: 10pt;}"
$style = $style + "TABLE{border: 1px solid black; border-collapse:collapse;}"
$style = $style + "TH{border: 1px solid black; background: #dddddd; padding: 5px; }"
$style = $style + "TD{border: 1px solid black; padding: 5px; }"
$style = $style + "</style>"
$subject = "Processing Log Check at " + (Get-Date -Format g)
$logpaths = Get-Content -Path C:\Powershell\xxxx\scenariologlocation.txt
$output = foreach ($logpath in $logpaths)
{
Write-Output $logpath
Get-ChildItem -path $logpath | sort LastWriteTime | select -ExpandProperty FullName -last 1 | Out-File C:\Powershell\path.txt
$path = Get-Content -Path C:\Powershell\path.txt
Get-Content -Path $path | Select-String -SimpleMatch "[" | select Line -last 10 | format-table -HideTableHeaders | Out-String
}
$body = (ConvertTo-Html -Head $style -body $output | Out-String)
Send-MailMessage -From "xxxxxx" -To "xxxxxx" -Subject $subject -Body $body -BodyAsHtml -SmtpServer "xxxxxxx"

I think you shouldn't pass a string to $output and then pass it to Convertto-Html -body
In my eyes the last part of the script should look like
$output = foreach ($logpath in $logpaths)
{
Write-Output $logpath
Get-ChildItem -path $logpath | sort LastWriteTime | select -ExpandProperty FullName -last 1 | Out-File C:\Powershell\path.txt
$path = Get-Content -Path C:\Powershell\path.txt
Get-Content -Path $path | Select-String -SimpleMatch "[" | select Line -last 10
}
$body = ($output | ConvertTo-Html -Head $style) | Out-String
Send-MailMessage -From "xxxxxx" -To "xxxxxx" -Subject $subject -Body $body -BodyAsHtml -SmtpServer "xxxxxxx"
So that Convertto-Html could build the table over all objects in $output. I don't really know if it is necessary to pipe the HTML to Out-String. Have no Powershell here. But I think this should help.

After a lot of fiddling and fanoodling, I ended up re-writing the lot and got what I needed into an email another way.
I'm not 100% convinced what I needed could be achieved from my original code (without resorting to the re-write).

Related

Powershell HTML body converting issue

I have a script that reads a list on a Sharepoint 2019 OnPremise site. The idea is to capture certifcates that are about to expire.
Problem is I can't conver it properly to a html-table however I do. I done converting before but not in PNP and with camel querys.
Script:
$smtpserver = "-"
$mailsender ="-"
$mailReceiver = "-"
$mailSubject = "Certificates about to expire"
$Header = #"
<style>
TABLE {border-width: 1px; border-style: solid; border-color: black; border-collapse: collapse;}
TD {border-width: 1px; padding: 3px; border-style: solid; border-color: black;}
</style>
"#
Import-Module "C:\PNP\SharePointPnPPowerShell2019\3.6.1902.2\SharePointPnPPowerShell2019.psd1"
if ($cred -eq $null)
{
$cred = Get-Credential
}
Connect-PnPOnline "-" -Credentials $cred
$list = Get-PnPList "-"
$listitems = Get-PnPListItem -List $list -Query "<View><Query><Where><Lt><FieldRef Name='Utg_x00e5_r' Type='DateTime'/><Value Type='DateTime'><Today OffsetDays='60'/></Value></Lt></Where></Query></View>" | Select-object {$_["Title"], $_["Farm"], $_["Server"] , $_["Utg_x00e5_r"]} | ConvertTo-Html -Head $header | Out-String
Send-MailMessage -From $mailsender -to $mailReceiver -subject $mailSubject -body ($listitems | Out-String) -SmtpServer $smtpServer -Port 25 -BodyAsHtml
What am I missing?
The Select-Object statement currently creates objects with only 1 property, hence the single column in your output.
Change it to something like:
...| Select-Object #{Name='Title';Expression={$_["Title"]}},#{Name='Farm';Expression={$_["Farm"]}},#{Name='Server';Expression={$_["Server"]}},#{Name='Utg_x00e5_r';Expression={$_["Utg_x00e5_r"]}} |ConvertTo-Html ...
... which will create objects with 4 separate properties, which will in turn translate into a 4-column HTML table

Sending foreach output via email

I have a script that is checking mirror status of databases. Output in Powershell is fine, but when I try to send it via mail, I'm getting "Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData" instead of data itself. I've tried to change it to Out-String but then I'm getting all results in one line. How this could be done to have formated output the same way as it is formated directly in PowerShell?
# rozszerzenie do obslugi
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | Out-Null;
$mail_from = "xxx";
$mail_to = "xxx";
$mail_subject = "Status mirrorowanych baz";
$mail_encoding = "UTF8";
$mail_smtp = "xxx";
# lista serwerow
$list = #("SERVER01V",
"SERVER02V"
);
$output = foreach($server in $list)
{
$srv = New-Object "Microsoft.SqlServer.Management.Smo.Server" $server;
# pokaz tylko mirrorowane
$databases = $srv.Databases | Where-Object {$_.IsMirroringEnabled -eq $true};
Write-Output "<br>==================================<br>";
Write-Output $server;
Write-Output "<br>==================================<br>";
$databases | Select-Object -Property Name, MirroringStatus | Format-Table -AutoSize | Out-String;
Write-Output "<br>";
}
$mail_body = $output;
Send-MailMessage -To $mail_to -From $mail_from -Subject $mail_subject -SmtpServer $mail_smtp -Encoding $mail_encoding -Body $mail_body -BodyAsHtml
You're currently sending a HTML mail. As such line breaks won't matter. If you want line breaks in your mail you either need to use text format or replace line breaks with a <br /> or something similar. It's probably going to be wise to have manually add <br /> in your look to have a consistent pattern and replace.
Try gather all data into an array, then use ConvertTo-HTML cmdlet, and the 'BodyAsHTML' switch in Send-MailMessage
$DatabaseArray=#()
ForEach ($server in $list) {
$srv = New-Object "Microsoft.SqlServer.Management.Smo.Server" $server
$DatabaseArray += $srv.Databases | Where-Object {$_.IsMirroringEnabled -eq $true} | Select-Object Name,MirroringStatus
}
$HTMLBody = $DatabaseArray | ConvertTo-HTML
Send-MailMessage -subject x -body $HTMLBody -BodyAsHTML

How to monitor list of services on remote machines?

I would like to monitor a list of services on remote machines.
These services are not the same on all remote machines.
The closest I have got is to monitor all services which are stopped on remote machines but I cant seem to find a way to script a way to monitor a list of services.
This is script I am working on:
$Date = Get-Date -Format dd-MMM-yyyy
$Time = Get-Date -Format "hh:mm:ss tt"
$Style = #"
<!DOCTYPE html>
<html>
...
"#
$ServerList = Get-Content -Path C:\temp\computers1.txt
$body = $ServerList |
Foreach-Object {
Get-Service -ComputerName $_ | Where-Object {
$_.Status -ne "Running" -and
$_.StartType -like "Automatic"
}
} |
Select-Object MachineName, Status, DisplayName, StartType |
Sort-Object -Property MachineNAme -Descending |
ConvertTo-Html
$colorTagTable = #{
Stopped = ' bgcolor="#ff0000">Stopped<';
Running = ' bgcolor="#00ff00">Running<'
}
# get possible values look them in text sorrounded by > < and replace
# them with style (pun intended).
$colorTagTable.Keys | foreach {
$body = $body -replace ">$_<", ($colorTagTable.$_)
}
ConvertTo-Html -Head $Style -Body $body | Out-File "C:\temp\srv.htm"
When in doubt, read the documentation.
-ComputerName<String[]>
Gets the services running on the specified computers. The default is the local computer.
Type the NetBIOS name, an IP address, or a fully qualified domain name (FQDN) of a remote computer. To specify the local computer, type the computer name, a dot (.), or localhost.
[...]
-Name<String[]>
Specifies the service names of services to be retrieved. Wildcards are permitted. By default, this cmdlet gets all of the services on the computer.
$Style = #"
<style>
...
</style>
"#
$ServiceList = 'NetLogon', 'Spooler', 'W32Time'
$ServerList = Get-Content -Path C:\temp\computers1.txt
Get-Service -ComputerName $ServerList -Name $ServiceList |
Select-Object MachineName, Status, DisplayName, StartType |
Sort-Object -Property MachineNAme -Descending |
ConvertTo-Html -Head $Style |
Out-File 'C:\temp\srv.htm'
Services that don't exist on a particular computer are ignored unless none of the services is running on that computer, in which case you'll get an error. Run Get-Service with the parameter -ErrorAction SilentlyContinue if you want to ignore it.
for service list I will have something like this to actually get the service which should be running and have email reports instead of logging and checking html pages
$servicelist=Get-WmiObject -Class Win32_Service -Filter "state = 'stopped' and startmode = 'auto'" | select Name
$From = "YourEmail#gmail.com"
$To = "AnotherEmail#YourDomain.com"
$Subject = "Daily Service Report From "
$Body = "get-content C:\temp\srv.htm "
$SMTPServer = "smtp.email.com"
#$SMTPPort = "587"
Send-MailMessage -From $From -to $To -Subject $Subject `
-Body $Body -SmtpServer $SMTPServer #-port $SMTPPort

$FormatEnumerationLimit = -1 within a PowerShell script

I have created a PowerShell script, but the output gets cut off with ...}
If I type $FormatEnumerationLimit = -1 before I run the script, it works. But the problem is I can't seem to include this command within my script. If I add this command at the top of my script, it doesn't work.
How can I get this included in the script?? Sorry I don't think the script copied over correctly in the window.
$FormatEnumerationLimit = -1
Get-ChildItem hklm:\SOFTWARE\Wow6432Node\software\nameofsoftware | ForEach- Object {
Get-ItemProperty $_.pspath
} | Foreach-Object {
$Properties = #{
Name = $_.Name
Header= $_.Header
True= $_.True
Schedule = $_.Schedule
}
New-Object -TypeName PSObject -Property $Properties
} | FL | Out-File C:\test.txt -Width 10000
Send-MailMessage -from "joe#joe.com" -to "joe#joe.com" -subject "Test"
-smtpserver 192.168.5.2 -port 25 -Attachments C:\test.txt
FL output is meant for the screen, rather than for files. Besides, why package all the information up to an object only to try and take it apart immediately? You could output the information in the format you want, directly:
} | Foreach-Object {
"Name: " + $_.Name
"Header: " + $_.Header
"True: " + $_.True
"Schedule: " + $_.Schedule
} | Set-Content C:\test.txt

Adding a config file to loop through for a PS script

My powershell script is setup as follows:
$body = Get-ChildItem E:\log -File -Recurse | Where Name -Match '(\d{8})\.' |
Foreach {Add-Member -Inp $_ NoteProperty ReturnDate ($matches[1]) -PassThru} |
Group DirectoryName |
Foreach {$_.Group | Sort ReturnDate -Desc | Select -First 1 | Out-String }
$emailSmtpServer = "server"
$emailFrom = "email"
$emailTo = "email"
$emailSubject = "Testing e-mail"
$emailBody = $body
Send-MailMessage -To $emailTo -From $emailFrom -Subject $emailSubject -Body ($body|Out-String) -SmtpServer $emailSmtpServer
In the log folder I have a bunch of subfolders, e.g. folder1, folder2, folder3. These are likely to change so I'd like to setup a config file to be able to maintain them instead of going through the entire E:\log folder each time I run the script.
I want to add something such as
$configfile = Get-Content -path E:\config.txt
This outputs Process1, Process2, Process3, etc and I'm uncertain how to put that data into my script the way it's currently structured. Any advice would be appreciated. I was trying to add
$body = Get-Childitem E:\log\$_
to my initial line, but that was not working
Try this:
get-childitem ( get-content e:/config.txt )