Basics of Creating Webpages with PowerShell

Updated: Mar 28

Creating a simple web report with PowerShell doesn't need to be a chore, there are limitations and its definitely not a proper HTML editor. It doesn't mean the output should look shoddy.

Like many I'm using PowerShell to analyse Windows and display the results. The screen grab below is a section of a report I'm currently working on and soon to be published. The script is a comprehensive vulnerability assessment written entirely in PowerShell and made to look pretty without trawling through copious amounts of log outputs.

This blog will cover the basics of taking PowerShell objects from various sources and creating HTLM output. Its not difficult, just fiddley, a couple of different techniques to successfully convert PowerShell to HTML maybe required.

Before everyone gets critical regarding the script formatting, some is due to how ConvertTo-HTML expects the data, most is to help those that aren’t familiar with scripting. There is a conscience decision not to use aliases or abbreviations and where possible to create variables.

#Set Output Location Variables

Nothing challenging here, creates a working directory, sets the variable for the report output. Tests the existence of the path and if doesn’t exist creates the directory structure.

$RootPath = "C:\Report"

$OutFunc = "SystemReport"

$tpSec10 = Test-Path "$RootPath \$OutFunc\"

if ($tpSec10 -eq $false)


New-Item -Path "$RootPath \$OutFunc\" -ItemType Directory -Force


$working = "$RootPath \$OutFunc\"

$Report = "$RootPath \$OutFunc\"+ "$OutFunc.html"

#HTML to Text

Keeping it simple, create a variable and add some text. This is the one I that ought to be straight forward and ended up being a bit of a pain. The conversion to HTML ended up producing garbage. Google gave some interesting solutions…. The fix I discovered turned out to be super simple. The fragment needs to be set as a ‘Table’ and not a ‘List’. Doh…..

$Intro = "The results in this report are a guide and not a guarantee that the tested system is not without further defect or vulnerabilities."

#Simple WMI

This is a report about Windows, had better collect some wmi attributes. There’s 2 methods, dump the attributes into a variable and process later. Or create a variable for each required attribute and hashtable the data, the latter is a lot of effort.

$hn = Get-CimInstance -ClassName win32_computersystem

$os = Get-CimInstance -ClassName win32_operatingsystem

$bios = Get-CimInstance -ClassName win32_bios

$cpu = Get-CimInstance -ClassName win32_processor

#Foreach and New-Object.

Now life starts to get interesting. The date format needs updating from “23/11/2021 00:00:00” to “23/11/2021” to maintain the formatting a ‘foreach’ is required to strip out the additional characters per line, then added to an array.

Under normal circumstances the red code snippet would suffice.

Foreach ($hfitem in $getHF)


$hfid = $hfitem.hotfixid

$hfdate = ($hfitem.installedon).ToShortDateString()

$hfurl = $hfitem.caption

$newObjHF = $hfid, $hfdate,$hfurl

$HotFix += $newObjHF


When dealing with HTML the correct method requires the use of ‘New-Object’ command.


$getHF = Get-HotFix | Select-Object HotFixID,InstalledOn,Caption

Foreach ($hfitem in $getHF)


$hfid = $hfitem.hotfixid

$hfdate = $hfitem.installedon

$hfurl = $hfitem.caption

$newObjHF = New-Object psObject

Add-Member -InputObject $newObjHF -Type NoteProperty -Name HotFixID -Value $hfid

Add-Member -InputObject $newObjHF -Type NoteProperty -Name InstalledOn -Value ($hfdate).Date.ToString("dd-MM-yyyy")

Add-Member -InputObject $newObjHF -Type NoteProperty -Name Caption -Value $hfurl

$HotFix += $newObjHF


#Pulling Data from the Registry

Registry keys require the ‘Get-ChildItem’ followed by ‘Get-ItemProperty’ to extract the individual settings from the Registry Hive. Each setting is then assigned to a variable.

$getUnin = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"

$UninChild = $getUnin.Name.Replace("HKEY_LOCAL_MACHINE","HKLM:")

$InstallApps =@()

Foreach ( $uninItem in $UninChild)


$getUninItem = Get-ItemProperty $uninItem

$UninDisN = $getUninItem.DisplayName -replace "$null",""

$UninDisVer = $getUninItem.DisplayVersion -replace "$null",""

$UninPub = $getUninItem.Publisher -replace "$null",""

$UninDate = $getUninItem.InstallDate -replace "$null",""

$newObjInstApps = New-Object -TypeName PSObject

Add-Member -InputObject $newObjInstApps -Type NoteProperty -Name Publisher -Value $UninPub

Add-Member -InputObject $newObjInstApps -Type NoteProperty -Name DisplayName -Value $UninDisN

Add-Member -InputObject $newObjInstApps -Type NoteProperty -Name DisplayVersion -Value $UninDisVer

Add-Member -InputObject $newObjInstApps -Type NoteProperty -Name InstallDate -Value $UninDate

$InstallApps += $newObjInstApps


#Cascading Style Sheets (CSS)

To apply a consistent style to each element we use a CSS containing text size, colour and font as well as spacing and background colours.

Each style, for example 'h1' has a set of properties that applies to any number of elements tagged "<h1>variable or text</span></h1>". reducing repeat lines of code required, update the the CSS and all elements receive the change.

CSS Tutorial ( is a good resource to learn and try out CSS.

In the example below h1, h2 and h3 set different sized fonts and colours.

$style = @"














border-width: 1px;

padding: 7px;

border-style: solid;


































The script references the CSS and applies to each element, in this case variables containing text.

$header1= "This is Header One."

$header2= "This is Header Two."

$header3= "This is Header Three."

$frag_H1 = $header1 | ConvertTo-Html -as table -Fragment -PreContent "<h1> $header1</span></h1>" | Out-String

$frag_H2 = $header2 | ConvertTo-Html -as table -Fragment -PreContent "<h2> $header2</span></h2>" | Out-String

$frag_H3 = $header3 | ConvertTo-Html -as table -Fragment -PreContent "<h3> $header3</span></h3>" | Out-String

ConvertTo-Html -Head $style -Body "<h1 align=center style='text-align:center'><span style='color:#4682B4;'>Header1 with Alt Colour</span><h1>",



$frag_H3 | out-file $Report

The result is..... triggering my love of consistency and complimentary colour schemes.

The 'styles' can be overridden by applying setting directly to the element, that's why is centred and blue, despite applying the <h1> style.

#Converting to HTML

Each WMI, Registry or PowerShell variable is converted with the element to HTML and stored in a variable.

Note: the WMI variables attributes are called via -property

$FragDescrip1 = $Descrip1 | ConvertTo-Html -as table -Fragment -PreContent "<h3><span>$Intro</span></h3>" | Out-String

$fragHost = $hn | ConvertTo-Html -As table -Property Name,Domain,Model -fragment -PreContent "<h2><span>Host Details</span></h2>" | Out-String

$fragOS = $OS | ConvertTo-Html -As table -property Caption,Version,OSArchitecture,InstallDate -fragment -PreContent "<h2><span>Windows Details</span></h2>" | Out-String

$fragBios = $bios | ConvertTo-Html -As table -property Name,Manufacturer,SerialNumber,SMBIOSBIOSVersion,ReleaseDate -fragment -PreContent "<h2><span>Bios Details</span></h2>" | Out-String

$fragCpu = $cpu | ConvertTo-Html -As table -property Name,MaxClockSpeed,NumberOfCores,ThreadCount -fragment -PreContent "<h2><span>Processor Details</span></h2>" | Out-String

$fragHotFix = $HotFix | ConvertTo-Html -As table -property HotFixID,InstalledOn,Caption -fragment -PreContent "<h2><span>Installed Updates</span></h2>" | Out-String

$fragInstaApps = $InstallApps | Sort-Object publisher,displayname -Unique | ConvertTo-Html -As Table -fragment -PreContent "<h2><span>Installed Applications</span></h2>" | Out-String

$HotFix output as a PowerShell variable

HotFixID InstalledOn Caption

-------- ----------- -------

KB5007292 23-11-2021

KB5004567 04-11-2021

KB5008295 10-11-2021

KB5007262 23-11-2021

KB5007414 23-11-2021

$HotFix after its been converted to HTML

<h2><span>Installed Updates</span></h2>










#Creating the Report

One final ConvertTo-Html applying the overall look and feel with the 'body' element plus the fragments and out putted as a .htm or .html file.

ConvertTo-Html -Head $style -Body "<h1 align=center style='text-align:center'><span style='color:#4682B4;'>TENAKA.NET</span><h1>",







$fragcpu | out-file $Report

The completed script can be found on Github (here) and will look like something like this.

9 views0 comments