PowerShell outtput to HTML - powershell

Using the following and would like to know how to output the results to HTML file
$Start = (Get-Date).AddMinutes(-5)
$Computername = gc C:\Temp\List.txt
$Events = gc C:\Temp\ErrorCodes.txt
Get-EventLog -AsString -ComputerName $Computername |
ForEach-Object {
# write status info
Write-Progress -Activity "Checking Eventlogs on \\$ComputerName" -Status $_
# get event entries and add the name of the log this came from
Get-EventLog -LogName $_ -EntryType Error, Warning -After $Start -ComputerName $ComputerName -ErrorAction SilentlyContinue |
Add-Member NoteProperty EventLog $_ -PassThru | Where-Object {$Events -contains $_.eventid}
} |
# select the properties for the report
Select-Object EventLog, EventID, TimeGenerated, EntryType, Source, Message

This will get it to print with pretty colors. You can tinker around with it and change whatever you want.
$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:thistle}"
$a = $a + "TD{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:palegoldenrod}"
$a = $a + "</style>"
$Variable = get-what_I_want
$Variable2 = get-what_I_want
$VariableHTML = $Variable | ConvertTo-HTML -head $a -body "<H2>Title I want</H2>"
$Variable2HTML = $Variable2 | ConvertTo-HTML -head $a -body "<H1>Header One</H1> <H2>Header Two</H2> <H3>Header Three</H3> <p>Paragraph<p/>"
$VariableHTML > C:\PoSH\My_Exported_HTML.html
I added a second variable. As you can see, you can add different levels of headers and a Paragraph. I found this helpful for single line items or if you want a better description.

If I would say: ConvertTo-Html is what you need, than probably that would be enough to add a comment.
Instead I will say: before asking questions outside PowerShell, ask them inside PowerShell first.
Get-Help *html*

Related

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>

Output foreach loop to html

So, here's my issue... I have the below PowerShell script that I want to use to do the following:
Pull Each Hyper-V cluster member (currently 29 x Server 2016 nodes)
Run a ForEach loop on each node to get all VMs that have Hyper-V replication enabled.
Combine all 29 outputs into a single .html file with table formatting.
Here is the code:
$Cluster = Get-ClusterNode -Cluster hv-cluster01.contoso.com
$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{column-width: 200px;border-width: 1px;padding: 8px;border-style: solid;border-color: black;background-color:thistle}"
$a = $a + "TD{column-width: 200px;border-width: 1px;padding: 3px;border-style: solid;border-color: black;background-color:PaleGoldenrod;text-align: center}"
$a = $a + "</style>"
ForEach ($Node in $Cluster)
{
Get-VMReplication -ComputerName $Node | Select-Object VMName, State, Health, PrimaryServer | ConvertTo-HTML -head $a -body "<H2>DR Replication Status</H2>" | Out-File C:\Scripts\Test.htm
}
My ultimate goal would be to put this into a scheduled task to run 2-3 times per day, and using Send-MailMessage, send the html content in the body of an eamil, to myself and others on my team.
I appreciate any help I can get.
In your code, you convert and output the object for each $Node in $Cluster. Instead you first have to populate your final object, which contains all information, and then convert and output these information.
You do so, by moving the convert and output part outside the loop. For example:
$Cluster = Get-ClusterNode -Cluster hv-cluster01.contoso.com
$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{column-width: 200px;border-width: 1px;padding: 8px;border-style: solid;border-color: black;background-color:thistle}"
$a = $a + "TD{column-width: 200px;border-width: 1px;padding: 3px;border-style: solid;border-color: black;background-color:PaleGoldenrod;text-align: center}"
$a = $a + "</style>"
$Report = ForEach ($Node in $Cluster) {
Get-VMReplication -ComputerName $Node | Select-Object VMName, State, Health, PrimaryServer
}
$Report | ConvertTo-HTML -head $a -body "<H2>DR Replication Status</H2>" | Out-File C:\Scripts\Test.htm
I've modified the script quite a bit more, to accommodate the rest of what I was trying to do...
I've managed to get the script to pull the content of the HTML file and place it into an email message. My problem is, some of the table style info isn't transferring to the email. Specifically, the column-width part. So all of the table entries are squished together. See below:
Remove-Item C:\Scripts\ReplicationHealth.htm
$Cluster = Get-ClusterNode -Cluster hv-cluster01.contoso.com
$a = "<style>"
$a = $a + "BODY{background-color:LightGrey;}"
$a = $a + "TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}"
$a = $a + "TH{column-width: 200px;border-width: 1px;padding: 8px;border-style: solid;border-color: black;background-color:DodgerBlue}"
$a = $a + "TD{column-width: 200px;border-width: 1px;padding: 3px;border-style: solid;border-color: black;background-color:PapayaWhip;text-align: center}"
$a = $a + "</style>"
$Report = ForEach ($Node in $Cluster) {
Get-VMReplication -ComputerName $Node | Select-Object VMName, State, Health, PrimaryServer
}
$time = Get-Date -Format G
$Report | ConvertTo-HTML -head $a -body "<H2>Hyper-V Replication Status</H2>", $time | Out-File C:\Scripts\ReplicationHealth.htm
$MailBody = Get-Content C:\Scripts\ReplicationHealth.htm -Raw
Send-MailMessage -SmtpServer "SERVERIP" -From "HyperV-Replication#contoso.com" -To "User#contoso.com" -Subject "Hyper-V Replication Status" -Body $MailBody -BodyAsHtml
A tiny bit more help would be awesome!
Thanks in advance!

Unable to generate HTML table from Powershell function

I have got a powershell function that gets all errors and warnings from the event logs, counts the occurance of each Event ID and returns a Powershell table. THe code that does this is as follows -
function Get-EventLogs {
[System.Collections.ArrayList]$results = #()
#Do you want to enable transcript for logging all output to file?
$enableLogging = $FALSE
$ExportEnabled = $FALSE
# logging
# if you need detailed logging for troubleshooting this script, you can enable the transcript
# get the script location path and use it as default location for storing logs and results
$log = $MyInvocation.MyCommand.Definition -replace 'ps1','log'
$resultPath = $PSScriptRoot + '\'
Push-Location $resultPath
if ($enableLogging)
{
Start-Transcript -Path $log -ErrorAction SilentlyContinue
Write-Host "Logging enabled..."
Write-Host
Write-Host "Powershell version"
$PSVersionTable.PSVersion
$Host.Version
(Get-Host).Version
Write-host
}
$currentCulture = [System.Threading.Thread]::CurrentThread.CurrentCulture
# Configuration
# selected Event log sources
# limit the feedback only to the following logs
$EventLogList = 'System','Application'
#for FIM Health Check the Security log has less signification value and is skipped
#add it to the eventlog list when you need it
#,'Security'
# if the event logs are containing too much data,
#collect x years of logs
$startdate = ((Get-Date).AddDays(-7))
$Count = 0
$Activity = "Checking log properties"
$allEvents = New-Object System.Collections.Hashtable
$Count = 0
$Activity = "Checking log detaills"
foreach ($eventlog in $EventLogList)
{
#$Count += 1
$pct = ($Count / $EventLogList.Count * 100)
$status = $EventLog + " (" + $Count + "/" + $EventLogList.Count +")"
Write-Progress -Activity $Activity -Status $status -PercentComplete $pct
write-host $count"." $eventlog
[System.Threading.Thread]::CurrentThread.CurrentCulture = New-Object "System.Globalization.CultureInfo" "en-US"
# query the event log
# store the data in a hashtable, to avoid new queries
$allEvents = $Null
$allEvents = Get-WinEvent -FilterHashtable #{logname=$eventlog;StartTime=$startdate;level=0,1,2,3,4,5} -ErrorAction SilentlyContinue
#DEBUG if ($eventlog -eq "Application") { $allevents}
if ($allEvents.count -eq 0)
{
$message = "No events for " + $eventlog + "log since "+ $startdate + "."
Write-Host $message
Write-Host
# no data to process, skip processing for current loop
Continue
}
#For events detailed reporting we're only interested in error events
#not interested in informational events (level 0 and 4)
$evtStats = $allEvents | where -Property level -Notin -Value 0,4 | Group-Object id | Sort-Object Count -Descending
$allevents = $Null
#prep export
$exportfile = $resultPath + "_EventIDStats"+ $eventlog + ".csv.txt"
if ($exportEnabled) {$export | Export-Csv $exportfile -NoTypeInformation}
# evtStats has number and ID attribute
# other attributes must be added:
# - errortype name
# - Source
# - errortype name
# for each event id in the event statistics
# display the most recent event
$Activity = "Looking up last event occurrence..."
$i= 0
foreach ($item in $evtStats)
{
$i += 1
$pct = ($i / $evtStats.Count * 100)
$eventID = $item.Name
$status = "EventID: "+ $item.Name
Write-Progress -Activity $Activity -Status $status -PercentComplete $pct
$customobj = "" | select Count,ErrorID,ErrorType,Message
$customobj.Count = $item.Count
$customobj.ErrorID = $item.Name
#get most recent event from the eventID
$id = $item.Name.ToInt32($Null)
[System.Threading.Thread]::CurrentThread.CurrentCulture = New-Object "System.Globalization.CultureInfo" "en-US"
$lastevent = get-winevent -FilterHashtable #{LogName=$eventlog;Id=$id} -MaxEvents 1 -ErrorAction SilentlyContinue
#depending on local settings, query might fail, if it fails reset to local culture
if ($lastevent.LevelDisplayName.Length -eq 0)
{
[System.Threading.Thread]::CurrentThread.CurrentCulture = $currentCulture
$lastevent = get-winevent -FilterHashtable #{LogName=$eventlog;Id=$id} -MaxEvents 1
}
$customobj.ErrorType = $lastevent.LevelDisplayName
$customobj.Message = $lastevent.Message
#prep EventID export
$exportfile = $resultPath + $eventlog +'_EventID_' + $customobj.ErrorID + ".csv.txt"
if ($exportEnabled)
{
$customobj | Export-Csv $exportfile -NoTypeInformation
}
$results += $customobj
}
#display with format
$results | Format-Table -AutoSize
return ,$results
if ($exportEnabled)
{
$exportfile = $resultPath + "_lastEvents_short_" + $eventlog + ".txt"
$results| Format-Table -AutoSize | out-file $exportfile
$exportfile = $resultPath + "_lastEvents_detail_" + $eventlog + ".txt"
$results | out-file $exportfile
}
}
#Write-Output $results | Format-Table -AutoSize
}
I then need to format the output of this function as a HTML table, so I can show it in my report.
$elogs = (Get-EventLogs) | Out-String
The code above outputs it as a long string (to be expected). But I cannot figure out how to format it as a HTML table. THe code that finally generates the report is as follows -
$Report = ConvertTo-Html -Title "$Computername" `
-Head "<center><h1>Server Report for <br><br>$Computername</h1></center><br /><br />" `
-Body "$Heading $Hardware $elogs $PercentFree $Output $Restarted $Services $Stopped $Css" }
Is there a way to output this as a table?
ConvertTo-Html is able to take an object and render that object as a HTML table. For simplicity's sake, I'll include the -Fragment switch that will ONLY return a HTML table (not all the HTML header, etc.).
Take a simple object, such as some basic process data, and you can create a HTML table:
[PS] $p = Get-Process | Select -First 3 -Property Name,ID
[PS] $p | ConvertTo-Html -Fragment
<table>
<colgroup><col/><col/></colgroup>
<tr><th>Name</th><th>Id</th></tr>
<tr><td>Process1</td><td>1888</td></tr>
<tr><td>Process2</td><td>1536</td></tr>
<tr><td>Process3</td><td>13920</td></tr>
</table>
Note that we don't need to specify a -Body with all the variables; we just need to make sure that we have all the data collected in an object (with the appropriate property names) and the cmdlet does the rest.
Going back to your script, there are a few things to look at:
Don't use Write-Host. If you want debug messages, consider using Write-verbose and Write-Debug. You'll need to set the $VerbosePreference and $DebugPreference variables to "Continue" to make this output visible.
Similarly, avoid using Format-Table
Your script returns an object ,$results. You should be able to pipe this into ConvertTo-Html with additional header and body content.
Note finally that the -Body switch doesn't format your object; it just includes extra HTML in front of the HTML table.

Trying to match strings when importing a CSV with Powershell

I am trying to create a report showing older operating systems out of a CSV file. The program displays the output beautifully, but doesn't actually parse anything. I'm trying to separate out Windows 2000, 2003, XP, and yes, windows NT machines. The sample data is below:
Finding Title,IP Address / FQDN,Port,Service,OS,FQDN,Default Username,Default Password,Remediation Notes,Misc Notes,Misc Notes 2,Contact,Remediation
Default Password (yada) for 'yadayada' Account on Windows,fakefake,445,cifs,Microsoft Windows Server 2003 Service Pack 2,fakefake,yadayada,yadayada,Change the password.,,,,
Default Password (yadayada) for 'yadayada' Account on Windows,fakefake1,445,cifs,Microsoft Windows Server 2003 Service Pack 2,fakefake1,yadayada,yadayada,Change the password.,,,,
Default Password (yadayada) for 'yadayada' Account on Windows,fakefake2,445,cifs,Microsoft Windows Server 2003 Service Pack 1,fakefake2,yadayada,yadayada,Change the password.,,,,
Default Password (yadayada) for 'yadayada' Account on Windows,fakefake3,445,cifs,Microsoft Windows Server 2008 R2 Standard,fakefake3,yadayada,yadayada,Change the password.
The program I have so far is here:
$name = Read-Host 'CSV File?'
$csvs = import-csv $name
$object = $csvs.os
ForEach ( $object in $csvs ) {
if ($object -like '*2003*') { out-file -inputobject $object -append $name}
elseif ($object -like '*2000*') { out-file -inputobject $object -append $name}
elseif ($objects -clike '*NT*') { out-file -inputobject $object -append $name}
elseif ($object -clike '*XP*') { out-file -inputobject $object -append $name}
write-output $outfile
}
$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: 2px;border-style: solid;border-color: black;background-color:thistle}"
$a = $a + "TD{border-width: 1px;padding: 2px;border-style: solid;border-color: black;background-color:white}"
$a = $a + "TD:Nth-Child(Odd) {Background-Color: #dddddd;}"
$a = $a + "</style>"
import-csv $name | ConvertTo-HTML -head $a| Out-File C:\Users\myuser\Downloads\scripts\Test.htm
I think you have a bug in there, you have 2 variables named object that you are using for different purposes:
$object = $csvs.os
ForEach ( $object in $csvs ) # object above getting overwritten
I would just get rid of the first object assignment and write the loop like this:
ForEach ( $object in $csvs ) {
if ($object.os -like '*2003*') { out-file -inputobject $object -append $name}
elseif ($object.os -like '*2000*') { out-file -inputobject $object -append $name}
elseif ($object.os -clike '*NT*') { out-file -inputobject $object -append $name}
elseif ($object.os -clike '*XP*') { out-file -inputobject $object -append $name}
}
Though you can avoid the repetition like this:
$csvs | where-object{ $_.os -like '*2003*' -or $_.os -like '*2000*' -or $_.os -clike '*NT*' -or $_.os -clike '*XP*'} | out-file -append $name

How to sum only 2nd column values using powershell?

sharesize.ps1
echo " "
$date1 = Get-Date
Write-Host -foreground Yellow -background Black "Script Started at $date1"
$path = "\*"
get-childitem $path | where {$_.PSIsContainer} | foreach {
$size = (Get-ChildItem $_ -recurse | where {!$_.PSIsContainer} | Measure-Object -Sum Length).Sum
$size = "{0:N2}" -f ($size / 1MB) + " MB"
$obj = new-object psobject
add-member -inp $obj noteproperty Path $_.fullName
add-member -inp $obj noteproperty "Size(MB)" $size
[array]$report += $obj
}
#display the table
$a = "<style>"
$a = $a + "BODY{background-color:green;}"
$a = $a + "TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}"
$a = $a + "TH{border-width: 2px;padding: 0px;border-style: solid;border-color: black;background-color:Yellow; font-family: Arial; font-size: 12pt}"
$a = $a + "TD{border-width: 2px;padding: 2px 6px 2px 3px;border-style: solid;border-color: black;background-color:Azure; font-family: Arial; font-size: 10pt}"
$a = $a + "</style>"
$report | Sort 'Size' -Descending | ConvertTo-HTML -head $a -title "Process Information" -body "<H2>Service Information</H2>"| Out-File -Append c:\temp\folder.html
$date2 = Get-Date
echo " "
Write-Host -foreground Yellow -background Black "Script Ended at $date2"
echo " "
Above code is working great to me, help is much appreciated for the below help.
Here my requirement is to add sum of 2nd column volues and append the output to the last line of the above code output html(c:\temp\folder.html) as,
Path | Size(MB)
C:\NVIDIA\Displaydriver | 400 MB
* | 860 MB
* | 100 MB
* | * MB
* | * MB
Total | 1000 MB(sum of all numbers in 2nd column values)
and also i need to align the 2nd column values and Total row to CENTER.
Please Help
To sum the sizes do this:
$totalSize = ($report | Measure-Object 'Size(MB)' -Sum).Sum
$total = 0
$report | % {[float]$tmp = $_."Size(MB)".TrimEnd(" MB"); $total += $tmp}
Then you can just add the $total object to your custom $report object before you convertTo-html and bango. Thanks for the neat script.
Only thing that confused me a little was your () your size property of $report makes PS think its a method, thus the quotes. Is not the best convention, but it works.
More explicitly :
...
[array]$report += $obj
}
$total = 0
$report | % {[float]$tmp = $_."Size(MB)".TrimEnd(" MB"); $total += $tmp}
$obj = new-object psobject
add-member -inp $obj noteproperty Path "Total Size: "
add-member -inp $obj noteproperty "Size(MB)" $total
[array]$report += $obj
#display the table
...
Also, Remove | Sort Size -Descending for the total to appear on the bottom.
I had a similar need and here is my simple solution:
$stuff=get-stuff
$results=
foreach ($item in $stuff) {
$item | select column1,#{N="Column 2";E={$_.column2}},description,created,#{N="Size (GB)";E={"{0:N2}" -f $_.sizegb}}
}
$results_sorted=#($results | sort created)
$results_sorted+=$item | select #{N="column1";E={"Totals"}},#{N="Column 2";E={$null}},#{N="Description";E={$null}},#{N="Created";E={$null}},#{N="Size (GB)";E={($results | Measure-Object "size (gb)" -Sum).Sum}}
$results_sorted | ft column1,"Column 2",Description,Created,"Size (GB)"