What is Just Enough Administration (JEA)?
Just Enough Administration (JEA) is a PowerShell security technology built into Windows Server 2022 that enables role-based access control for administrative tasks. Rather than granting users full administrator rights to perform specific jobs, JEA creates constrained PowerShell remoting endpoints where users can only run a pre-approved set of cmdlets and scripts. A helpdesk technician, for example, can be given the ability to reset AD passwords but nothing else — they cannot browse the filesystem, install software, or run arbitrary code. JEA sessions run as a privileged virtual account or Group Managed Service Account (gMSA) on the target server, so the connecting user never needs local admin rights. All activity is logged to a transcript file for auditing.
JEA is built on top of PowerShell Remoting (WinRM) and uses two configuration file types: Role Capability Files (.psrc) that define what a role can do, and Session Configuration Files (.pssc) that define the endpoint and map users to roles.
Creating Role Capability Files (.psrc)
A Role Capability File defines the set of PowerShell cmdlets, functions, providers, scripts, and external executables available within a JEA role. Create a role capability file using New-PSRoleCapabilityFile:
# Create the directory structure for role capabilities
# Role capability files must live in a RoleCapabilities subfolder of a PowerShell module
$modulePath = "$env:ProgramFilesWindowsPowerShellModulesJEARoles"
New-Item -Path "$modulePathRoleCapabilities" -ItemType Directory -Force
# Create a helpdesk role capability file
New-PSRoleCapabilityFile -Path "$modulePathRoleCapabilitiesHelpDesk.psrc" `
-Description "Helpdesk role: AD password reset and account unlock only" `
-VisibleCmdlets @(
'Restart-Computer',
@{ Name = 'Set-ADAccountPassword'; Parameters = @{ Name = 'Identity' }, @{ Name = 'NewPassword' }, @{ Name = 'Reset' } },
@{ Name = 'Unlock-ADAccount'; Parameters = @{ Name = 'Identity' } },
@{ Name = 'Get-ADUser'; Parameters = @{ Name = 'Identity' }, @{ Name = 'Filter' }, @{ Name = 'Properties' } },
'Get-Help'
) `
-VisibleFunctions @('TabExpansion2','Get-HelpMessage') `
-VisibleAliases @('help') `
-VisibleProviders @() `
-VisibleExternalCommands @()
# Create a network admin role capability file
New-PSRoleCapabilityFile -Path "$modulePathRoleCapabilitiesNetworkAdmin.psrc" `
-Description "Network admin role: manage network adapters and firewall rules" `
-VisibleCmdlets @(
'Get-NetAdapter',
'Get-NetIPAddress',
'Get-NetIPConfiguration',
'Get-NetRoute',
'Get-NetFirewallRule',
@{ Name = 'Set-NetIPAddress'; Parameters = @{ Name = 'InterfaceAlias' }, @{ Name = 'IPAddress' }, @{ Name = 'PrefixLength' } },
@{ Name = 'New-NetFirewallRule'; Parameters = @{ Name = 'DisplayName' }, @{ Name = 'Direction' }, @{ Name = 'Action' }, @{ Name = 'Protocol' }, @{ Name = 'LocalPort' } },
@{ Name = 'Remove-NetFirewallRule'; Parameters = @{ Name = 'DisplayName' } },
'Test-NetConnection',
'Resolve-DnsName',
'Get-DnsClientServerAddress',
'Clear-DnsClientCache'
) `
-VisibleExternalCommands @('C:WindowsSystem32ipconfig.exe','C:WindowsSystem32ping.exe','C:WindowsSystem32tracert.exe')
Notice the syntax for restricting individual parameters on cmdlets. The @{ Name = 'Set-ADAccountPassword'; Parameters = ... } hashtable form lets you further constrain which parameters a user can pass, preventing privilege escalation through parameter abuse. An attacker who can only pass -Identity and -NewPassword to Set-ADAccountPassword cannot pivot to other operations.
Creating Session Configuration Files (.pssc)
The Session Configuration File defines the JEA endpoint — who can connect, what roles they receive, how the virtual account is configured, and where transcripts are saved:
New-PSSessionConfigurationFile -Path "C:JEAHelpdeskEndpoint.pssc" `
-SessionType RestrictedRemoteServer `
-Description "JEA endpoint for helpdesk staff" `
-RunAsVirtualAccount $true `
-TranscriptDirectory "C:JEATranscriptsHelpdesk" `
-RoleDefinitions @{
'CORPHelpDeskTier1' = @{ RoleCapabilities = 'HelpDesk' }
'CORPHelpDeskTier2' = @{ RoleCapabilities = 'HelpDesk','NetworkAdmin' }
} `
-LanguageMode NoLanguage `
-ExecutionPolicy RemoteSigned
# Create transcript directory
New-Item -Path "C:JEATranscriptsHelpdesk" -ItemType Directory -Force
# Validate the configuration file syntax before registering
Test-PSSessionConfigurationFile -Path "C:JEAHelpdeskEndpoint.pssc"
The SessionType RestrictedRemoteServer setting is critical — it restricts the language mode and disables direct .NET object creation. The LanguageMode NoLanguage setting prevents users from running arbitrary PowerShell expressions; they can only call the explicitly approved cmdlets. Always run Test-PSSessionConfigurationFile before registering to catch syntax errors.
Registering the JEA Endpoint
Register the endpoint using Register-PSSessionConfiguration. This creates a WinRM listener that users connect to by name:
# Register the helpdesk JEA endpoint
Register-PSSessionConfiguration `
-Name "JEA_HelpDesk" `
-Path "C:JEAHelpdeskEndpoint.pssc" `
-Force
# Verify the endpoint is registered
Get-PSSessionConfiguration | Where-Object Name -like 'JEA*' |
Select-Object Name, PSVersion, RunAsVirtualAccountGroups
# The endpoint will be visible in WinRM listener list
winrm enumerate winrm/config/listener
After registration, restart WinRM or the endpoint will not be reachable until the next service restart. The -Force flag re-registers if an endpoint with the same name already exists, which is useful during updates.
Run-As Virtual Accounts vs Group Managed Service Accounts
JEA session commands run in the context of a privileged account on the target server — either a local Virtual Account or a Group Managed Service Account (gMSA).
Virtual Accounts are automatically created local accounts that are members of the local Administrators group (or a specified group) and are tied to the WinRM service. They are easy to set up and require no additional infrastructure:
# Virtual account (default - local admin on the target server)
# In .pssc:
RunAsVirtualAccount = $true
# Virtual account with restricted group membership (best practice)
RunAsVirtualAccount = $true
RunAsVirtualAccountGroups = @('Backup Operators') # only add to groups needed
Group Managed Service Accounts (gMSA) are preferred in domain environments because the account is domain-trusted. This is required when the JEA commands need to access network resources like other domain controllers or file servers:
# First create the gMSA in Active Directory (run on a DC)
New-ADServiceAccount `
-Name "jea-helpdesk" `
-DNSHostName "jea-helpdesk.corp.example.com" `
-PrincipalsAllowedToRetrieveManagedPassword "Domain Computers"
# Install the gMSA on the target server
Install-ADServiceAccount -Identity "jea-helpdesk"
Test-ADServiceAccount -Identity "jea-helpdesk"
# Reference in the .pssc file instead of RunAsVirtualAccount:
GroupManagedServiceAccount = 'CORPjea-helpdesk$'
Testing JEA Endpoints
After registering an endpoint, test it by connecting as a non-privileged user and verifying the restricted environment:
# Connect to the JEA endpoint as a helpdesk user
$credential = Get-Credential -UserName "CORPhduser01" -Message "Helpdesk credentials"
Enter-PSSession -ComputerName SRV01 `
-ConfigurationName "JEA_HelpDesk" `
-Credential $credential
# Inside the session, verify what is available
Get-Command # Should only show approved cmdlets
Get-PSProvider # Should show no providers if VisibleProviders = @()
# Try to run a forbidden command — should fail
Get-ChildItem C: # AccessDenied in NoLanguage mode
# Try an approved command
Get-ADUser -Identity "targetuser" -Properties Enabled,LockedOut
# Exit the session
Exit-PSSession
The Get-PSSessionCapability cmdlet lets administrators inspect what capabilities a specific user will have in an endpoint without connecting as that user — useful for auditing and troubleshooting:
# Run on the server where the endpoint is registered
Get-PSSessionCapability -ConfigurationName "JEA_HelpDesk" -Username "CORPhduser01" |
Select-Object Name, CommandType | Sort-Object CommandType, Name | Format-Table -AutoSize
Auditing JEA Sessions via Transcript Directory
Every JEA session automatically generates a transcript file in the directory specified in the .pssc file. These transcripts capture all input and output, providing a complete audit trail. Set up log rotation to manage transcript storage:
# Review transcript directory
Get-ChildItem "C:JEATranscriptsHelpdesk" | Sort-Object LastWriteTime -Descending | Select-Object -First 10
# Read the latest transcript
$latest = Get-ChildItem "C:JEATranscriptsHelpdesk" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
Get-Content $latest.FullName
# Find all sessions where Set-ADAccountPassword was called (security audit)
Select-String -Path "C:JEATranscriptsHelpdesk*.txt" -Pattern "Set-ADAccountPassword" |
Select-Object Filename, LineNumber, Line | Format-Table -AutoSize
# Clean up transcripts older than 90 days
Get-ChildItem "C:JEATranscripts" -Recurse -File |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-90) } |
Remove-Item -Force
Forward transcripts to a centralized log management system (SIEM) for long-term retention and correlation. PowerShell also supports Enhanced Script Block Logging via Group Policy which captures more detail than transcripts alone.
JEA for Helpdesk: AD Password Reset Only
A minimal helpdesk JEA configuration that only permits AD password resets and account unlocks, useful as a starting point for a Tier 1 support team:
# HelpDeskMinimal.psrc
New-PSRoleCapabilityFile -Path "$modulePathRoleCapabilitiesHelpDeskMinimal.psrc" `
-VisibleCmdlets @(
@{
Name = 'Set-ADAccountPassword'
Parameters = @(
@{ Name = 'Identity'; ValidateSet = $null },
@{ Name = 'NewPassword'; ValidateSet = $null },
@{ Name = 'Reset'; ValidateSet = $null }
)
},
@{ Name = 'Unlock-ADAccount'; Parameters = @( @{ Name = 'Identity' } ) },
@{ Name = 'Get-ADUser'; Parameters = @( @{ Name = 'Identity' }, @{ Name = 'Properties'; ValidateSet = 'LockedOut','Enabled','PasswordExpired','PasswordLastSet' } ) }
) `
-VisibleCmdlets @('Get-Help') `
-LanguageMode NoLanguage
Deploying JEA with DSC
For large-scale deployment, use Desired State Configuration to ensure JEA endpoints are consistently configured across all servers. The JeaDsc module on the PowerShell Gallery provides DSC resources for JEA:
Install-Module JeaDsc -Scope AllUsers -Force
# DSC Configuration to deploy JEA endpoint
Configuration HelpdeskJEA {
param([string[]]$ComputerName)
Import-DscResource -ModuleName PSDesiredStateConfiguration
Import-DscResource -ModuleName JeaDsc
Node $ComputerName {
File TranscriptDir {
Ensure = 'Present'
Type = 'Directory'
DestinationPath = 'C:JEATranscriptsHelpdesk'
}
JeaRoleCapabilities HelpDeskRole {
Ensure = 'Present'
Name = 'HelpDesk'
ModuleName = 'JEARoles'
VisibleCmdlets = @('Get-ADUser','Unlock-ADAccount','Set-ADAccountPassword')
Description = 'Helpdesk password reset role'
}
JeaSessionConfiguration HelpdeskEndpoint {
Ensure = 'Present'
Name = 'JEA_HelpDesk'
RoleDefinitions = "@{ 'CORPHelpDeskTier1' = @{ RoleCapabilities = 'HelpDesk' } }"
RunAsVirtualAccount = $true
TranscriptDirectory = 'C:JEATranscriptsHelpdesk'
DependsOn = '[JeaRoleCapabilities]HelpDeskRole','[File]TranscriptDir'
}
}
}
# Compile and apply
HelpdeskJEA -ComputerName 'SRV01','SRV02','SRV03' -OutputPath "C:DSCJEA"
Start-DscConfiguration -Path "C:DSCJEA" -Wait -Verbose -Force
JEA dramatically reduces the attack surface of your Windows Server 2022 infrastructure by enforcing least-privilege principles at the PowerShell layer. Even if a helpdesk account is compromised, the attacker is limited to resetting passwords — they cannot install malware, access the filesystem, or pivot to other systems. Combined with transcript logging forwarded to a SIEM, JEA provides both security enforcement and a complete audit trail that satisfies compliance requirements in regulated industries.