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
NoLanguageorRestrictedLanguagemode, 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
- A user connects to a machine via
Enter-PSSessionorInvoke-Command, targeting a specific JEA endpoint (a named session configuration). - PowerShell checks the user's group membership against the role capability file to determine what commands they're allowed to run.
- The session runs under a virtual account with just enough privilege to execute the permitted commands.
- 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.psrcCreating a Role Capability File
$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 $rolePathwhere:
New-PSRoleCapabilityFilegenerates a valid.psrcrole definition template.-Pathtarget file path on disk within theRoleCapabilitiessubfolder.ModulesToImportmodules automatically loaded into the constrained session.-VisibleCmdletsthe 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:
@{
ModulesToImport = 'Microsoft.PowerShell.Management'
VisibleCmdlets = @(
@{ Name = 'Restart-Service'; Parameters = @{ Name = 'Name' } },
@{ Name = 'Get-Service' }
)
VisibleFunctions = @()
VisibleExternalCommands = @()
}where:
VisibleFunctions/VisibleExternalCommandsis an additional allowlist for custom functions (defined via theFunctionDefinitionsfield) 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:
$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.
Name Value
---- -----
Parameters {[ValidateSet, System.Object[]], [Name, Name]}
Name Restart-ServiceConfiguring 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.
$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 $configPathwhere:
New-PSSessionConfigurationFilegenerates the.psscconfiguration file.-SessionType RestrictedRemoteServerenforces the constrainedNoLanguagemode and limited command set required for JEA, hiding all built-in commands except core PowerShell remoting primitives.-RunAsVirtualAccountinstructs Windows to dynamically generate a temporary local administrator account to execute permitted commands, rather than running under the connecting user's own credentials.-RoleDefinitionsmaps an Active Directory or local group to one or more role capability files, determining what members of that group can do.-TranscriptDirectoryautomatically records an audit log of every command and output stream emitted inside the session.Test-PSSessionConfigurationFilevalidates syntax and verifies that referenced role capabilities exist on the system.
The resulting .pssc looks like this:
@{
SchemaVersion = '2.0.0.0'
SessionType = 'RestrictedRemoteServer'
RunAsVirtualAccount = $true
RoleDefinitions = @{
'CONTOSO\HelpDeskTier1' = @{ RoleCapabilities = 'ServiceOperator' }
}
TranscriptDirectory = 'C:\JEA\Transcripts'
}Name Value
---- -----
RunAsVirtualAccount True
SessionType RestrictedRemoteServer
RoleDefinitions {[CONTOSO\HelpDeskTier1, System.Collections.Hashtable]}
SchemaVersion 2.0.0.0
TranscriptDirectory C:\JEA\TranscriptsRegistering 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-PSSessionConfiguration -Name "HelpDeskServices" `
-Path "C:\JEAConfig\JEA_ServiceAdmin.pssc" `
-Force
Get-PSSessionConfiguration -Name "HelpDeskServices" |
Select-Object -Property Name, SecurityDescriptorSddl, RunAsUserwhere:
-Nameis the public name of the JEA endpoint, passed to the-ConfigurationNameparameter when connecting.-Pathis the path to the validated session configuration (.pssc) file.-Forceoverwrites an existing endpoint registration with the same name and restarts the WinRM service to apply the change.
./register-jea-endpoint.ps1Name SecurityDescriptorSddl RunAsUser
---- ---------------------- ---------
HelpDeskServices O:NSG:BAD:P(A;;GA;;;BA)(A;;GA;;;S-... Virtual AccountVerifying the Endpoint
Get-PSSessionConfiguration -Name "HelpDeskServices"Name : HelpDeskServices
SessionType : RestrictedRemoteServer
RunAsVirtualAccount : TrueConnecting 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.
Enter-PSSession -ComputerName "Server01" -ConfigurationName "HelpDeskServices"
Get-Command | Select-Object -Property CommandType, Name
Restart-Service -Name "Spooler"
Get-Processwhere:
-ConfigurationName "HelpDeskServices"targets the specific JEA endpoint instead of the unrestricted default remoting endpoint (Microsoft.PowerShell).Get-Commandlists only the commands permitted by the combined role capability definitions.
[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):
[Server01]: PS> Restart-Service -Name "wuauserv"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:
$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" -Forcewhere the role is scoped so tightly that helpdesk staff can view and restart only the Spooler service, nothing else.
./deploy-jea-endpoint.ps1Name Type Keys
---- ---- ----
PrintOpsEndpoint Container {Name=PrintOpsEndpoint}