I have the following command running from a powershell script which gives me the info I need and is all nicely formatted in a table. The command is:
gcloud --project $gcpProject compute snapshots list --format="table[box,title='GCP Project:$gcpProject snapshots for $yesterday'](name,creationTimestamp,diskSizeGb,storageBytes)" --filter="creationTimestamp.date('%Y-%m-%d')=$yesterday"
I have a Start-Transcript -path $Log1 near the beginning of that script.
This is the output from the gcloud command that I get in PS:
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ GCP snapshots │
├─────────────────────────────────────────────────────┬───────────────────────────────┬──────────────┬───────────────┤
│ NAME │ CREATION_TIMESTAMP │ DISK_SIZE_GB │ STORAGE_BYTES │
├─────────────────────────────────────────────────────┼───────────────────────────────┼──────────────┼───────────────┤
│ snapshot1-us-central1-a-20191024022411-1ub96cw9 │ 2019-10-23T19:24:11.743-07:00 │ 500 │ 1104631168 │
│ snapshot2-us-east1-b-20191024020148-iusphq0h │ 2019-10-23T19:01:49.100-07:00 │ 900 │ 1129102848 │
└─────────────────────────────────────────────────────┴───────────────────────────────┴──────────────┴───────────────┘
This is just how I want the email recipient to see it when they open their email. But I can't figure out what I need to do in order to send this as the $body of the email and properly formatted. In Notepad++ it looks perfect too but not if I copy & paste it into a new email.
When I get the email the table is all gibberish (lines are made with a bunch of ????) and table is not formatted properly. I tried ConvertTo-Html and -BodyAsHtml but none of that worked.
Here's my code for sending the email:
If (Test-Path $Log1) {
$body = #(Get-Content -Path $Log1).
Where({ $_.Trim() -notin $pattern2 -and $_ -NotMatch "End time: 20.*|Start time: 20.*" }) # Trimming some things from the Log1 file that I don't want included in the email
send-MailMessage -SmtpServer $SmtpServer -Port $SmtpPort -Credential $Cred -UseSsl -Verbose -To $to -From $from -Subject $subject -Body ($body | out-string)
}
The problem is most likely that when you paste the text from Notepad/Notepad++ into a new email, your client is not using a fixed width font. You can try changing your email font to something fixed-width, then pasting (keeping text-data only) or changing from an HTML formatted email to a plaintext one.
You will also want to make sure that when the email is sending from Powershell that it's either being sent as plaintext (Outlook and other clients usually render plaintext emails in a fixed width font by default), or that you are selecting a well-known fixed width font for your text in the HTML body. But at that point, you might as well just send an HTML-formatted table and plug in the data that way.
If this is used for any sort of automation though I'd highly recommend just putting the data in a CSV for ease of parsing.
I was able to resolve it by formatting the output from gcloud as csv then converting it to html with:
$body = Import-Csv c:\report.csv | ConverTo-Html -head a$ -Body "<H2>My Header</H2>"
where a$ sets the HTML head information:
$a = "<style>"
$a = $a + "BODY{background-color:peachpuff;}"
$a = $a + "TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}"
$a = $a + "TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:red}"
$a = $a + "TD{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:green}"
$a = $a + "</style>"
Then for send-MailMessage I simply added -BodyAsHtml
Related
I wrote a script to get "pending/failed/past due to install" KBs listing in SCCM software center from remote servers, generarting output in .csv file and converted csv file content to HTML report. Script is working fine.
ConvertTo-HTML code which I wrote and working fine is as below:
$Style =#"
<Style>
BODY{font-family:Calibro;font-size:12pt;}
TABLE{border-width: 1px;border-style:solid;border-color: black;border-collapse;Padding-right:5px}
TH{border-width: 1px;padding: 5px;border-style: solid;border-color: black;color:black;backgroud-color:#FFFFFF}
TH{border-width: 1px;padding: 5px;border-style: solid;border-color: black;background-color:#34baed}
TH{border-width: 1px;padding: 5px;border-style: solid;border-color: black}
</style>
"#
$Output = Import-Csv ".\Servers.csv"
$HTMLData = $Output|Select ServerName, LastBootedon,ArticleId,Patch,state
$Email = $HTMLData|ConvertTo-HTML -Body "<H2> SCCM Software Center Pre Scan Report. </H2>" -Head $Style -preContent "Total Servers: $TotalServers"
CSV file content:
Servername
LastBootedon
ArticleId
State
A
10-11-2021
KBxxxx
Failed
B
11-11-2021
KBxxxx
Past Due to Install
C
11-11-2021
KBxxxx
Downloaded but failed to install
D
10-11-2021
KBxxxx
Error connecting server: WMI Error
E
11-11-2021
KBxxxx
Error connecting server: WMI Error
F
11-11-2021
KBxxxx
Error connecting server: WMI Error
G
10-11-2021
KBxxxx
Failed
H
11-11-2021
KBxxxx
Past Due to Install
I
11-11-2021
KBxxxx
Downloaded but failed to install
J
10-11-2021
KBxxxx
Downloading
K
11-11-2021
KBxxxx
Pending to install
Now I want to add count of reoccuring "State" column values in separate new line at top of the HTML report.
Desired Output what I need in HTML report:
SCCM Software Center Pre Scan Report
State Count
Failed 2
Past Due to Install 3
Downloaded but failed to install 10
WMI Error 26
Pending to install 6
Downloading 1
<HTMLCODE>
I'd suggest building a summary from the different states and using Format-List on the result. Capture that as string and put in between <pre>..</pre> tags so you can add this in monospaced font to the report.
P.S. The font name is Calibri, not Calibro and the csv you show doesn't have a column Patch
Try:
$Style =#"
<Style>
BODY{font-family:Calibri;font-size:12pt;}
TABLE{border-width: 1px;border-style:solid;border-color: black;border-collapse;Padding-right:5px}
TH{border-width: 1px;padding: 5px;border-style: solid;border-color: black;color:black;backgroud-color:#FFFFFF}
TH{border-width: 1px;padding: 5px;border-style: solid;border-color: black;background-color:#34baed}
TH{border-width: 1px;padding: 5px;border-style: solid;border-color: black}
</style>
"#
$data = Import-Csv ".\Servers.csv"
# build the summary
$states = [ordered]#{ State = 'Count' }
$data | Group-Object State | ForEach-Object { $states[$_.Name] = $_.Count }
$states['Total Servers'] = ($data.Servername | Select-Object -Unique).Count
# format this as List string and remove the colons in front of the Count
$summary = ([PsCustomObject]$states | Format-List | Out-String ).Trim() -replace '(?m)^(.+)(?<!:):( .*)$', '$1 $2'
# put the summary in between <pre>..</pre> tags into the body
$body = "<H2> SCCM Software Center Pre Scan Report. </H2><pre>$summary</pre>"
$HTMLData = $data | Select ServerName, LastBootedon,ArticleId,Patch,State # the csv you show doesn't have a column Patch..
$Email = $HTMLData | ConvertTo-HTML -Body $body -Head $Style
Your email should then look like this:
Regex details for the -replace:
( Match the regular expression below and capture its match into backreference number 1
. Match any single character that is not a line break character
+ Between one and unlimited times, as many times as possible, giving back as needed (greedy)
)
(?<! Assert that it is impossible to match the regex below with the match ending at this position (negative lookbehind)
: Match the character “:” literally
)
: Match the character “:” literally
( Match the regular expression below and capture its match into backreference number 2
\ Match the character “ ” literally
. Match any single character that is not a line break character
* Between zero and unlimited times, as many times as possible, giving back as needed (greedy)
)
I have the following variable stored
PS C:>$PathArray
jagbir.singh1990#gmail.com
mr.singh#gmail.com
PS C:>$PathArray2
805775-1.zip
805775-2.zip
$Arrayresult = for ( $i = 0; $i -lt $max; $i++)
{
Write-Verbose "$($PathArray[$i]),$($PathArray2[$i])"
[PSCustomObject]#{
PathArray = $PathArray
PathArray2 = $PathArray2
}
PS C:>$Arrayresult
PathArray PathArray2
--------- ----------
{jagbir.singh1990#gmail.com, mr.singh#gmail.com...} {805775-1.zip, 805775-2.zip...}
{jagbir.singh1990#gmail.com, mr.singh#gmail.com...} {805775-1.zip, 805775-2.zip...}
I want to send email1 with body text containing zip file1 name and email2 with body text containing zip file2 name
Ex:
From : jagbir.singh1990#gmail.com
Body : 805775-1.zip file transfer successful.
No Attachments required
code:
foreach($X in $Arrayresult){
Send-MailMessage -From $X.Patharray -To 'jagbir.singh1990#gmail.com' -Subject 'File Transfer completed successfully' -body 'File $X.PathArray2 transfer success' -smtpServer 'smtp.gmail.com' -Port 465
}
Write-Host "Email sent.."
How can I seperate each email for each zip file
email1 --> file1
email2 --> file2
I'm not sure why you would want to keep the email addresses and filenames in separate arrays and then combine them in an array of PSObjects for this.
Anyway, the first mistake is that you did not specify a value for variable $max, which should be the count for the smallest of the two arrays (I'm using more descriptive variable names for these arrays):
$max = [math]::Min($email.Count, $files.Count)
The second one is that you are adding the complete arrays in the properties of each PSCustomObject you create instead of one element of the input arrays.
Finally, I would suggest using a simpler loop (not creating a new PSObject array), use Splatting for the parameters of Send-MailMessage and add some error checking while sending:
Something like:
# array of email addresses to send TO
$email = 'jagbir.singh1990#gmail.com', 'mr.singh#gmail.com'
# array of filenames to use in the mail body
$files = '805775-1.zip', '805775-2.zip'
# determine the max number of iterations
$max = [math]::Min($email.Count, $files.Count)
# set up a hashtable for splatting the parameters to the Send-MailMessage cmdlet
$mailParams = #{
From = 'me#gmail.com'
Subject = 'File Transfer completed successfully'
SmtpServer = 'smtp.gmail.com'
Port = 465
# other parameters go here
}
for ($i = 0; $i -lt $max; $i++) {
# inside the loop we add/update the parameters 'To' and 'Body'
$mailParams['To'] = $email[$i]
$mailParams['Body'] = "File $($files[$i]) transfer success"
try {
Send-MailMessage #mailParams -ErrorAction Stop
Write-Host "Email sent.."
}
catch {
Write-Error "Email NOT sent.`r`n$($_.Exception.Message)"
}
}
I am attempting to parse multiple mails in a specific mail folder, to eventually create a csv file with the information.
If I run the script on one user, the output I get is perfect;
Name Value
---- -----
Kontor. HR
Prøvetid Ja
Blomster på dagen Nej
Telefonnr. 85789845
Adresse Hovedgade 13
Ansættes i stedet for Ninette Kock
Navn Marianne Pedersen
Etage 1. sal
Fri telefon Ja
Benyttet navn Marianne Pedersen
Medarbejdertype Fastansat
Overtagelse af mobilnr. Ja
Placering Sydøst
Ansættelsesdato 01-01-2020
Stilling Fuldmægtig
Lokalnr. +45 3370 0000
Besked til IT-centret
Antal timer 37
Journalistpraktikant Nej
Tidl. ansat Nej
Initialer MAPE
Blomster anden dag 02-01-2020
Postnr/By 2630 Taastrup
Cpr. nr. XXXX
The problem is when I try to parse a second mail, the first mail will still get parsed correctly, but the second one will throw an error;
ConvertFrom-StringData : Data item 'Medarbejdertype' in line 'Medarbejdertype=Fastansat' is
already defined.
If I try to parse the second mail as the first mail, it will parse perfectly.
Problem happens when I try to parse more then one.
I have tried to clear the hash-table like so;
$data.clear()
clear-variable -name data
$data = #{}
But it somehow seems that the hash-table is not emptied?
The code I am using:
Clear-Host
$navne = "MAJE", "ASHO"
# create an instance of Outlook
Add-Type -AssemblyName "Microsoft.Office.Interop.Outlook"
$outlook = New-Object -ComObject Outlook.Application
$nameSpace = $outlook.GetNameSpace("MAPI")
$html = New-Object -ComObject "HTMlFile"
# Opens and save all mails in Nye Til Oprettelse in $mails.
$mails = $nameSpace.Folders.Item('hotline').Folders.Item('Indbakke').Folders.Item('#HOVSA''er').Folders.Item('#01Nye til oprettelse').Items | Select-Object -Property subject, htmlbody
# Loop over $mails, check if $navn = subject
foreach ($navn in $navne) {
foreach ($mail in $mails) {
if($mail | Where-Object {$_.subject -match "$Navn"}) {
#$mail.HTMLBody
# write data to $html, then find all <table> tags and parse that text.
$html.IHTMLDOCUMENT2_write($mail.HTMLBody)
$data = $html.all.tags("table") | % innertext
$data = $data -split '\r?\n' -match '^[^:]+:[^:]+$' -replace ':\s*', '=' -join "`n" | ConvertFrom-StringData
$navn
$data
$data = #[]
}
}
}
You're appending each email to the $html document using $html.IHTMLDOCUMENT2_write($mail.HTMLBody), but you only initialise it once at the top of your script, so each time your foreach ($mail in $mails) loops you add another email to the document.
That means the second time the loop iterates there are two emails in the $html document and you're getting an error about duplicate data items as a result.
Try moving $html = New-Object -ComObject "HTMlFile" inside your foreach loop so it reads:
foreach ($mail in $mails) {
...
$html = New-Object -ComObject "HTMlFile"
$html.IHTMLDOCUMENT2_write($mail.HTMLBody)
...
}
P.S.: You might also want to look at using a non-COM library like the HTML Agility Pack to do your html processing - it'll potentially be a bit faster and more reliable (and portable) in an automation script than COM is.
i have a text file that is automatically downloaded and updated with Task Scheduler.
It looks like this:
C:\PrintLog\GetUsageReportFromPrinterWebService\10.22.17.102:58:<input type="hidden" name="AVAILABELBLACKTONER" value="60">
C:\PrintLog\GetUsageReportFromPrinterWebService\192.167.10.140:58:<input type="hidden" name="AVAILABELBLACKTONER" value="80">
C:\PrintLog\GetUsageReportFromPrinterWebService\192.167.14.128:58:<input type="hidden" name="AVAILABELBLACKTONER" value="80">
I would like to:
delete "C:\PrintLog\GetUsageReportFromPrinterWebService\" and "input type="hidden" name="AVAILIBLETONER""
replace that IP adress with printer name (example 10.51.17.122:58 equals HP-01, 192.168.10.150:58 equals HP-02 etc)
check if "value=" is smaller than 20 and if so than send an email with function sendMail
It doesn't matter what is in that email (after that I will check it manually with webservice anyway).
I just need this as an remainder/alerter that some printer is getting low on toner, so I am not forced to manually check that txt file everyday(I most certainly would forget that :) ). These printers are offsite so I need to know in advance that the printer is low.
Note1: There are empty lines and spaces at the beginning of that txt
Note2: And no, there is no send report via email when low on toner to be configured on those printers (I double checked).
Note3: using C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
I guess point one and two are optional. The third one is important for me, I googled for something similar but just got lost as everyone wanted something little bit different.
Thanks
Here's another possibility (I think it requires Powershell 4 because of Send-Mailmessage, but I could be wrong):
#requires -version 4.0
$logs = Get-Content "F:\scripts\ParseLog\file.log"
$warning = $false
$lowPrinters = ""
# Mail Settings
$Subject = "Low Toner Warning"
$To = "printeradmin#contoso.com"
$From = "admin#contoso.com"
$SMTPServer = "smtp.contoso.com"
$Priority = "High"
$printerList = #{
"10.51.17.122:58" = "HP-01";
"192.168.10.150:58" = "HP-02";
"10.22.17.102:58" = "HP-03";
"192.167.10.140:58" = "HP-04";
}
foreach ( $log in $logs ) {
if ( $log -match '^C:\\PrintLog\\GetUsageReportFromPrinterWebService\\([^:]+:[^:]+):.*value="(.*)">$' ) {
$printer = $Matches[1]
$toner = [int]$Matches[2]
}
if( $toner -lt 20 ) {
$warning = $true
if( $printerList.ContainsKey( $printer ) ) {
$printerName = $printerList[ $printer ]
} else {
$printerName = $printer
}
$lowPrinters += "Printer {0} has a low toner level of {1}.`n" -f $printerName, $toner
}
}
if( $warning ) {
Send-MailMessage -From $From -To $To -Subject $Subject -body $lowPrinters -SmtpServer $SMTPServer
}
Along about line 8 we setup some stuff for sending email. Starting at line 15, we build a hash table mapping printer IPs/Ports with Printer Names (since printer queues aren't always listed in DNS, I decided to use a hash table instead). On line 23, we use a regular expression to grab the ip and port, and the toner value by using the -match operator. Stuff grabbed by the regex is stored in an array called $Matches.
As an example, you could do something like this:
$filePath = "C:\updatedFile.txt"
$prefix = "C:\PrintLog\GetUsageReportFromPrinterWebService\"
$lines = Get-Content $filePath |Where-Object {$_.Trim() -like "$($prefix)*"}
foreach($line in $lines)
{
$content = $line.Trim().Substring($prefix.Length)
$parts = $content -split ":"
$inputML = [xml]"$($parts[2])</input>"
$inputValue = [int]$inputML.input.value
if($inputValue -lt 20)
{
$printer = [System.Net.DNS]::GetHostByAddress($parts[0]).HostName
<#
Your sendMail call in here
#>
}
}
You remove the "C:\PrintLog\GetUsageReportFromPrinterWebService\" part with Substring(), split it into 3 pieces, and then parse to last part as an xml element, giving much easier access to the value attribute
The IP to Printer name part will only work if you have reverse DNS in place already
I'm trying to pass several parameters to the following script in the $attachments parameter, and this seems to work well with a single attachment, or when there are no spaces in the path, however when passing more than one value to the array when one or more have spaces in the path the script fails with an error indicating that it can't find the file specified.
To increase the difficulty, this is being launched from c#. I've tried several permutations of the command below. The entire command line looks something like this:
powershell.exe -executionpolicy unrestricted -file "c:\program files (x86)\App\Samples\SendEmail.ps1" -attachments "c:\Program Files (x86)\App\Logs\1234.log,c:\Program Files (x86)\App\Logs\12345.log"
The script:
param(
[array]$attachments,
[string]$from = 'test#test.com',
[array]$to = 'test#test.com',
[string]$subject = 'Threshold Exceeded',
[string]$body = 'Testing. Please ignore.',
[string]$smtpServer = 'testsmptserver.com'
)
$mailParams = #{
From = $from
To = $to
Subject = $subject
Body = $body
SMTPServer = $smtpServer
}
if ($attachments)
{
Get-ChildItem $attachments | Send-MailMessage #mailParams
}
else
{
Send-MailMessage #mailParams
}
Has anyone encountered something like this? How did you resolve it?
You need to split your $attachments variable because it's being treated as a single file.
Instead of
Get-ChildItem $attachments
Try
Get-ChildItem ($attachments -split ',')
You're only passing one (quoted) string to your 'attachments' array, so you're only populating attachments[0].
Try passing multiple strings:
PS C:\Windows\system32> [array]$wrong="there,you,go"
PS C:\Windows\system32> $wrong
there,you,go
PS C:\Windows\system32> $wrong[0]
there,you,go
PS C:\Windows\system32> $wrong[1]
PS C:\Windows\system32> $wrong[2]
PS C:\Windows\system32> [array]$right="there","you","go";
PS C:\Windows\system32> $right
there
you
go
PS C:\Windows\system32> $right[0]
there
PS C:\Windows\system32> $right[1]
you
PS C:\Windows\system32> $right[2]
go
PS C:\Windows\system32>
From there it should be pretty clear that you can include leading and or trailing spaces like this:
-attachments "value 1 has a trailing space "," value 2 has a leading space"
to get:
attachments[0]="value 1 has a trailing space ";
attachments[1]=" value 2 has a leading space";
You mentioned you're running it from within C#, so I'll remind readers to also escape each of these quotation marks (" -> \") within the C# string that holds this command.