JEA - I got it to work, but inevitably...questions

Hi, POSHers,

The goal is to allow a TechOps engineering group to manage a Windows service specific to their application, and possibly, kill a specific Windows process.

I’ve set up a basic JEA endpoint using the guide

I just found this, too by Nathanial Webb Secure Your Powershell Session with JEA and Constrained Endpoints –

So, I’ll read 'em again, see what I missed.

Two main questions - Re: the role capabilities file, given the Visible Cmdlets section set up, why does get-service (no service name specified) return the whole list of services? And, how to create multiple functions in the Visible Functions section?

The basic set up:

VisibleCmdlets = 
@{ Name = 'get-service'; Parameters = @{ Name = 'Name'; ValidateSet = 'pm2.exe'}},
@{ Name = 'get-process'; Parameters = @{ Name = 'ProcessName'; ValidateSet = 'PM2' }}

I actually get the full list of services and processes, when executing get-service or get-process (without specifying the service name) from the pssession prompt.

Not the expected result. I might have expected some fault message saying the name parameter was null or empty, or maybe getting exactly the status of the pm2.exe service. But I got everything.

If I specify the service name, it’s good.

[server1]: PS>get-service pm2.exe

Status Name DisplayName
------ ---- -----------
Running pm2.exe PM2

If I specify an “illegal” name, I’m prevented, which is expected:

[server1]: PS>get-service wuauserv
Cannot validate argument on parameter 'Name'. The argument "wuauserv" does not belong to the set "pm2.exe" specified by the ValidateSet
attribute. Supply an argument that is in the set and then try the command again.
+ CategoryInfo : InvalidData: (:) [get-service], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,get-service

Why would get-service return everything, then?

So, I tried a different approach (because TechOps are the unwashed horde, and don’t deserve to see all Windows services…#ServerAdminOgre), and wrote a simple function in the Visible Functions section.

VisibleFunctions = 'check_pm2'

FunctionDefinitions = @{
Name = 'check_pm2'

ScriptBlock = {get-service |where {$ -eq 'pm2.exe'}}

Worked fine, no surprises:

[server1]: PS>check_pm2

Status Name DisplayName
------ ---- -----------
Running pm2.exe PM2


However, I don’t have the syntax knowledge to create a second function. In Visual Studio code, I’ve tried syntax variations like:

VisibleFunctions = 'check_pm2', 'check_pm2_process'

FunctionDefinitions = @{
Name = 'check_pm2'
ScriptBlock = {get-service |where {$ -eq 'pm2.exe'}}

Name = 'check_pm2_process' 
ScriptBlock = {get-process |where {$_.processname -eq 'pm2'}}

but VS Code returns errors that duplicate keys Name and ScriptBlock are not allowed for hash literals.

I’ve not yet found documentation that explains how to have multiple functions in this section, so…any ideas? Or links to how-to?

Yes, the blog said that if many functions are being written, then best to put them into a separate module. Cross that bridge when I get there.

In advance, thanks for the look-see and comments.

Did you happen to add the parameters and validate sets after you already registered? If so, you will need to unregister and re-register the endpoint. Your visibleCmdlet section looks correct.


Hi, pwshliquori,

Thanks for the thought.

The documentation clarified that role capability file can be changed on the fly. If a user is logged in, they will have to log out of their pssession for the change to take effect. I’ve tested that, it works as described.

True, for changes to the session file, yes, un-register and re-register the endpoint. As I’ve linked a local server account to the session file, add/delete from the local group can also be done on the fly.



So digging deeper, it looks like this is the expected behavior and cannot set a mandatory parameter in the psrc. I was able to replicate the issue you are having. I think the best way to go is to create that simple module with two functions making the name parameters mandatory. Or create a two simple scripts that get the service and process and run them when the endpoint kicks in.


Perfect, thank you for helping. That seems to be a good plan.

Just as a follow-on, I think I have the syntax. VS Code doesn’t list any code problems. For a few small functions, this seems the easiest. For larger or more quantity, writing them in the .psm1 or in individual .ps1 seems like the route to go.

VisibleFunctions = 'check_pm2', 'restart_pm2'



ScriptBlock= {get-service|where {$’pm2.exe’}} },


ScriptBlock= {restart-service-name ‘pm2.exe’-passthru}}