PRACTITIONER GUIDE | IDENTITY SECURITY
Practitioner GuideUpdated 12 min read

PowerShell JEA (Just Enough Administration): How to Give Help Desk PowerShell Access Without Making Them Domain Admins

No local admin required
is the core JEA property. A user connecting to a JEA endpoint runs under a virtual account (a temporary local admin created and destroyed per session) -- the connecting user's own account needs no elevated permissions. They cannot elevate beyond what the role capability file allows
Transcript per session
JEA logs every command and output to a transcript file by default. Transcripts are written to a path you define (typically a share the connecting user cannot delete). Every help desk action via JEA is attributable to the connecting user and the exact commands they ran
Role capability file
is a .psrc file that defines the allowed commands, parameters, aliases, and modules for a JEA role. Multiple roles can be assigned to different AD groups. A help desk role can allow password reset; a server role can allow service management -- the same JEA endpoint enforces both with different access per group
WinRM-based
JEA runs over PowerShell remoting (WinRM). It does not require any additional software installation beyond PowerShell 5.1+ and WinRM enabled. JEA session configurations are registered on the target machine; users connect using Enter-PSSession or Invoke-Command with the -ConfigurationName parameter

SponsoredRetool

Retool's new app builder is where AI-generated code ships safely

Building apps with AI is easy. Getting them to production safely is another story.

Start building for free today

Shared domain admin credentials for help desk work are a security failure that organizations accept because the alternative -- writing custom delegation for every administrative task -- seems prohibitively complex. JEA is the answer. It takes about an hour to build a constrained PowerShell endpoint for a specific role (password reset, service management, user account unlock), and the result is logged, auditable, least-privilege administration. The help desk engineer never sees admin credentials, never has admin rights, and every command they run is in a transcript file.

Create the Role Capability File

The role capability file (.psrc) defines what a user can do in the JEA session.

# Create the role capability file directory structure
# JEA looks for role capabilities in: <module>\RoleCapabilities\
$modulePath = 'C:\Program Files\WindowsPowerShell\Modules\JEA-HelpDesk'
New-Item -ItemType Directory -Path "$modulePath\RoleCapabilities" -Force

# Create the role capability file
New-PSRoleCapabilityFile -Path "$modulePath\RoleCapabilities\HelpDesk.psrc"

Edit the generated .psrc file to define the allowed capabilities:

# HelpDesk.psrc -- allowed operations for tier 1 help desk
@{
    # PowerShell edition
    SchemaVersion = '2.0.0.0'
    GUID = '<generate-with-New-Guid>'
    Author = 'Security Team'
    Description = 'Help desk tier 1: user unlock, password reset, service restart'

    # Allow specific commands (Cmdlet name + allowed parameters)
    VisibleCmdlets = @(
        # Unlock AD accounts
        @{ Name = 'Unlock-ADAccount'; Parameters = @{ Name = 'Identity' } },
        # Reset AD passwords (force change at next logon)
        @{ Name = 'Set-ADAccountPassword'; Parameters = @{ Name = 'Identity'; Name = 'Reset'; Name = 'NewPassword' } },
        # Get user info (read-only)
        'Get-ADUser',
        'Get-ADGroupMember',
        # Restart specific services
        @{ Name = 'Restart-Service'; Parameters = @{ Name = 'Name'; ValidateSet = @('Spooler', 'W32Time', 'BITS') } },
        @{ Name = 'Get-Service'; Parameters = @{ Name = 'Name' } },
        # Clear print queue
        @{ Name = 'Clear-PrintJobList'; Parameters = @{ Name = 'PrinterName' } },
        'Get-Printer'
    )

    # Allow these modules (imports their exported commands, filtered by VisibleCmdlets)
    ModulesToImport = @('ActiveDirectory', 'PrintManagement')

    # Visible functions (you can define custom functions here)
    VisibleFunctions = @('Get-HelpDeskTicketStatus')

    # Visible aliases (tab completion and shorthand)
    VisibleAliases = @()

    # Visible providers (filesystem paths visible in session)
    # Leave empty to restrict all drive access
    VisibleProviders = @()
}

Create the Session Configuration File and Register the Endpoint

The session configuration file (.pssc) ties roles to AD groups and sets the virtual account.

# Create the session configuration file
New-PSSessionConfigurationFile `
    -Path 'C:\JEA\HelpDeskEndpoint.pssc' `
    -SessionType RestrictedRemoteServer `
    -RunAsVirtualAccount `
    -RoleDefinitions @{
        'DOMAIN\HelpDesk-Tier1' = @{ RoleCapabilities = 'HelpDesk' }
        'DOMAIN\ServerAdmins' = @{ RoleCapabilities = 'HelpDesk', 'ServerManagement' }
    } `
    -TranscriptDirectory 'C:\JEA\Transcripts' `
    -LanguageMode NoLanguage
    # NoLanguage: blocks arbitrary PowerShell code; only pre-defined commands allowed
    # Use ConstrainedLanguage for slightly more flexibility (allows some PowerShell features)

# Register the JEA endpoint on the target machine
Register-PSSessionConfiguration `
    -Name 'JEA-HelpDesk' `
    -Path 'C:\JEA\HelpDeskEndpoint.pssc' `
    -Force

# Verify registration
Get-PSSessionConfiguration -Name 'JEA-HelpDesk' | Format-List

Set transcript directory permissions:

# Transcripts should be writable by the virtual account, not the connecting user
# Virtual accounts write as NT AUTHORITY\SYSTEM or the machine account
$acl = Get-Acl 'C:\JEA\Transcripts'
# Deny the JEA endpoint users the ability to delete or modify transcript files
# Grant only the security team / SIEM collector read access

For server fleet deployment, use DSC or Group Policy to push the module and session configuration:

# Copy the module to all target servers
$servers = @('server01', 'server02', 'fileserver01')
foreach ($server in $servers) {
    Copy-Item -Path 'C:\Program Files\WindowsPowerShell\Modules\JEA-HelpDesk' `
        -Destination "\\$server\C$\Program Files\WindowsPowerShell\Modules\JEA-HelpDesk" `
        -Recurse -Force
    Invoke-Command -ComputerName $server -ScriptBlock {
        Register-PSSessionConfiguration -Name 'JEA-HelpDesk' -Path 'C:\JEA\HelpDeskEndpoint.pssc' -Force
    }
}
Free daily briefing

Briefings like this, every morning before 9am.

Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.

Connect to a JEA Endpoint and Test

Connect as a help desk user:

# Interactive session
Enter-PSSession -ComputerName 'server01' -ConfigurationName 'JEA-HelpDesk'
# Prompt shows: [server01]: PS>
# User is now in the JEA constrained session

# Try an allowed command
Unlock-ADAccount -Identity 'jsmith'
# Should succeed

# Try a non-allowed command
Get-ADDomain
# Error: The term 'Get-ADDomain' is not recognized as the name of a cmdlet...

# Try to break out of the constrained session
[System.Reflection.Assembly]::LoadWithPartialName('System')
# Error: You cannot call methods on null-valued expressions (NoLanguage mode blocks this)

# Exit the session
Exit-PSSession

Invoke a specific command without an interactive session:

# Run a single JEA command from a script or ticketing system integration
Invoke-Command -ComputerName 'server01' -ConfigurationName 'JEA-HelpDesk' -ScriptBlock {
    Unlock-ADAccount -Identity $using:username
}

Verify what a connecting user can see in the JEA session:

# From within a JEA session:
Get-Command
# Returns only the allowed commands for the connecting user's role

Get-PSSessionCapability -ConfigurationName 'JEA-HelpDesk' -Username 'DOMAIN\helpdesk1'
# From outside: shows what a specific user will be able to do

Common JEA Use Cases and Role Capability Templates

Help desk account management:

# Allow: unlock account, reset password, read user info
VisibleCmdlets = @(
    @{ Name = 'Unlock-ADAccount'; Parameters = @{ Name = 'Identity' } },
    @{ Name = 'Set-ADAccountPassword'; Parameters = @{ Name = 'Identity'; Name = 'Reset'; Name = 'NewPassword' } },
    'Get-ADUser',
    @{ Name = 'Enable-ADAccount'; Parameters = @{ Name = 'Identity' } },
    @{ Name = 'Disable-ADAccount'; Parameters = @{ Name = 'Identity' } }
)

Service management (on-call team):

# Allow restarting a specific list of services
VisibleCmdlets = @(
    @{ Name = 'Restart-Service'; Parameters = @{ Name = 'Name'; ValidateSet = @('AppPool1', 'W3SVC', 'SQLServerAgent') } },
    @{ Name = 'Start-Service'; Parameters = @{ Name = 'Name'; ValidateSet = @('AppPool1', 'W3SVC') } },
    @{ Name = 'Stop-Service'; Parameters = @{ Name = 'Name'; ValidateSet = @('AppPool1', 'W3SVC') } },
    'Get-Service', 'Get-EventLog'
)

DNS management (network team, not domain admins):

ModulesToImport = @('DnsServer')
VisibleCmdlets = @(
    'Get-DnsServerZone', 'Get-DnsServerResourceRecord',
    @{ Name = 'Add-DnsServerResourceRecord'; Parameters = @{ Name = 'ZoneName'; Name = 'A'; Name = 'Name'; Name = 'IPv4Address' } },
    @{ Name = 'Remove-DnsServerResourceRecord'; Parameters = @{ Name = 'ZoneName'; Name = 'Name'; Name = 'RRType'; Name = 'Force' } }
)

Review transcripts for audit:

# Find all transcripts from a specific user
Get-ChildItem 'C:\JEA\Transcripts' | Where-Object {
    (Get-Content $_.FullName -First 20) -match 'DOMAIN\\helpdesk1'
}

# Parse a transcript for specific commands
Select-String -Path 'C:\JEA\Transcripts\*.txt' -Pattern 'Set-ADAccountPassword'

The bottom line

JEA deployment order: identify the three or four most common help desk and tier 1 admin tasks that currently require shared admin credentials, write a role capability file for each, register the JEA endpoint on the target server fleet, and test with a non-admin account. The transcript directory is your audit log -- protect it from modification by the connecting users and ship it to your SIEM. NoLanguage mode is the safest setting for help desk endpoints; consider ConstrainedLanguage only for more complex automation scenarios.

Frequently asked questions

Can JEA be used to prevent users from calling .NET classes directly?

Yes. The NoLanguage session mode (the safest setting) completely prevents users from executing arbitrary PowerShell code including .NET class calls, variable assignments, loops, and conditionals. They can only run the specific cmdlets defined in the role capability file. ConstrainedLanguage mode allows some scripting constructs (variables, loops) but blocks type access and .NET reflection. For help desk delegations, NoLanguage is almost always the right choice. For automation scripts that need more PowerShell constructs, ConstrainedLanguage provides a middle ground, but NoLanguage is preferred.

What is the virtual account in JEA and what privileges does it have?

A virtual account is a temporary local account created by JEA when a user connects, used to impersonate an administrator without the connecting user having those rights. For local operations (service management, file access on the target machine), the virtual account is a member of the local Administrators group. For Active Directory operations, the virtual account uses the machine account -- which only has standard domain read rights. If your JEA endpoint needs to write to AD (reset passwords, unlock accounts), you must configure JEA to run as a Group Managed Service Account (gMSA) instead of a virtual account and grant that gMSA the specific AD permissions needed.

Can I create a JEA endpoint that allows Active Directory password resets?

Yes, but it requires a gMSA rather than a virtual account. Virtual accounts have machine-account-level AD rights which are not sufficient to reset user passwords. Configure the session with RunAsVirtualAccountGroups for local operations, or use RunAsManagedServiceAccount with a gMSA that has been granted the Reset Password extended right on the relevant OU. Grant the gMSA only the specific delegated permissions needed (Reset Password, Write pwdLastSet for force-change-at-next-logon) -- not Domain Admin rights.

How do I keep JEA configurations consistent across a large server fleet?

Three approaches work at scale: PowerShell DSC (Desired State Configuration) with a JEA DSC resource to enforce the configuration, a startup script deployed via Group Policy that checks for and re-registers the JEA endpoint if missing, or packaging the role capability module as a PowerShell module distributed via an internal PowerShell repository (PSGallery-style feed). For most environments, copying the module directory to each server and registering the endpoint as part of your server build baseline is simplest. Treat JEA endpoints as infrastructure code -- version control the .psrc and .pssc files, and deploy changes through a change management process.

How do I audit what commands a JEA user actually executed during a session?

Enable JEA session transcription by setting TranscriptDirectory in the session configuration file (.pssc). Each JEA session generates a transcript recording every command typed and its output. Store transcripts in a central, write-protected network location rather than locally (to prevent tampering by users who have local admin). Additionally, enable PowerShell Script Block Logging via Group Policy -- this captures every script block executed in the JEA session in the PowerShell Operational event log (Event ID 4104), providing a second source of record that is harder to tamper with than local transcript files. Forward these event logs to your SIEM for centralized audit retention.

What are the most common JEA deployment mistakes that reduce its security value?

The most consequential JEA mistakes: overly broad role capability files that grant visibility commands (Get-ADUser, Get-Process, Get-ItemProperty) across the entire domain rather than scoping to specific OUs or systems, creating a single 'IT admin' JEA role that grants more capability than any individual team member actually needs rather than defining per-team roles (helpdesk, server admin, network admin), and failing to use RunAsVirtualAccount or RunAsGroupManagedServiceAccount -- without a run-as account, the session runs in the connecting user's context and JEA provides no privilege separation. A JEA endpoint that grants a helpdesk account the ability to run Restart-Service on any service (including security services) on any server in the domain has weaker isolation than a simple OU-scoped delegation. Scope capabilities to the specific servers and actions each role genuinely needs.

Sources & references

  1. Microsoft -- Just Enough Administration overview
  2. Microsoft -- JEA role capabilities
  3. Microsoft -- JEA session configurations

Free resources

25
Free download

Critical CVE Reference Card 2025–2026

25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.

No spam. Unsubscribe anytime.

Free download

Ransomware Incident Response Playbook

Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.

No spam. Unsubscribe anytime.

Free newsletter

Get threat intel before your inbox does.

50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.

Unsubscribe anytime. We never sell your data.

Eric Bang
Author

Founder & Cybersecurity Evangelist, Decryption Digest

Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.

Black Hat Giveaway

Win a $2,495 Black Hat pass.

Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.

Joins Decryption Digest daily briefing. Unsubscribe anytime.

Giveaway: Black Hat USA 2026 Full-Access Pass ($2,495 value)

Details →
Daily Briefing

Subscribe to enter the giveaway

Every subscriber is automatically entered. You also get daily threat intel every morning: zero-days, ransomware, and nation-state campaigns. Free. No spam.

Already subscribed? You're already entered.

Giveaway

Win a $2,495 Black Hat USA 2026 pass.