SpecOps is a .NET7 web application that allows you to centralize and host your PowerShell 7 scripts for execution by anyone, particularly non-technical staff. The application is written with PowerShell in mind, but could easily be extended to allow other types. The application is intended to be used in a Windows-based Intranet environment.
Scripts are added and configured dynamically through the scriptsettings.json configuration file. Scripts can be added and changed on the fly, and the configuration specified in the json file allows for the dynamic generation of a GUI for users to input parameter values via rudimentary support for HTML5 input element types (i.e. text, date, number, etc.) and dropdowns via HTML select elements. The application leverages the jQuery Validation library so that basic validation is also possible by specifying HTML5-based attributes such as required, pattern, etc.
SpecOps now also contains a realtime PowerShell terminal to aid in script development or to simply execute a quick command. The terminal is made possible by jQueryTerminal, and syntax highlighting of input is done via PrismJS. The terminal is restricted to members of the Admin policy.
The application leverages Bootstrap4 and the AdminLTE theme for styling with all plugins available.
Script output is returned in realtime via SignalR. Serilog is set up for logging to a file, but can easily be extended to log to a database, elasticsearch, etc.
Security is based around the concept of Security Policies for different types of users (i.e. Admin, HelpDesk, L2-Support, etc.). Scripts themselves are grouped into Categories. Security Policies are defined in the appsettings.json file, and specify the Categories of scripts allowed for users in that policy as well as the Active Directory groups that are members of that policy. To access the site, you must be a member of an AD group included in one of the policies. The Script Runner page automatically determines the appropriate script Categories available and only shows the relevant scripts.
"SecurityPolicies": [
{
"Name": "Admin",
"Groups": [ "mydomain\\abc", "mydomain\\def" ],
"CategoryIds": [ "Admin" ]
},
{
"Name": "HelpDesk",
"Groups": [ "mydomain\\uvw", "mydomain\\xyz" ],
"CategoryIds": [ "MyApp1", "MyApp2" ]
}
]
The application also works off a domain - simply use your computer name\login instead of domain\login.
Scripts are configured in scriptsettings.json.
1) CategoryId allows for grouping of related scripts and can be any meaningful value (Admin, HelpDesk, MyApplication, etc.) 2) Id is the script's unique identifier and should be unique across all scripts (regardless of Category). A GUID is recommended. 3) PathAndFilename may be relevant to the application's folder (i.e. ./Scripts/Demo.ps1) or could be any fully qualified path to which the AppPoolIdentity has access. 4) Name is the user-friendly (short) name of the script that will be shown in the dropdown. 5) Summary is a brief description of the script and will be shown when selected in the dropdown. 6) InputParms is an array of Input Parameter objects for each parameter to be passed into the script.
"ScriptSettings": [
{
"CategoryId": "Admin",
"Id": "7CF9D423-5CB7-432B-A3E9-618340ADF5A7",
"PathAndFilename": "./Scripts/Demo.ps1",
"Name": "Demo",
"Summary": "This is a brief summary of what the demo script does....",
"EnableImpersonation": true,
"InputParms": [
{
"Name": "StrParam1",
"Type": "text",
"Description": "Input 3-letter String Parameter",
"Placeholder": "Enter text here...",
"Required": "true",
"Pattern": "[A-Za-z]{3}"
},
{
"Name": "DropdownParam1",
"Type": "select",
"Description": "Dropdown Select Parameter",
"Options": {
"": "Select a value...",
"abc": "ABC Text To Display",
"def": "DEF Text To Display",
"ghi": "GHI Text To Display"
},
"Required": "true"
},
],
"Runspace": {
"ExecutionPolicy": "Unrestricted",
"Min": 1,
"Max": 2,
"Modules": [
"PSDiagnostics"
]
}
}
}
]
Writing to the different output streams in the PowerShell script will trigger different output levels in the log. The desired css class can be specified for each level via the appsettings.json file as to facilitate the desired color coding of the log text. Each log record is also timestamped.
PowerShell Write | Output Level |
---|---|
Write-Output |
Data |
Write-Host |
Info |
Write-Information |
Info |
Write-Warning |
Warning |
Write-Debug |
Debug |
Write-Verbose |
Verbose |
Write-Progress |
Progress |
Write-Error (or Exceptions) |
Error |
Two additional output types not specific to the scripts are:
Example showing each Output Level being mapped to the desired css class:
"OutputLevels": [
{ "Name": "Data", "CssClass": "so-data" },
{ "Name": "Error", "CssClass": "so-error" },
{ "Name": "Warning", "CssClass": "so-warning" },
{ "Name": "Info", "CssClass": "so-info" },
{ "Name": "Progress", "CssClass": "so-progress" },
{ "Name": "Verbose", "CssClass": "so-verbose" },
{ "Name": "Debug", "CssClass": "so-debug" },
{ "Name": "System", "CssClass": "so-system" },
{ "Name": "Unknown", "CssClass": "so-unknown" }
]
SpecOps now includes a realtime PowerShell terminal to aid in script development or simply executing a quick command. The terminal leverages the jQueryTerminal library.
Access to the terminal is restricted to members of the Admin policy.
Script output is styled the same as the output table on the Script Runner page. Input is also syntax highlighted via PrismJS.
Impersonation is supported and is controlled via a dropdown option. When set to No, any code entered is executed and logged under the security context of the AppPoolIdentity. When set to Yes, it will be executed under the context of the current user.
Please be very careful in allowing access to the Terminal as it could be very dangerous in the hands of unskilled users.
Serilog is used to log detailed information about the application, the scripts being executed, etc. It is currently configured to log to text files in the /logs folder.
System-generated input parameters are also available in every script automatically and could be used for additional custom logging purposes. See the input parameters section for details.
The Script Runner page supports routing parameters for the CategoryId and the ScriptId. This allows you to construct links directly to the desired category, and optionally, script.
.../Pages/Scripts/{CategoryId}/{ScriptId}
Thank you to @keithbabinec for his work with hosted runspaces in his PowerShellHostedRunspaceStarterKits project.