MSXFAQ MeetNow aktiv: Komm doch einfach dazu.

PowerShell mit HTML-Ausgabe

Die Welt nutzt die dynamische Generierung von HTML-Ausgaben mit ASPX und PHP als "Code in Webseiten". Wie können wir mit PowerShell Code eine ähnliche Funktion erreichen?

Überlegungen

Dass ich die PowerShell sehr gerne nutze, können Sie an den verschiedenen Skripten auf der MSXFAQ sicher erkennen. Aber an einer Aufgabenstellung habe ich mich lange aufgehalten. Auf der Projektseite Reportweb habe ich umfangreich beschrieben, wie ich mit PowerShell entsprechende HTML-Dateien erstelle um diese dann über den IIS einem Browser bereit zustellen. Nur über einen Umweg habe ich da es erreicht, dass ich per Browser auch einen Report antriggern kann. Und ich hatte auch meine Gründe, warum ein PowerShell-Script eben nicht vom IIS ausgeführt werden sollte.

Aber es gibt sicher auf Fälle, in denen es trotzdem gewünscht ist, dass eine Webseite beim Aufruf dynamisch die Ergebnisse ermittelt. Und so habe ich mir Gedanken machen müssen, wie man PowerShell-Befehle durch den IIS aufruft und die Ausgaben an den Browser zurück sendet. Wer früher "dynamische Webseiten" entwickelt hat, nutzte entweder die klassische "CGI-BIN-"Schnittstelle, über die der Webserver einen Code ausführte, der dann die komplette HTML-Seite lieferte. Hier gibt es natürlich keinen "Status" und jede generierte Seite startet einen Prozess was Vor- aber auch Nachteile hat. Dann wurden die Webserver leistungsfähiger und erlaubten "Inline-Code", d.h. ich habe eine HTML-Seite geschrieben, in der Code eingebettet wurde, der dann vom Webserver an einen Handler übergeben hat. Der Handler war quasi ein Nebenprozess, der einfach aktiv geblieben ist. So ist das heute noch bei, IIS mit ASPX-Seiten oder PHP-Seiten. Der Webserver "interpretiert" den Code.

Entsprechende Module gibt es natürlich schon von Drittherstellern für den IIS. Die erste Version von PowerShell.ASP war sogar kostenfrei verfügbar. Dem ist aber nicht so und nicht immer will ich gleiche eine Webseite. Ich kennen dazu drei Varianten, die ich mit Beispielen vorstelle. Ich nutze einfach eine Liste der Prozesse als Ausgabe

PowerShell schreibt direkt HTML

Es ist sicher nicht schwer zu verstehen, wie ich in einem PowerShell-Script direkt eine HTML-Ausgabe in einer Datei codiere.

"<html>"
"<head><title>Serverstatus</title></head>"
"<body>"
"<h1>Titel</h1>"
"<p>Last Updates:"
Get-Date
"</p>"
"<h2>ServiceStatus</h2>"
Get-Service | Select Name,Status,Displayname | ConvertTo-Html
"</body>"
"</html>"

Die Lesbarkeit ist aber schon so nicht einfach und auch ein Syntax Highlightning von VSCode u.a. ist eingeschränkt:

Alternativ könnten Sie die Ausgabe auch die HTML-Ausgabe als mehrzeiligen String ausgehen und die Ergebnisse vorab erstellen.

Das Problem bei diesem Skript ist, dass der Editor eigentlich weiß, dass hier PowerShell Code und HTML-Code in einer Datei sind und nicht einfach zu editieren sind. Schon kleinste Fehler bei der Definition der HTML-Tags oder Sonderzeichen sind zeitaufwändig zu suchen.

Interpreter 1

Wer schon einmal PHP, ASPX oder andere "Webserver-Skriptsprachen" genutzt hat, kennt vermutlich die Konstruktion von <? und ?>, um Code in Webseiten einzubauen. Sie können die folgende Seite einfach als "test.php" auf einem Webserver speichern, der einen PHP-Handler hat um sie ausführen zu lassen:


Beispiel einer PHP-Seite

Jeder halbwegs pfiffige HTML-Editor zeigt HTML mit Farben an und unterstützt sie beim Entwickeln. Das möchte ich für PowerShell auch haben und habe den Code daher umgeschrieben.

<html>
<head><title>Serverstatus</title></head>
<body>
<h1>Titel</h1>
<p>Last Updates:<?PS Get-Date ?></p>
<h2>ServiceStatus</h2>
<?PS
get-service | select Name,Status,Displayname | convertto-html -fragment ?>
</body>
</html>

Wenn ich die Datei mit der Endung "HTML" speichere, dann kommen die meisten Editoren damit zurecht.

Allerdings versteht natürlich niemand die "<?PS" Angabe, um da drin dann PowerShell zu decodieren. Dazu brauchen wir dann einen eigenen Handler. Der ist auch relativ schnell mit etwas KI-Hilfe erstellt. Über Regular Expressions kann ich die Code-Teile aus der HTML-Datei aufteilen und dann in einem PSession über "System.Management.Automation.PowerShell" ausführen lassen. Jeder ausgeführte Block kann dabei weiterhin auf die Daten des vorherigen Codeteils zugreifen, d.h. Variablen sind bleiben erhalten.

Invoke-PS1HTML

Ich habe für Tests ein PowerShell-Skript geschrieben, welches solche HTML-Dateien nach "<?PS" durchsucht, den darin eingeklammerten Code ausführt und die Ausgaben einfügt, ehe dann über die Pipeline oder zusätzlich als Text-Datei schreibt

powershell/invoke-ps1html.ps1

Viel Spaß beim damit experimentieren 

Interpreter 2

Was allerdings mit dem ersten Interpreter nicht geht, sind "offene" Blöcke. Hier ein Beispiel:

<html>
<head><title>PS1X Test Offener Block</title></head>
<body>
<h1>PS1X Test Offener Block</h1>

<?PS if ((Get-Date) -ge (get-date "01.01.2026")) {   ?>

<p>Es ist nach dem 1.1. 2026</p>

<?PS } else { ?>

<p>Es ist vor dem 1.1. 2026</p>

<?PS } ?>

</body>
</html>

Wenn ich das erreichen will, könnte ich mit einem Interpreter einfach bei der Auswertung den Code zu umschreiben, dass  alle HTML-Blöcke zu Variablen werden, die dann in ein PowerShell-Script eingebunden werden. Aus dem Beispiel würde dann folgender Code:

@"
<html>
<head><title>PS1X Test Offener Block</title></head>
<body>
<h1>PS1X Test Offener Block</h1>
"@
if ((Get-Date) -ge (get-date "01.01.2026")) { 
@"
<p>Es ist nach dem 1.1. 2026</p>
"@
} else { 
@"
<p>Es ist vor dem 1.1. 2026</p>
"@
}
@"
<body>
</html>
"@

Schwer ist das nicht und den Code könnte ich dann auch in einem Rutsch ausführen und müsste nicht jeden einzelnen Block einem PowerShell Runspace zuschieben. Wir haben natürlich immer noch die Option, den Code am Anfang zu schreiben und im HTML-Code dann einfach die Variablen einzusetzen. Ich sollte den Code aber schon als eigenen Job, Session oder Runspace ausführen, damit nach dem Ende auch wieder alle Speicherbereiche freigegeben werden.

Meine einfachen Beispiele haben noch kein "Escape" der besonderen Zeichen. Wenn wie irgendwo im HTML-Code ein @", "@, <?PS, oder ?> verwenden, dann kann das Skript brechen.

Webserver-Extension

Interpreter 1 und Interpreter 2 können helfen, eine HTML-Datei mit dem Code zu verbinden und ausführen und generieren zu lassen. Der Interpreter-Code ändert sich nicht aber noch muss jemand aktiv werden. Die nächste Steigerung wäre dann eine ISAPI-Erweiterung für den IIS oder gleich einen eigenen Webserver auf Basis der HTTP-Listener (PowerShell als HTTPServer) oder der Kestrel-Servers

Generierung von HTML-Ausgaben anhand eines Templates ist nur der Anfang, Irgendwann wollen Sie vielleicht auch Ausgaben dynamisch als Webserver generieren. Hier gibt es dann eigentlich nur die Option, entweder auch in PowerShell direkt einen rudimentären Webeserver selbst zu schreiben oder eine IIS-Erweiterung zu entwickeln, die ihren ausführt.

Interessanterweise gibt oder gab es sogar eine entsprechende Erweiterung, die analog zum Namen "ASP" und "ASPX" eine "PS1X" eingebracht hat, die dann von einem PowerShell-Handler  verarbeitet wurden.

Leider wurde das Community Projekt später von nSoftware übernommen und ist mittlerweile nicht mehr auffindbar. Damit bleiben dann noch komplett in PowerShell geschriebene Webserver, von denen es einige gibt

Ich habe noch weitere Projekte auf PowerShell als HTTPServer verlinkt aber viele davon haben seit vielen Jahren keine Updates oder Weiterentwicklung erfahren. Entweder gibt es keine Fehler oder der Bedarf ist einfach nicht mehr vorhanden, weil entsprechende Projekte dann doch eher in PHP oder ASPX nativ codiert werden. Dynamisch interpretierter PS1-Code, den ein Webserver mit "LocalSystem"-rechnten aufruft und von einem weniger privilegiertem Benutzer editiert werden kann, ist durchaus ein Sicherheitsrisiko

Weitere Links