If you are like me you spend a lot of time in PowerShell. You have script that gather a lot of information and would love to share this information with others. But now what? Spin up a new MVC web application just to present some data? What if you could spin up a web server with a simple command? Deliver the content right from PowerShell without having to deal with another project or even more dependencies!

With OmniShell now you can!

Start-WebServer

Start-WebServer allows you to spin up a web server at any URI. Check this out.

Get-WebServer | Stop-WebServer

Start-WebServer -Uri "http://localhost" -OnRequest {
param ([string] $uri, [string] $method, [string] $data, [string] $headers)
return @{ Message = "$uri, $method, $data, $headers" }
}

Now you can visit http://localhost and see the details of the request. You should get an output similar to the following.

/, GET, , {"$id":"1","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36","Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8","Accept-Language":"en-US,en;q=0.9","Upgrade-Insecure-Requests":"1","Connection":"keep-alive","Accept-Encoding":"gzip, deflate, br","Host":"localhost"}
Note: Do not use Invoke-WebApi or Invoke-WebRequest to visit the server in the same OmniShell instance hosting the server. This will start a recursive loop that will end the universe. Ok, actually OmniShell will just lock up.

Now let's try something more advanced.

Get-WebServer | Stop-WebServer

Start-WebServer -Uri "http://localhost" -OnRequest {
param ([string] $uri, [string] $method, [string] $data, [string] $headers)

$html = "<html><ul>"
$html += Get-Process | % { "<li>" + $_.Name + "</li>" }
$html += "</ul></html>"

return @{ Message = $html }
}

Now you can visit http://localhost the response will contain a list of processes running on the host.

OK, what if we wanted to simulate a 302 redirect?

Get-WebServer | Stop-WebServer

Start-WebServer -Uri "http://localhost" -OnRequest {
param ([string] $uri, [string] $method, [string] $data, [string] $headers)
return @{ StatusCode = 302; RedirectLocation = "http://epiccoders.com" }
}

This will now return the response of 302 with a redirect to Epic Coders. Now when you visit http://localhost the request will be redirected to http://epiccoders.com.

Integration with other APIs

Ever need to integrate with another product or want to prototype something really quickly. For this example imagine you need to integrate with another API from another team but the API isn't ready. You need to start testing your part of the project but you don't want to spent a long time with a custom implementation?

No, problem just spin up a web API using OmniShell. Let's say the API you depend on needs to return a list of users. We'll just mock up the request.

Get-WebServer | Stop-WebServer

Start-WebServer -Uri "http://localhost/api/Users" -OnRequest {
param ([string] $uri, [string] $method, [string] $data, [string] $headers)

$users = @()
$users += @{ Name = "John Doe"; Age = 21; }
$users += @{ Name = "Jane Doe"; Age = 32; }

return @{ Message = ($users | ConvertTo-Json); ContentType = "application/json" }
}

Ok, so what if we wanted to simulate a failure?

Get-WebServer | Stop-WebServer

Start-WebServer -Uri "http://localhost/api/Users" -OnRequest {
param ([string] $uri, [string] $method, [string] $data, [string] $headers)

$users = @()
$users += @{ Name = "John Doe"; Age = 21; }
$users += @{ Name = "Jane Doe"; Age = 32; }

#return @{ Message = ($users | ConvertTo-Json); ContentType = "application/json" }
return @{ StatusCode = 500; Message = "Boom, something went wrong..." }
}

This will now return the follow response.

Request URL:http://localhost/api/Users
Request Method:GET
Status Code:500 InternalServerError
Content: Boom, something went wrong...

OK, but what if we wanted to send an error 500 but with a custom status description?

Get-WebServer | Stop-WebServer

Start-WebServer -Uri "http://localhost/api/Users" -OnRequest {
param ([string] $uri, [string] $method, [string] $data, [string] $headers)

$users = @()
$users += @{ Name = "John Doe"; Age = 21; }
$users += @{ Name = "Jane Doe"; Age = 32; }

#return @{ Message = ($users | ConvertTo-Json); ContentType = "application/json" }
return @{ StatusCode = 500; StatusDescription = "Nope..."; Message = "Boom, something went wrong..." }
}

This will now return the follow response.

Request URL:http://localhost/api/Users
Request Method:GET
Status Code:500 Nope....
Content: Boom, something went wrong...

You can also do more advance scenarios to support multiple URIs in one server.

Get-WebServer | Stop-WebServer

Start-WebServer -Uri "http://localhost/api/Users" -OnRequest {
param ([string] $uri, [string] $method, [string] $data, [string] $headers)

if ($uri.ToLower().EndsWith("/api/users")) {
$users = @()
$users += @{ Name = "John Doe"; Age = 21; }
$users += @{ Name = "Jane Doe"; Age = 32; }

return @{ Message = ($users | ConvertTo-Json); ContentType = "application/json" }
}

if ($uri.ToLower().EndsWith("/api/users/1")) {
$user = @{ Name = "John Doe"; Age = 21; }
return @{ Message = ($user | ConvertTo-Json); ContentType = "application/json" }
}

return @{ StatusCode = 404 }
}

This server will now response to "api/Users" and to the "api/Users/1" request. All other request will result in a response of 404 : Not Found.

You can see how easy it is to start a web server and deliver content. You can deliver as much content as you want and however you want all in PowerShell!

I'd be very interested to know if you find this cmdlet interesting and in what ways you end up using it.

contact information
303 Turner Street, Easley, SC 29640
864-616-2366, bobby.cannon@live.com