Skip to content

PowerShell Just Enough Administration (JEA)

How PowerShell Just Enough Administration (JEA) enforces least-privilege access by delegating specific tasks (like restarting services) without granting full admin rights or exposing credentials.

Just Enough Administration (JEA) is a role-based security technology built into PowerShell Remoting that enforces the principle of least privilege. JEA lets you delegate specific administrative tasks to non-administrator users on remote machines without granting them full local administrator rights, unrestricted shell access, or knowledge of high-privilege credentials. Instead of giving someone local admin access to restart a service, JEA can grant them permission to run only Restart-Service on only approved services, nothing more.

The JEA Architecture and Concept

In traditional server administration, operators who need to perform single tasks (such as restarting a service or checking DNS settings) are often added to the local Administrators group. This grants blanket privileges across the entire operating system. Traditional delegation forces a choice between two extremes: no access, or full administrative access.

JEA introduces a middle ground built on four ideas:

  • Role-based access: users connect through a constrained PowerShell endpoint that exposes only a defined set of commands.
  • Virtual accounts: the session runs under a temporary, auto-generated, highly-privileged virtual account, so the connecting user never needs to know or possess administrative credentials.
  • Constrained language mode: the endpoint operates in NoLanguage or RestrictedLanguage mode, blocking arbitrary script blocks, variable manipulation, and .NET reflection.
  • Full auditing: every command executed within a JEA session is logged via PowerShell transcription and Windows Event Logs, linking each action back to the operator's actual identity rather than the virtual administrative account.
+------------------------------------------------------------------------+
|                          Connecting User                               |
|                  (e.g., CORP\HelpDeskOperator)                         |
+------------------------------------------------------------------------+
                                    |
                                    | Enter-PSSession -ConfigurationName HelpDeskJEA
                                    v
+------------------------------------------------------------------------+
|                      JEA Remoting Endpoint                             |
|                                                                        |
|  1. Session Configuration (.pssc) -> Maps user identity to roles       |
|  2. Role Capability (.psrc)       -> Exposes Restart-Service (only)    |
|  3. RunAs Virtual Account         -> Runs command with local admin     |
|                                      rights                            |
+------------------------------------------------------------------------+

How a JEA Session Works

  1. A user connects to a machine via Enter-PSSession or Invoke-Command, targeting a specific JEA endpoint (a named session configuration).
  2. PowerShell checks the user's group membership against the role capability file to determine what commands they're allowed to run.
  3. The session runs under a virtual account with just enough privilege to execute the permitted commands.
  4. All activity is logged for auditing.

Defining Capabilities: Role Capability Files (.psrc)

A role capability file (.psrc) defines what a given role is allowed to do, including which cmdlets, functions, and external programs are visible and executable within the session, as well as permitted parameters and validated parameter values.

To make a role capability discoverable, it must reside inside a subfolder named RoleCapabilities within an active module path on the target system:

MyJEAModule/
├── MyJEAModule.psd1
└── RoleCapabilities/
    └── ServiceOperator.psrc

Creating a Role Capability File

create-role-capability.ps1powershell
$modulePath = "$env:ProgramFiles\PowerShell\Modules\JEAOps\RoleCapabilities"
New-Item -Path $modulePath -ItemType Directory -Force | Out-Null

$rolePath = Join-Path $modulePath "ServiceOperator.psrc"

New-PSRoleCapabilityFile -Path $rolePath `
    -Description "Allows restarting and monitoring specific print and spooler services." `
    -VisibleCmdlets @(
        "Get-Service",
        @{
            Name = "Restart-Service"
            Parameters = @{
                Name = "Name"
                ValidateSet = @("Spooler", "PrintNotify")
            }
        }
    )

Test-Path -Path $rolePath

where:

  • New-PSRoleCapabilityFile generates a valid .psrc role definition template.
  • -Path target file path on disk within the RoleCapabilities subfolder.
  • ModulesToImport modules automatically loaded into the constrained session.
  • -VisibleCmdlets the explicit allowlist of cmdlets available. You can specify simple strings (e.g., "Get-Service") or hashtables that restrict specific parameters and validate permitted arguments.

The generated .psrc looks like this:

ServiceOperator.psrcpowershell
@{
    ModulesToImport = 'Microsoft.PowerShell.Management'
    VisibleCmdlets  = @(
        @{ Name = 'Restart-Service'; Parameters = @{ Name = 'Name' } },
        @{ Name = 'Get-Service' }
    )
    VisibleFunctions = @()
    VisibleExternalCommands = @()
}

where:

  • VisibleFunctions / VisibleExternalCommands is an additional allowlist for custom functions (defined via the FunctionDefinitions field) or non-PowerShell executables (e.g., C:\Windows\System32\ipconfig.exe).

Restricting to Specific Values

Role capabilities can go further, restricting a parameter to an explicit list of allowed values:

ServiceOperator.psrcpowershell
$VisibleCmdlets = @(
    @{
        Name       = 'Restart-Service'
        Parameters = @{
            Name        = 'Name'
            ValidateSet = 'Spooler', 'BITS'
        }
    }
)

where ValidateSet limits the -Name parameter to only Spooler and BITS, preventing the user from restarting any other service.

Outputplaintext
Name                           Value
----                           -----
Parameters                     {[ValidateSet, System.Object[]], [Name, Name]}
Name                           Restart-Service

Configuring Endpoints: Session Configuration Files (.pssc)

A session configuration file (.pssc) defines who can connect to the JEA endpoint. It maps security principals (users and groups) to their corresponding role capabilities and configures the execution environment, such as enabling virtual administrative accounts and transcription.

create-session-config.ps1powershell
$configPath = "C:\JEAConfig\JEA_ServiceAdmin.pssc"
New-Item -Path "C:\JEAConfig" -ItemType Directory -Force | Out-Null

New-PSSessionConfigurationFile -Path $configPath `
    -SessionType RestrictedRemoteServer `
    -RunAsVirtualAccount `
    -TranscriptDirectory "C:\JEATranscripts" `
    -RoleDefinitions @{
        'CORP\HelpDeskUsers' = @{ RoleCapabilities = 'ServiceOperator' }
    }

Test-PSSessionConfigurationFile -Path $configPath

where:

  • New-PSSessionConfigurationFile generates the .pssc configuration file.
  • -SessionType RestrictedRemoteServer enforces the constrained NoLanguage mode and limited command set required for JEA, hiding all built-in commands except core PowerShell remoting primitives.
  • -RunAsVirtualAccount instructs Windows to dynamically generate a temporary local administrator account to execute permitted commands, rather than running under the connecting user's own credentials.
  • -RoleDefinitions maps an Active Directory or local group to one or more role capability files, determining what members of that group can do.
  • -TranscriptDirectory automatically records an audit log of every command and output stream emitted inside the session.
  • Test-PSSessionConfigurationFile validates syntax and verifies that referenced role capabilities exist on the system.

The resulting .pssc looks like this:

JEAEndpoint.psscpowershell
@{
    SchemaVersion       = '2.0.0.0'
    SessionType         = 'RestrictedRemoteServer'
    RunAsVirtualAccount = $true
    RoleDefinitions     = @{
        'CONTOSO\HelpDeskTier1' = @{ RoleCapabilities = 'ServiceOperator' }
    }
    TranscriptDirectory = 'C:\JEA\Transcripts'
}
Outputplaintext
Name                           Value
----                           -----
RunAsVirtualAccount            True
SessionType                    RestrictedRemoteServer
RoleDefinitions                {[CONTOSO\HelpDeskTier1, System.Collections.Hashtable]}
SchemaVersion                  2.0.0.0
TranscriptDirectory            C:\JEA\Transcripts

Registering a JEA Endpoint

Once the role capability and session configuration files are in place, register the configuration with the Windows Remote Management (WinRM) service using Register-PSSessionConfiguration to create the actual JEA endpoint.

register-jea-endpoint.ps1powershell
Register-PSSessionConfiguration -Name "HelpDeskServices" `
                                 -Path "C:\JEAConfig\JEA_ServiceAdmin.pssc" `
                                 -Force

Get-PSSessionConfiguration -Name "HelpDeskServices" |
    Select-Object -Property Name, SecurityDescriptorSddl, RunAsUser

where:

  • -Name is the public name of the JEA endpoint, passed to the -ConfigurationName parameter when connecting.
  • -Path is the path to the validated session configuration (.pssc) file.
  • -Force overwrites an existing endpoint registration with the same name and restarts the WinRM service to apply the change.
Consolepowershell
./register-jea-endpoint.ps1
Outputplaintext
Name             SecurityDescriptorSddl                 RunAsUser
----             ----------------------                 ---------
HelpDeskServices O:NSG:BAD:P(A;;GA;;;BA)(A;;GA;;;S-... Virtual Account

Verifying the Endpoint

Consolepowershell
Get-PSSessionConfiguration -Name "HelpDeskServices"
Outputplaintext
Name                    : HelpDeskServices
SessionType             : RestrictedRemoteServer
RunAsVirtualAccount     : True

Connecting to and Auditing a JEA Session

Once registered, authorized non-administrator users connect the same way they would to any remoting endpoint, specifying the JEA endpoint's name via -ConfigurationName.

test-jea-session.ps1powershell
Enter-PSSession -ComputerName "Server01" -ConfigurationName "HelpDeskServices"

Get-Command | Select-Object -Property CommandType, Name

Restart-Service -Name "Spooler"

Get-Process

where:

  • -ConfigurationName "HelpDeskServices" targets the specific JEA endpoint instead of the unrestricted default remoting endpoint (Microsoft.PowerShell).
  • Get-Command lists only the commands permitted by the combined role capability definitions.
Outputplaintext
[Server01]: PS> Get-Command

CommandType     Name
-----------     ----
Function        Exit-PSSession
Cmdlet          Get-Command
Cmdlet          Get-FormatData
Cmdlet          Get-Help
Cmdlet          Get-Service
Cmdlet          Measure-Object
Cmdlet          Restart-Service
Cmdlet          Select-Object

Get-Process: The term 'Get-Process' is not recognized as a name of a cmdlet, function, script file, or executable program.

If the operator attempts to bypass the parameter constraints (for example, targeting an unauthorized service):

Consolepowershell
[Server01]: PS> Restart-Service -Name "wuauserv"
Outputplaintext
Cannot validate argument on parameter 'Name'. The argument "wuauserv" does not belong to the set "Spooler,PrintNotify" specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again.

Practical Usage: Delegating Print Spooler Restarts to Helpdesk

A common real-world JEA scenario is allowing helpdesk staff to fix a frequent, low-risk issue, a hung Print Spooler, without granting them administrative access to the print server. The following script generates both the role capability and session configuration files, then registers the resulting JEA endpoint in one pass:

deploy-jea-endpoint.ps1powershell
$modulePath = "C:\Program Files\WindowsPowerShell\Modules\PrintOpsJEA"
New-Item -Path "$modulePath\RoleCapabilities" -ItemType Directory -Force | Out-Null

@'
@{
    ModulesToImport = 'Microsoft.PowerShell.Management'
    VisibleCmdlets  = @(
        @{ Name = 'Restart-Service'; Parameters = @{ Name = 'Name'; ValidateSet = 'Spooler' } },
        @{ Name = 'Get-Service'; Parameters = @{ Name = 'Name'; ValidateSet = 'Spooler' } }
    )
}
'@ | Out-File -FilePath "$modulePath\RoleCapabilities\PrintOperator.psrc"

@'
@{
    SchemaVersion       = '2.0.0.0'
    SessionType         = 'RestrictedRemoteServer'
    RunAsVirtualAccount = $true
    RoleDefinitions     = @{ 'CONTOSO\HelpDeskTier1' = @{ RoleCapabilities = 'PrintOperator' } }
    TranscriptDirectory = 'C:\JEA\Transcripts'
}
'@ | Out-File -FilePath "C:\JEA\PrintOpsEndpoint.pssc"

Register-PSSessionConfiguration -Name "PrintOpsEndpoint" -Path "C:\JEA\PrintOpsEndpoint.pssc" -Force

where the role is scoped so tightly that helpdesk staff can view and restart only the Spooler service, nothing else.

Consolepowershell
./deploy-jea-endpoint.ps1
Outputplaintext
Name                       Type       Keys
----                       ----       ----
PrintOpsEndpoint           Container  {Name=PrintOpsEndpoint}
Navigation