Skip to main content

Web app to run PowerShell commands on a computer from anywhere

Feodor Fitsner

Feodor Fitsner

Pglet founder and developer
info

After publishing this post on Reddit PowerShell community we received great feedback about security. Currently Pglet service is in preview and is not recommended for use in production. We are working on built-in authentication/authorization functionality at the moment. It's going to be "Login with GitHub/Google/Microsoft" OAuth at first plus OpenID for any other providers.

Normally, to access computer via PowerShell you need to configure PowerShell remoting, open WinRM ports on firewall and, the most unpleasant part, add NAT rule on your router to expose a computer to the entire Internet.

So, how can I securely make a web UI for my script without any port opened on the firewall? I used Pglet - a free open-source service for adding remote UI to your scripts. Pglet acts as a relay between your script and a web browser. Your script "dials" the service and sends UI state updates while web users receive live page updates via WebSockets. Kind of Phoenix LiveView for PowerShell :)

To run the app you need to install pglet module:

Install-Module pglet -Scope CurrentUser -Force

The module works on Windows PowerShell on Windows 10 and PowerShell Core 6+ on Windows, Linux and Mac.

Here is how the app looks like:

Below is the entire program (GitHub):

Import-Module pglet
Connect-PgletApp -Name "ps-console/*" -Web -ScriptBlock {
$ErrorActionPreference = 'stop'
$page = $PGLET_PAGE
$page.Title = "PowerShell Remote Console"
$page.HorizontalAlign = 'stretch'
# Textbox with a command entered
$cmd = TextBox -Placeholder "Type PowerShell command and click Run or press ENTER..." -Width '100%'
# Event handler to call when "Run" button is clicked or Enter pressed
$run_on_click = {
$cmd_text = $cmd.value
if ([string]::IsNullOrWhitespace($cmd_text)) {
return
}
# disable textbox and Run button, add spinner while the command is evaluating
$cmd.value = ''
$command_panel.disabled = $true
$results.controls.insert(0, (Text $cmd_text -BgColor 'neutralLight' -Padding 10))
$results.controls.insert(1, (Spinner))
$page.update()
try {
# run the command
$result = Invoke-Expression $cmd_text
# if result is Array present it as Grid; otherwise Text
if ($result -is [System.Array]) {
$result_control = Grid -Compact -Items $result
} else {
$result_control = Text -Value ($result | Out-String) -Pre -Padding 10
}
} catch {
$result_control = Text -Value "$_" -Pre -Padding 10 -Color 'red'
}
# re-enable controls and replace spinner with the results
$command_panel.disabled = $false
$results.controls.removeAt(1)
$results.controls.insert(1, $result_control)
$page.update()
}
# container for command textbox and Run button
$command_panel = Stack -Horizontal -OnSubmit $run_on_click -Controls @(
$cmd
Button -Text "Run" -Primary -Icon 'Play' -OnClick $run_on_click
)
# results container
$results = Stack
# "main" view combining all controls together
$view = @(
$command_panel
Stack -Controls @(
Stack -Horizontal -VerticalAlign Center -Controls @(
Text 'Results' -Size large
Button -Icon 'Clear' -Title 'Clear results' -OnClick {
$results.controls.clear()
$results.update()
}
)
$results
)
)
# display the "main" view onto the page
$page.add($view)
}

In the program I used just a few Pglet controls: Textbox, Button, Spinner and Stack for the layout. Controls are organized into DOM and every time page.update() is called its state is sent to Pglet service.

When you run the script, a new random URL is generated for your app and printed to the console:

At the very top of the program there is main entry point for the app:

Connect-PgletApp -Name "ps-console/*" -Web -ScriptBlock {
...
}

-Name parameter is a page URL and * means a randomly-generated string. You can add your own prefix to the random string or use another namespace, e.g. my-pages/ps-example-*.

If you want to tweak the app and test it locally, remove -Web parameter and a local Pglet server will be started. There is also self-hosted Pglet server if you need more control.

That's it! More great examples are coming!

Pglet 0.2.3

Feodor Fitsner

Feodor Fitsner

Pglet founder and developer

Pglet 0.2.3 adds more chart controls:

  • Pie Chart - demo
  • Line Chart - demo
  • Horizontal Bar Chart - demo

New controls#

  • Callout - demo
  • IFrame - demo
  • Header navigation menu (inverted toolbar) - demo

Deep linking#

It's now possible to switch between application states (views, areas) by means of URL hash. See how to use it in Deep linking guide.

Other fixes and improvements#

  • #13 Deep linking
  • #56 Fixed: VerticalBarChart control: theme colors are not supported in data points
  • #59 iframe control
  • #62 Enhancement - Application Installation Conforms to XDG Specification
  • #65 Line Chart (LineChart) control
  • #67 Horizontal Bar Chart (BarChart) control
  • #69 Pie Chart (PieChart) control
  • #70 Callout control
  • #71 Inverted toolbar button for use in app header
  • #73 Enhancement: configure pglet server options via config file
  • #74 Add host clients authentication with a token
  • #75 Stack control: overflow properties

Give Pglet a try and let us know what you think! There are multiple feedback channels available:

Pglet 0.2.2

Feodor Fitsner

Feodor Fitsner

Pglet founder and developer

Pglet 0.2.2 adds the first chart control - VerticalBarChart:

View live demo of VerticalBarChart control

Theming improvements#

All color-like attributes in all controls can now accept Fluent UI theme slot colors and "shared" colors.

The list of theme colors can be found on Fluent UI Theme Slots page.

The list of shared colors can be found on Fluent UI Shared Colors page.

For example, you can add an icon with themePrimary color:

add icon name=shop color=themePrimary

Color is being searched in the following order:

  1. Theme slot color
  2. Shared color
  3. Fallback to a named web color or color hex value.

Other fixes and improvements#

  • #43 Nav control: unify expanded/collapsed for groups and items
  • #45 ChoiceGroup control: Add "iconColor" property to Option
  • #46 Stack control: add "wrap" property
  • #47 Text control: Markdown mode
  • #48 Chart control: VerticalBarChart
  • #49 Add "trim" attribute to "add" command
  • #52 All color attributes accept theme slot colors and shared colors
  • #53 New control: Image
  • #54 Link control: can contain child controls and other improvements
  • #55 Text control: new "border*" properties

Give Pglet a try and let us know what you think! There are multiple feedback channels available:

Pglet 0.2.0

Feodor Fitsner

Feodor Fitsner

Pglet founder and developer

We've just released Pglet 0.2.0!

A ton of new controls were added such as navigation menu, toolbar, grid, tabs, dialog and panel. Now we feel confident Pglet allows to fully unleash your creativity and build a user interface of any complexity!

New features#

ARM support#

This release adds binaries for Linux ARM and Apple Silicon M1 - Go 1.16 made that possible. Now you can add remote web UI to your Raspberry PI apps or control what's going on in any IoT device.

Docker image#

Docker image pglet/server with self-hosted Pglet Server is now available - run it on a VPS server or drop into your Kubernetes cluster.

Theming#

Theming in Pglet takes similar approach as in Fluent UI Theme Designer - you choose primary, text, background colors and the theme is auto-magically generated from those colors. To change page theme in Pglet set page control properties: themePrimaryColor, themeTextColor, themeBackgroundColor. Check out Theme demo!

New controls#

Improved controls#

Button - additional types of Button control were implemented: compound buttons, icon buttons, toolbar, action and link buttons, buttons with context menus and split buttons. Check out Buttons demo!

Text - You can now control the styling of Text control such as color and background as well as border properties and text alignment within it (vertical alignment in center works too!). Check out Text demo!

Other fixes and improvements#

  • Controls are based on Fluent UI 8.
  • replace command.
  • Event ticker to avoid hanging event loops.
  • Pglet Server now does not allow remote host clients by default. Remote hosts clients can be enabled with ALLOW_REMOTE_HOST_CLIENTS=true environment variable. Pglet Server Docker image set this variable by default.

Give Pglet a try and let us know what you think! There are multiple feedback channels available:

Launching Pglet

Feodor Fitsner

Feodor Fitsner

Pglet founder and developer

Today we are officially launching Pglet!

This is not a groundbreaking event shaking the World and it won't make any ripples on the Internet, but it's still very important milestone for us as it's a good chance to make functionality cut off and present Pglet to the public.

What we've got#

You probably won't be able to do a real app with Pglet yet, but we believe it's quite the MVP in a Technical Preview state. The core Pglet functionality is there: pages can be created, controls can be added, modified and removed with live updates streamed to users via WebSockets, page control events triggered by users are broadcasted back to your program - the entire concept's working. We've got basic layout (Stack) and data entry controls (Textbox, Button, etc.) to do simple apps and dashboards, but Fluent UI library, Pglet is based on, is huge and it's a lot more controls to do!

We've got Pglet client bindinds for 4 languages: Python, Bash, PowerShell and Node.js. We chose these languages for MVP to have a good sense of what might be involved in supporting another language. These are scriping non-typed environments mostly, but probably the next binding we do would be Go or C#. Python bindings is the most complete implementation with classes for every control and control event handlers.

We've got Pglet Service which is a hosted Pglet server which you can use right away to bring your web experiences to the web. For technical preview it's just a basic deployment on GAE (will do a separate blog post about that), but quite enough to play with.

The experience#

It's been really exciting to work on Pglet during the last 6 months and we learned a lot. Being a C# and mostly Windows developer for more than 15 years it was an absolute pleasure to develop in Go: clean and simple language syntax, goroutines and channels, everything async by design, explicit exceptions management - I'll probably do another post about that experience! Making Pglet UI in React with TypeScript was fun as well: both are fantastic technologies! There is also a challenge to support multiple platforms. Pglet works on Windows, Linux and macOS and you need to constantly think about the experience on all 3 platforms and do a triple amount of tests, CI workflows and other chore things.

What's next#

For year 2021 our goal is being able to build and run full-blown backend apps in production. Therefore we are going to work in multiple directions:

Controls#

We are going to add more controls and improve existing ones. Pglet is still missing navigation controls like menus, toolbars and tabs. Grid is on top priority, for sure, and it's going to be the huge! Charts will be added as well, so you can build beautiful dashboards.

Pglet Service#

This year we are going to bring Pglet Service into production mode with a proper persistence, authentication and account/profile dashboards. All Pglet backend UI is going to be implemented with Pglet.

More docs and examples#

We'll be working on providing more Pglet examples, we'll write deployment guides for standalone Pglet apps and self-hosted Pglet Server.

Conclusion#

At this stage we are actively looking for any feedback to understand if the project idea is moving in the right direction. We'd be happy to know what would be your requirements, what's missing in Pglet, what's nice or inconvenient, what could be implemented with higher priority.

Feel free to give Pglet a try and let us know what you think! There are multiple feedback channels available:

Introducing Pglet

Feodor Fitsner

Feodor Fitsner

Pglet founder and developer
Pglet (pronounced as "piglet") empowers DevOps to easily add rich user interface into their internal apps and utilities without any knowledge of HTML, CSS and JavaScript.

The problems of internal apps#

Hi, I'm Feodor, the founder of AppVeyor - CI/CD service for software developers.

At AppVeyor, as any other startup, we do a lot of internal apps supporting the core business. Our users don't see those apps. These could be scripts for processing database data, monitoring dashboards, background apps for housekeeping, scheduled scripts for reporting, backend web apps for account management and billing.

Internal apps need User Interface (UI) to present progress/results and grab user input. The simplest form of UI is text console output. Console output can be easily produced from any program or script.

Text output has limitations. It could be hard to present complex structures like hierarhies or visualize the progress of multiple concurrent processes (e.g. copying files in parallel). There is no easy way to fill out the form. Plain text cannot be grouped into collapsible areas. We need rich UI for our internal apps.

Console output can be logged and studied later or you can sit in front of your computer and stare at log "tail". But we want to be mobile. We want to control internal apps from anywhere and any device. We want to share the progress of long-running process with colleagues or send a link to a realt-time dashboard to a manager. Maybe have "Approve" button in the middle of the script to proceed with irreversible actions. We want to collaborate on the results in a real-time. Does it mean we need to build another web app?

Building web apps is hard. Our small team is mostly DevOps. We all do development, deployment and maintenance. We are all good in doing backend coding in different languages: Bash, PowerShell, Python, C#, TypeScript. However, not every team member is a full-stack developer being able to create a web app. Frontend development is a completely different beast: HTTP, TLS, CGI, HTML, CSS, JavaScript, React, Vue, Angular, Svelte, WebPack and so on. Web development today has a steep learning curve.

Building secure web apps is even harder. Internal app works with sensitive company data (databases, keys, credentials, etc.) and presumably hosted in DMZ. You simply can't allow any developer being able to deploy web app with an access to internal resources and available to the whole world.

Pglet to the rescue#

Let's say you are the developer responsible for deployment and maintenance of backend services and database - DevOp. You mostly work with Golang and use Bash or Python for writing scripts and tools. Your team asked you to implement a simple real-time dashboard with some metrics from backend services. Dashboard should be accessible outside your org.

Should you do a web app? You don't have much experience of writing web apps. Alright, you know the basics of HTML/CSS, you know how to use StackOverflow, but how do you start with the app? Should it be IIS + FastCGI or Flask, plain HTML + jQuery or React, or something else?

Pglet gives you a page, a set of nice-looking controls and the API to arrange those controls on a page and query their state.

Pglet is the tool that hosts virtual pages for you. Think of a page as a "canvas", a "hub", an "app" where both your programs and users collaborate. Programs use an API to control page contents and listen to the events generated by users. Users view page in their browsers and continuosly receive real-time page updates. When in- API is just plain-text commands like "add column A", "add text B to column A", "get the value of textbox C", "wait until button D is clicked" - it's easy to format/parse strings in any programming language.

Bash-like pseudo-code for a simple app greeting user by the name could look like the following:

# create/connect to a page and return its "handle" (named pipe)
$p = (pglet page connect "myapp")
# display entry form
echo 'add row' > $p
echo 'add col id=form to=row' > $p
echo 'add textbox id=yourName to=form' > $p
echo 'add button id=submit to=form' > $p
# listen for events coming from a page
while read eventName eventTarget < "$p.events"
do
# user fills out the form and clicks "submit" button
if [[ "$eventTarget" == "submit" && "$eventName" == "click" ]]; then
# read textbox value
echo 'get yourName value' > $p
read $yourName < $p
# replace forms contents with the greeting
echo 'clear form' > $p
echo "add text value='Thank you, $yourName!' to=form" > $p
fi
done

You can build a web app in Bash! No HTML, no templates, no spaghetti code. You don't need to care about the design of your internal app - you get fully-featured controls with "standard" look-n-feel. What you care about is the time you need to deliver the required functionality.

Highlights#

  • Imperatively program UI with commands.
  • Standard controls: layout, data, form. Skins supported.
  • Fast and simple API via named pipes - call from Bash, PowerShell and any other language.
  • Secure by design. Program makes calls to Pglet to update/query UI. Pglet doesn't have access and knows nothing about internal resources located behind the firewall. Pglet keeps no sensitive data such as connection strings, credentials or certificates.
  • Two types of pages can be hosted:
    • Shared page: multiple programs/scripts can connect to the same page and multiple users can view/interact with the same page.
    • App: a new session is created for every connected user; multiple programs/scripts can serve user sessions (load-balancing).