It all started with a simple question “How to deliver reports to clients?” simple ASK and solution is further more simple. Yes, using NODE JS and PowerShell we can play with data! Since I can’t share client data and to keep this blog simple let me show a easiest way to retrieve system information as fancy HTML sorry PUG. In my upcoming blogs we will cover retrieving remote machines and bootstrapping for now it’s only for local host 🙂 ! All PowerShell lovers! Hold On I am not using ConvertTo-HTML or any other custom modules!
Our goal is to attain the below illustrated GIF 🙂
If you are PowerShell addict I bet you might have enjoyed Don Jones ConvertTo-EnhancedHTML – Find a TechNet Wiki Post shared by me! Yes, somewhat similar I tried! Now, let us host as web app using PowerShell, Node JS and PUG (NOTE: We play with PUG!). A sample system information PowerShell scripts is shown below! Yes, you can add functionality as required!
SystemInformation.ps1
$DiskInformation = Get-CimInstance -ClassName Win32_Volume | Select-Object DriveLetter, Label, SerialNumber, @{n = 'FreeSpace'; E = {"{0:N2}" -f ($_.FreeSpace / 1GB)}}, @{n = 'Capacity'; E = {"{0:N2}" -f ($_.Capacity / 1GB)}} $ServicesInformation = Get-CimInstance -ClassName Win32_Service | Select-Object Name , State , ExitCode -First 5 $ProcessInformation = Get-CimInstance -ClassName Win32_Process | Select-Object Name , WorkingSetSize , Handle -First 5 [pscustomobject]@{ Disk = $DiskInformation Service = $ServicesInformation ProcessInformation = $ProcessInformation } | ConvertTo-Json -Compress
We get output as illustrated below
{ "Disk": [ { "DriveLetter": "C:", "Label": "XyZ_OS", "SerialNumber": 3390096209, "FreeSpace": "269.02", "Capacity": "471.55" }, { "DriveLetter": null, "Label": "Windows", "SerialNumber": 1491697679, "FreeSpace": "4.46", "Capacity": "4.77" } ], "Service": [ { "Name": "AdobeARMservice", "State": "Stopped", "ExitCode": 1077 }, { "Name": "AdobeFlashPlayerUpdateSvc", "State": "Stopped", "ExitCode": 1077 }, { "Name": "AgentService", "State": "Running", "ExitCode": 0 }, { "Name": "AJRouter", "State": "Stopped", "ExitCode": 1077 }, { "Name": "ALG", "State": "Stopped", "ExitCode": 1077 } ], "ProcessInformation": [ { "Name": "System Idle Process", "WorkingSetSize": 4096, "Handle": "0" }, { "Name": "System", "WorkingSetSize": 32768, "Handle": "4" }, { "Name": "smss.exe", "WorkingSetSize": 212992, "Handle": "444" }, { "Name": "csrss.exe", "WorkingSetSize": 1761280, "Handle": "648" }, { "Name": "wininit.exe", "WorkingSetSize": 32768, "Handle": "756" } ] }
We all know this and no big deal here! What makes bit excitement is in PUG file! All, we need to loop through JSON and present the data! Here is the logic!
tbody tr th DriveLetter th Label th SerialNumber th FreeSpace th Capacity each result in results.Disk tr td=result.DriveLetter td=result.Label td=result.SerialNumber td=result.FreeSpace td=result.Capacity
Using each statement in pug we are retrieving the Disk information and likewise Service and Process data. Note here the properties are case sensitive! With no wait! Here is the full code!
Server.JS
var express = require('express'), app = express(), path = require('path'), bodyparser = require('body-parser'), pug = require('pug'), shell = require('node-powershell'), ps = new shell({ executionPolicy: 'bypass', noProfile: true }); app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'pug'); app.get('/', function (request, response) { response.render('index') }); app.get('/about', function (request, response) { response.render('about') }) // Get-SystemInformation app.get('/GetSystemInformation', function (request, response) { ps.addCommand("./scripts/SystemInformation.ps1") ps.invoke().then(output => { var SystemInformation = JSON.parse(output) response.render('SystemInformation', { results: SystemInformation }) }) }) app.listen(3000) console.log('Your application is hosted!')
SystemInformation.pug
doctype html head meta(name='viewport', content='width=device-width, initial-scale=1') style. .accordion { background-color: #eee; color: #333; cursor: pointer; padding: 18px; width: 100%; border: none; text-align: center; outline: none; font-size: 15px; transition: 0.4s; font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; } .active, .accordion:hover { background-color: #ccc; } .panel { padding: 0 18px; display: none; background-color: white; overflow: hidden; font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; } #services { font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; border-collapse: collapse; width: 100%; } #services td, #services th { border: 1px solid #ddd; padding: 8px; } #services tr:nth-child(even) { background-color: #f2f2f2; } #services tr:hover { background-color: #ddd; } #services th { padding-top: 12px; padding-bottom: 12px; text-align: left; background-color: #4CAF50; color: white; } h1 { text-align: center } h1 System Information button.accordion Volume Information .panel table#services hr tbody tr th DriveLetter th Label th SerialNumber th FreeSpace th Capacity each result in results.Disk tr td=result.DriveLetter td=result.Label td=result.SerialNumber td=result.FreeSpace td=result.Capacity hr button.accordion Service Information .panel table#services hr tbody tr th Name th ExitCode th State each result in results.Service tr td=result.Name td=result.State td=result.ExitCode button.accordion Process Information .panel table#services hr tbody tr th Name th WorkingSetSize th Handle each result in results.ProcessInformation tr td=result.Name td=result.WorkingSetSize td=result.Handle script. var acc = document.getElementsByClassName("accordion"); var i; for (i = 0; i < acc.length; i++) { acc[ i ].addEventListener("click", function () { this.classList.toggle("active"); var panel = this.nextElementSibling; if (panel.style.display === "block") { panel.style.display = "none"; } else { panel.style.display = "block"; } }); }
Enjoy PowerShell!