This ASP.NET Core "Shim" provides a fast, responsive way to serve a PowerShell based MVC website. If you are looking for a dead-simple way to execute and serve individual .ps1
scripts in IIS and do not care about speed, please check out my PWSHCGI7
project on GitHub
- Ensure the .Net Core hosting package is installed in IIS
- Publish this project to a folder
- Point IIS to that folder when making a new site
- Change the Application Pool to Unmanaged
$_GET
- use like PHP$variable = $_GET['somevariable']
$_POST
- use like PHP$variable = $_POST['somevariable']
$_COOKIE
- use like PHP$variable = $_COOKIE['somevariable']
$_JSONDATA
- Raw POST data unparsed if requestcontent-type
=application/json
$_RAWPOSTDATASTREAM
- used to access the RAW stream ofcontext.Request.Body
(File Uploads / etc)$_REQUEST_METHOD
- Will equal something likeGET
orPOST
$_REQUEST_PATH
- Will contain the request path (e.g./
,/home
,/api/something
)
$_HEADERS
- This is a dictionary for headers. Call$_.HEADERS.add("Key","Value")
$_STATUSCODE
- This is anINT
for the HTTP Response Status Code (e.g. 200 / 404)
All web requests get routed to a file called controller.ps1
under the PwshWeb directory. This framework forces you to adhere to an MVC model. An example of how a controller would work is below:
using module ".\modules\Get-ParsedPSHTML"
$templateDir = $PSScriptRoot + "\views\"
$modelDir = $PSScriptRoot + "\models\"
$template = [PSCustomObject]@{
htmlTemplateFile = "default_layout.html"
modelScript = ""
}
switch -Regex ($_REQUEST_PATH) {
"^\/$" {
$template.modelScript = "home.ps1"
}
"^\/unauthorized$" {
$template.htmlTemplateFile = "401.html"
}
"^\/api/authenticate.ps1$"{
$template.modelScript = "api\authenticate.ps1"
$template.htmlTemplateFile = ""
$_HEADERS.add("Content-Type", "application/json")
}
Default {
$template.htmlTemplateFile = "404.html"
$template.headers.add("Status", "404 Not Found")
$_HEADERS.add("Status", "404 Not Found")
$Global:_STATUSCODE = 404 #Global isn't required but VSCode complains otherwise
}
if ($template.modelScript -ne "") {
Invoke-Expression $(Get-Content -Raw -Path ($modelDir + $template.modelScript))
}
if ($template.htmlTemplateFile -ne "") {
$response = `
$(Get-ParsedPSHTML -inputHTML $(Get-Content -Raw -Path ($templateDir + $template.htmlTemplateFile)))
}
else {
$response = `
$controllerOutput
}
return $response #return response to ASP.NET Core shim for delivery back to IIS
Put static content that should be available without authentication in the public
directory under PwshWeb
Provides a method to serve binary data using authentication / other logic.
Set PWSH variable $_BINARYRESPONSE
to a Byte-Array value ([byte[]]
)
Example:
# controller.ps1
$_HEADERS.Add("Content-type", "image/png")
$_BINARYRESPONSE = [System.IO.File]::ReadAllBytes("C:\somepath\image.png")
Return a byte array directly to the pipeline
Due to how the PWSH SDK works, the output of arrays is returned one at a time to the pipeline. This results in a very large PSDataCollection<PSObject>
collection containing individual Byte
objects that need to be added to a List<byte>
in a foreach
loop and then returned by ASP.Net Core.
It is for this reason that this method should only be used for small byte arrays.
Example:
# controller.ps1
$_HEADERS.Add("Content-type", "image/png")
return [byte[]][System.IO.File]::ReadAllBytes("C:\somepath\image.png")
Set Environment Variable DEBUG
to TRUE
for PWSH Error Output on your web pages.
Example in IIS web.config
:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet" arguments=".\AspnetCorePWSH.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess">
<!--INSERT BELOW THIS LINE-->
<environmentVariables>
<environmentVariable name="DEBUG" value="TRUE" />
</environmentVariables>
<!--INSERT ABOVE THIS LINE-->
</aspNetCore>
</system.webServer>
</location>
</configuration>