What Is Just Enough Administration?
Just Enough Administration (JEA) is a PowerShell security feature that allows you to delegate specific administrative capabilities to users without granting them full administrator rights on a system. JEA works by creating a constrained PowerShell remoting endpoint — a custom PSSession configuration — that limits which cmdlets, parameters, functions, providers, and external programs a connecting user can access. When a user connects to a JEA endpoint, their session runs in a highly restricted environment regardless of their actual permissions on the system. The actual work is performed by a virtual account or group managed service account that has the necessary privileges, meaning the connecting user never directly holds those privileges themselves.
JEA solves one of the most persistent problems in Windows administration: the need to give IT staff admin rights on servers just to perform routine tasks. A helpdesk technician who needs to restart a specific service, reset a user’s Active Directory password, or read log files should not need to be a member of the local Administrators group. With JEA, they connect to a custom endpoint that exposes exactly those operations and nothing else. All activity is transcripted and auditable.
JEA is built on top of PowerShell Remoting (WinRM) and requires PowerShell 5.1 or later, which is included in Windows Server 2022. No additional software is needed.
Core JEA Components
JEA configuration involves two types of files:
- Role Capability Files (.psrc) — Define what a role can do: which cmdlets, parameters, aliases, functions, providers, and external programs are visible. Each role is described in its own .psrc file stored in a
RoleCapabilitiessubfolder within a PowerShell module. - Session Configuration Files (.pssc) — Define the endpoint itself: which users or groups are mapped to which roles, whether to use a virtual account or GMSA, the PowerShell version, transcript settings, and language mode.
The general workflow is:
- Create a PowerShell module to hold your role capability files.
- Create one or more .psrc files defining each role’s permitted operations.
- Create a .pssc file that maps AD groups to roles and configures the endpoint.
- Register the endpoint using
Register-PSSessionConfiguration. - Test by connecting with a non-admin account and verifying that only permitted commands are available.
Creating Role Capability Files
Role capability files are the heart of JEA. They define the exact commands available to a role. Start by creating a PowerShell module to house them — JEA discovers .psrc files by looking in the RoleCapabilities subfolder of any module in $env:PSModulePath.
# Create module directory structure
$modulePath = "C:Program FilesWindowsPowerShellModulesJEARoles"
New-Item -Path "$modulePathRoleCapabilities" -ItemType Directory -Force
# Create the role capability file scaffold
New-PSRoleCapabilityFile -Path "$modulePathRoleCapabilitiesHelpDesk.psrc"
Open the generated .psrc file and configure it. Here is a complete example for a helpdesk role that can manage DNS entries and restart specific services:
# HelpDesk.psrc
@{
# Modules to import into the JEA session
ModulesToImport = 'DnsServer', 'ActiveDirectory'
# Visible cmdlets — specify exact cmdlets or use wildcards
# Format: 'CmdletName' or @{Name='CmdletName'; Parameters=@{Name='ParameterName'; ValidateSet='Value1','Value2'}}
VisibleCmdlets = @(
# Allow Get-* cmdlets from DnsServer module
@{Name = 'Get-DnsServerResourceRecord'; Parameters = @{Name = 'ZoneName'}}
@{Name = 'Add-DnsServerResourceRecordA'; Parameters = @{Name = 'ZoneName'; Name = 'Name'; Name = 'IPv4Address'}}
@{Name = 'Remove-DnsServerResourceRecord'; Parameters = @{Name = 'ZoneName'; Name = 'Name'}}
# Allow specific AD cmdlets
'Get-ADUser'
'Get-ADComputer'
@{Name = 'Unlock-ADAccount'; Parameters = @{Name = 'Identity'}}
@{Name = 'Set-ADAccountPassword'; Parameters = @{Name = 'Identity'; Name = 'Reset'; Name = 'NewPassword'}}
# Allow restarting specific services only
@{Name = 'Restart-Service'; Parameters = @{Name = 'Name'; ValidateSet = 'DNS','W32Time','Spooler'}}
'Get-Service'
)
# Visible external commands (executables)
VisibleExternalCommands = @(
'C:WindowsSystem32ipconfig.exe'
'C:WindowsSystem32nslookup.exe'
)
# Visible aliases
VisibleAliases = @('gcm', 'gm', 'help', 'man')
# Visible providers (file system, registry, etc.)
# Restrict to read-only for most providers
VisibleProviders = @('FileSystem', 'Variable', 'Function')
# Functions defined inline that are available in the session
FunctionDefinitions = @(
@{
Name = 'Get-ServerEventLog'
ScriptBlock = {
param([string]$LogName = 'System', [int]$Count = 50)
Get-WinEvent -LogName $LogName -MaxEvents $Count
}
}
)
}
The ValidateSet parameter restriction is critical — it means that even though Restart-Service is allowed, the user can only restart the three named services. Attempting to restart any other service will fail with a validation error. This prevents a user from using a permitted cmdlet to harm services outside their area of responsibility.
Creating the Session Configuration File
The session configuration file (.pssc) defines the endpoint. It specifies which groups are mapped to which role capability files, the run-as account type, language mode, and transcript settings.
# Create the session configuration file scaffold
New-PSSessionConfigurationFile -Path "C:JEAHelpdeskEndpoint.pssc" `
-SessionType RestrictedRemoteServer
# Edit the generated file to configure it as follows:
# HelpdeskEndpoint.pssc
@{
# PowerShell version
SchemaVersion = '2.0.0.0'
GUID = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' # auto-generated
# Set to RestrictedRemoteServer to constrain the session
SessionType = 'RestrictedRemoteServer'
# Run commands as a virtual account (temporary admin account created per session)
RunAsVirtualAccount = $true
# Optionally restrict virtual account to specific AD groups
# RunAsVirtualAccountGroups = 'DNS Admins'
# Map AD groups to role capabilities
RoleDefinitions = @{
'CORPJEA-HelpDesk' = @{RoleCapabilities = 'HelpDesk'}
'CORPJEA-DNSAdmins' = @{RoleCapabilities = 'HelpDesk', 'DNSAdmin'}
'CORPDomain Admins' = @{RoleCapabilities = 'HelpDesk', 'DNSAdmin', 'FullAdmin'}
}
# Language mode — NoLanguage prevents arbitrary script execution
LanguageMode = 'NoLanguage'
# Transcript all JEA sessions for audit
TranscriptDirectory = 'C:JEATranscripts'
# Execution policy within the JEA session
ExecutionPolicy = 'Restricted'
}
The RunAsVirtualAccount = $true setting is important. When enabled, JEA creates a temporary local administrator account for each session. The connecting user’s credentials are used only for authentication — the actual commands run as the virtual account. This ensures that even if the connecting user’s account is compromised, the attacker only has access to the specific commands the JEA role permits, not full admin rights.
Registering the JEA Endpoint
Register the endpoint using Register-PSSessionConfiguration. This must be run from an elevated PowerShell prompt on the server where the endpoint will be hosted. After registration, the WinRM service is restarted automatically to pick up the new endpoint.
# Register the JEA endpoint
Register-PSSessionConfiguration -Name "HelpDesk" `
-Path "C:JEAHelpdeskEndpoint.pssc" `
-Force
# Verify the endpoint was registered
Get-PSSessionConfiguration -Name "HelpDesk"
# List all registered endpoints (default Microsoft.PowerShell endpoint will also appear)
Get-PSSessionConfiguration | Select-Object Name, Permission
The endpoint is now accessible over PowerShell Remoting. The -Force flag restarts WinRM automatically; without it, you will be prompted to confirm the WinRM restart.
To update an existing endpoint after modifying the .pssc or .psrc files:
# Unregister and re-register to apply changes
Unregister-PSSessionConfiguration -Name "HelpDesk"
Register-PSSessionConfiguration -Name "HelpDesk" `
-Path "C:JEAHelpdeskEndpoint.pssc" `
-Force
Connecting to a JEA Endpoint
Users connect to a JEA endpoint using Enter-PSSession with the -ConfigurationName parameter specifying the JEA endpoint name. The user does not need local admin rights on the target server — they only need WinRM network access and membership in one of the groups mapped in the .pssc file.
# Connect to JEA endpoint interactively
Enter-PSSession -ComputerName "Server01.corp.example.com" `
-ConfigurationName "HelpDesk" `
-Credential (Get-Credential "CORPhelpdesk.user")
# Inside the JEA session, only permitted commands are visible
# The prompt shows [Server01]: PS> indicating a remote session
[Server01]: PS> Get-Command # Shows only JEA-permitted commands
[Server01]: PS> Get-ADUser -Identity "jsmith" # Works
[Server01]: PS> Get-Process # Fails — not in role capability
[Server01]: PS> Restart-Service -Name "DNS" # Works
[Server01]: PS> Restart-Service -Name "BITS" # Fails — not in ValidateSet
[Server01]: PS> Exit-PSSession
For non-interactive (scripted) use, create a PSSession and invoke commands against it:
$session = New-PSSession -ComputerName "Server01" `
-ConfigurationName "HelpDesk" `
-Credential $cred
Invoke-Command -Session $session -ScriptBlock {
Unlock-ADAccount -Identity "jsmith"
Get-ADUser -Identity "jsmith" | Select-Object Name, LockedOut, Enabled
}
Remove-PSSession -Session $session
Transcript Logging for JEA Sessions
One of JEA’s most valuable features for security and compliance is automatic transcript logging. When TranscriptDirectory is set in the .pssc file, PowerShell writes a full transcript of every command entered and every output returned to a file on the server. Transcripts are stored with a filename that includes the date, time, and connecting user’s name, making them easy to search and correlate with specific incidents.
# Example transcript file path:
# C:JEATranscripts
# PowerShell_transcript.SERVER01.HELPDESK_USER.20260517130412.txt
# Transcript content example:
# **********************
# Windows PowerShell transcript start
# Start time: 20260517130412
# Username: CORPhelpdesk.user
# RunAs User: WinRM Virtual UsersWinRM VA_1_helpdesk
# Configuration Name: HelpDesk
# Machine: SERVER01
# **********************
# PS> Unlock-ADAccount -Identity jsmith
# PS> Get-ADUser -Identity jsmith | Select-Object Name, LockedOut
Ensure the transcript directory is protected so that only the JEA virtual account (and administrators) can write to it, but the transcript files are not readable by connecting users:
# Create and secure the transcript directory
New-Item -Path "C:JEATranscripts" -ItemType Directory -Force
# Grant write access to SYSTEM and Administrators, remove user access
$acl = Get-Acl "C:JEATranscripts"
$acl.SetAccessRuleProtection($true, $false) # Disable inheritance
$adminRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
"BUILTINAdministrators", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow")
$systemRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
"NT AUTHORITYSYSTEM", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow")
$acl.AddAccessRule($adminRule)
$acl.AddAccessRule($systemRule)
Set-Acl -Path "C:JEATranscripts" -AclObject $acl
Forward the transcript directory to your SIEM or log management platform using a file-watching agent so that JEA transcripts are retained centrally and reviewed as part of your privileged access monitoring program.
Get-PSSessionCapability: Checking Effective Permissions
Get-PSSessionCapability allows an administrator to preview exactly which commands a specific user would see when connecting to a JEA endpoint, without requiring that user to actually connect. This is useful for validating your role configuration and troubleshooting access issues.
# Check what CORPhelpdesk.user can do on the HelpDesk endpoint
Get-PSSessionCapability -ConfigurationName "HelpDesk" `
-Username "CORPhelpdesk.user"
# Output shows all cmdlets, functions, aliases, and commands available to that user
# This reflects the union of all role capabilities the user is mapped to
# Check if a specific user has access to a specific command
Get-PSSessionCapability -ConfigurationName "HelpDesk" `
-Username "CORPhelpdesk.user" |
Where-Object {$_.Name -eq "Restart-Service"}
Virtual Accounts vs Group Managed Service Accounts
JEA supports two types of run-as accounts for executing commands on behalf of connecting users.
Virtual accounts are temporary local accounts created automatically by JEA for each session. They are members of the local Administrators group on the JEA server (or a custom group if RunAsVirtualAccountGroups is specified). Virtual accounts require no pre-provisioning, work immediately after registration, and are destroyed when the session ends. They have no password and cannot be used to authenticate to other systems, which limits the blast radius if a JEA session is misused. Virtual accounts are the recommended choice for most JEA endpoints that do not need to authenticate to remote resources.
Group Managed Service Accounts (gMSA) are domain accounts whose password is managed automatically by Active Directory. Use a gMSA when the JEA endpoint needs to access network resources — for example, if the JEA session needs to read from a file share, query a remote SQL database, or call a web service. The gMSA can be granted the necessary permissions on those remote resources, and JEA sessions will run as the gMSA. Configure a gMSA in the .pssc file as follows:
# First create the gMSA in AD (requires Key Distribution Service)
New-ADServiceAccount -Name "JEA-HelpDeskSvc" `
-PrincipalsAllowedToRetrieveManagedPassword "JEA-HelpDesk-Servers" `
-DNSHostName "jea-helpdesk.corp.example.com"
# Install the gMSA on the JEA server
Install-ADServiceAccount -Identity "JEA-HelpDeskSvc"
# Configure the .pssc to use the gMSA instead of a virtual account
# In HelpdeskEndpoint.pssc:
# RunAsVirtualAccount = $false
# GroupManagedServiceAccount = 'CORPJEA-HelpDeskSvc$'
Auditing JEA Usage
Beyond transcript logging, JEA sessions generate standard PowerShell event log entries and Windows Security events. Enable PowerShell Script Block Logging and Module Logging to capture additional detail about commands executed in JEA sessions:
# Group Policy path for script block logging:
# Computer Configuration > Administrative Templates > Windows Components >
# Windows PowerShell > Turn on PowerShell Script Block Logging
# Value: Enabled
# Group Policy path for module logging:
# Computer Configuration > Administrative Templates > Windows Components >
# Windows PowerShell > Turn on Module Logging
# Module Names: * (log all modules)
# Events appear in:
# Microsoft-Windows-PowerShell/Operational log
# Event ID 4103 (module logging)
# Event ID 4104 (script block logging)
# Also monitor Windows Security event 4656/4688 for virtual account process creation
# Filter for RunAs entries containing "WinRM VA_" to identify JEA sessions
Combining JEA transcripts, PowerShell script block logging, and Windows Security event 4688 process creation events gives you a comprehensive audit trail of all privileged operations performed through JEA. This audit trail is essential for detecting misuse, supporting incident response investigations, and demonstrating compliance with least-privilege access controls required by frameworks such as NIST SP 800-53, ISO 27001, and PCI DSS.
For organizations with many JEA endpoints, consider centralizing transcript files using Robocopy scheduled tasks or a SIEM forwarder, and building automated alerts for specific command patterns — for example, any JEA session that attempts commands outside its role capability (which generates a distinct error event), or sessions exceeding an unusually long duration.