We're reclaiming inactive PDIs to keep them available for active builders. Learn what's changing, who's affected, and how to protect your work. Read More

Article - JEA Discovery Setup : Full Walkthrough and Every Failure Scenario I've Hit

Vishnu-K
Kilo Sage

I've been through the JEA Discovery setup enough times now that I figured it was worth writing up properly. The official docs cover the basics, but there are a handful of gotchas that aren't obvious until something breaks late at night. This guide covers the full setup from scratch, plus every failure scenario I've personally run into.

Quick background if you're new to this: JEA (Just Enough Administration) is a PowerShell feature that lets you lock down what a remote session can actually do. Instead of giving your discovery account local admin rights on every Windows server, you expose only the specific function that ServiceNow needs. Much cleaner from a security standpoint, and most security teams are a lot happier with it.

 

Note: A few exact strings referenced below (a certificate provider name, a .NET namespace, and one Windows folder or error name) contain a word this community's filter blocks, so they're not spelled out inline. There is a separate attached file, JEA_Discovery_Exact_Commands.txt, with those pieces written out exactly. Cross reference it wherever you see "(see attachment)" below.

How It Actually Works

Before touching any config, it's worth understanding what's happening under the hood, because when things go wrong, this is what you're debugging.

Two MID Server parameters control the whole thing:

  • mid.windows.management_protocol: defaults to WMI, needs to be WinRM
  • mid.powershell.jea.endpoint: the name of the JEA endpoint on your target servers (for example JEA_DISCO_V2)

When the MID starts up, it reads those parameters and imports its PowerShell modules, including JEAUtils.psm1. Part of that import is finding a code signing certificate and storing it in a global variable called SNC_jea_disco_cert. If you see the log entry "SNC_jea_disco_cert is now available!" in the agent log, that part worked.

When a Discovery probe fires, the MID checks whether it has that certificate and a JEA endpoint name. If yes, it signs the script block it's about to run using the certificate's private key, opens a WinRM session to the target using New-PSSession, and sends the signed block via a function called JEAExecute-Script.

On the target side, the JEA session is extremely locked down: NoLanguage mode, RestrictedRemoteServer session type, and literally the only thing you can call is JEAExecute-Script. That function verifies the signature before running anything. If the signature checks out, it runs. If not, it denies it.

 

One thing people miss: The MID is running all of this as the MID Server service account, not as your discovery credential. The service account is what needs access to the signing certificate's private key. The discovery credential only comes into play when actually authenticating the WinRM session.

Setup Walkthrough

Step 1: Get the JEA profile files onto your target servers

Grab the sample JEA v2 profile from KB0965705. It gives you three files that need to live in a specific folder on each target server. Replace <jea_folder_name> with whatever you want to call the module folder. I usually just call it JEA.

C:\Windows\System32\WindowsPowerShell\v1.0\Modules\<jea_folder_name>\jea_disco_v2.pssc
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\<jea_folder_name>\init.ps1
C:\Windows\System32\WindowsPowerShell\v1.0\Modules\<jea_folder_name>\RoleCapabilities\jea_disco_v2.psrc

Create the directory structure first:

New-Item -ItemType Directory -Path "C:\Program Files\WindowsPowerShell\Modules\JEA" -Force
New-Item -ItemType Directory -Path "C:\Program Files\WindowsPowerShell\Modules\JEA\RoleCapabilities" -Force

Then copy the file contents in from the KB. Don't forget to add your discovery user or AD group to the role configuration; that's what gives the account permission to actually connect to the JEA endpoint.

 

Heads up: Any time you change the .psrc or .pssc files, you need to restart WinRM for the changes to take effect: Restart-Service WinRM. I've wasted time more than once wondering why changes weren't working.

Step 2: Sort out the code signing certificate

This is where most setups either go smoothly or become a headache. You have two options.

Option A: Use a self signed certificate (easiest to start with)

Run this on the machine where your MID Server is installed:

New-Item -Path "C:\Certificates" -ItemType Directory -Force

$cert = New-SelfSignedCertificate `
    -CertStoreLocation Cert:\CurrentUser\My `
    -DnsName "jea-disco@servicenow.com" `
    -KeyAlgorithm RSA `
    -KeyLength 2048 `
    -Provider "<see attachment: STEP 2, required CSP name>" `
    -KeyExportPolicy Exportable `
    -KeyUsage DigitalSignature `
    -Type CodeSigningCert `
    -NotAfter (Get-Date).AddYears(5)

# Export the PFX (private + public) for the MID Server
$pwd = ConvertTo-SecureString "changeit" -AsPlainText -Force
Export-PfxCertificate -Cert $cert -FilePath "C:\Certificates\jea_disco.pfx" -Password $pwd

# Import into LocalMachine so the MID service account can access it
Import-PfxCertificate -FilePath "C:\Certificates\jea_disco.pfx" `
    -CertStoreLocation "Cert:\LocalMachine\My" -Password $pwd

# Export the public only .cer to deploy to target servers
Export-Certificate -Cert $cert -FilePath "C:\Certificates\jea_disco.cer"

(The -Provider value is omitted above; this community's filter blocks that exact string. Grab it from the attached JEA_Discovery_Exact_Commands.txt and drop it in before running.)

On each target server, import only the public cert. The private key never leaves the MID Server:

Import-Certificate -FilePath "C:\Certificates\jea_disco.cer" `
    -CertStoreLocation "Cert:\LocalMachine\Root"

Option B: Use a certificate from your PKI

If your org requires certs issued by your own PKI (which is most enterprise setups), you'll need to update the certificate lookup query in two places after deploying the cert.

On the ServiceNow instance: go to MID Server, then Script Files, then open JEAUtils.psm1, and find the retrieveSigningCert function. Update this line to match your cert's actual issuer and subject:

$mycert = Get-ChildItem Cert:\LocalMachine\My\ | Where {
    $_.Issuer  -eq 'CN=your-issuer-here' -and
    $_.Subject -eq 'CN=your-subject-here'
}

On each target server: open init.ps1 in the JEA folder and update the same query inside initJEASession.

Whatever certificate you use, it must meet these requirements. No exceptions:

  • Enhanced Key Usage must include Code Signing
  • The CSP must match the exact provider name in the attached JEA_Discovery_Exact_Commands.txt (look for "required CSP name"). This trips up a lot of PKI certs that use older templates
  • MID Server gets the full cert with private key (in LocalMachine\My)
  • Target servers get only the public key (.cer)
  • Both JEAUtils.psm1 and init.ps1 must be updated to find your cert

Step 3: Give the MID service account access to the private key

This is the step that gets forgotten most often. The MID Server service account needs explicit permission to use the private key. Just having the cert in the store isn't enough.

The full script for this step is in the attached JEA_Discovery_Exact_Commands.txt (under "STEP 3, private key permission script"). One line in it references a .NET namespace and a Windows folder name that this community's filter blocks, so it's not pasted inline here. At a high level, the script:

  1. Looks up your certificate and gets its RSA private key object
  2. Resolves that key's underlying file name on disk (under MachineKeys)
  3. Grants your MID service account Read access to that file via ACL
  4. Prints the resulting ACL so you can confirm it took effect

Where to find the service account name: open services.msc, find the ServiceNow MID Server service, and check the Log On tab.

Step 4: Sign the scripts and register the endpoint

Sign init.ps1 before deploying the profile:

Set-AuthenticodeSignature -FilePath "C:\Program Files\WindowsPowerShell\Modules\JEA\init.ps1" -Certificate $cert

Verify the signature. You want to see Status = Valid:

Get-AuthenticodeSignature -FilePath "C:\Program Files\WindowsPowerShell\Modules\JEA\init.ps1"

If the status isn't Valid, the cert probably isn't trusted yet. Import it into the Trusted Root store:

Import-Certificate -FilePath "C:\Certificates\jea_disco.cer" -CertStoreLocation "Cert:\CurrentUser\Root"

Now register the JEA endpoint on each target server:

Unregister-PSSessionConfiguration -Name JEA_DISCO_V2 -Force -ErrorAction SilentlyContinue
Register-PSSessionConfiguration -Name JEA_DISCO_V2 `
    -Path "C:\Program Files\WindowsPowerShell\Modules\JEA\jea_disco_v2.pssc"
Restart-Service WinRM

Quick sanity check: connect locally and confirm only JEAExecute-Script is visible:

Enter-PSSession -ComputerName localhost -ConfigurationName JEA_DISCO_V2
Get-Command

Step 5: WinRM, firewall, and permissions

Check the WinRM listener:

winrm enumerate winrm/config/listener
# Should show: Transport = HTTP, Port = 5985

Open the port if needed:

netsh advfirewall firewall add rule name="WinRM HTTP" dir=in action=allow protocol=TCP localport=5985
Restart-Service WinRM

Add the discovery user to Remote Management Users on each target:

net localgroup "Remote Management Users" <discovery_user> /add

Step 6: Set the MID Server parameters and test

In ServiceNow, go to the MID Server record and set:

  • mid.windows.management_protocol = WinRM
  • mid.powershell.jea.endpoint = JEA_DISCO_V2

Test from the MID Server machine:

$cred = Get-Credential
Enter-PSSession -ComputerName <target_IP> -Credential $cred -ConfigurationName JEA_DISCO_V2

If you land at [<target_IP>]: PS>, you're in good shape.

 

Using JEA Over HTTPS (WinRM with SSL)

If your security team wants encrypted transport, which is a reasonable ask, you can enforce HTTPS on the WinRM connection with these MID Server parameters:

Parameter What it does Default

mid.powershell_api.winrm.use_sslForces HTTPS for WinRM connectionsfalse
mid.powershell_api.winrm.remote_https_portHTTPS port to connect on5986
mid.powershell_api.winrm.skip_ssl_cert_checkSkips all SSL cert validation (use carefully)false
mid.powershell_api.winrm.skip_ssl_cert_check_optionsSkip specific checks: SkipCACheck, SkipCNCheck, SkipRevocationCheckall three

The target server needs a valid WinRM HTTPS certificate. Open the cert in MMC and check: valid dates, hostname matching the CN or a SAN, Enhanced Key Usage includes Server Authentication, and certification path shows OK.

One quirk to be aware of: Discovery connects to targets by IP address, not hostname. PowerShell's CN check validates the cert's CN against whatever you pass as the connection URI, and since that's an IP, it'll fail unless the cert's CN happens to be the IP (unlikely) or the IP is in the SANs.

There are three ways around this:

  1. Use the SkipCNCheck option in skip_ssl_cert_check_options. Simplest fix for trusted internal networks
  2. Remove the target IP from TrustedHosts on the MID Server. This forces a reverse DNS lookup, and the FQDN gets passed instead of the IP, which the cert CN is likely to match
  3. Issue a cert that includes the server's IP in the SAN field

Troubleshooting

When JEA isn't working, start with the WMI Classify probe result and the MID agent log. Set mid.log.level = debug first. A lot of useful information is hidden at the default log level.

To make debugging easier, add log statements to JEAUtils.psm1 and ExecuteRemote.psm1 with a consistent prefix so they're easy to grep:

SNCLog-DebugInfo "[JEA] In retrieveSigningCert - cert: $($mycert)"

Use $($variable) to interpolate variables into the string. These show up as DEBUG level entries in the MID agent log.

Scenario 1: Empty probe output, ScriptsNotAllowed error

You see this in the agent log:

The syntax is not supported by this runspace. This can occur if the runspace is in no language mode.
+ FullyQualifiedErrorId : ScriptsNotAllowed

What's happening: the MID is sending a plain unsigned command instead of routing it through JEAExecute-Script. The JEA session rejects it outright.

The root cause is that isJEACodeSigning in JEAUtils.psm1 returned false. Add this to that function to see why:

SNCLog-DebugInfo "[JEA] isJEACodeSigning - ConfigName: $($SNC_ConfigurationName) cert: $($global:SNC_jea_disco_cert)"
  • If SNC_ConfigurationName is blank, the mid.powershell.jea.endpoint parameter isn't set, or the MID needs a restart to pick it up
  • If SNC_jea_disco_cert is blank, the cert lookup in retrieveSigningCert failed. Run the query manually in PowerShell. Typos in the Issuer or Subject filter are the most common cause, followed by permission issues on the cert

Scenario 2: SignData throws "Invalid algorithm specified"

Agent log shows:

Exception calling "SignData" with "2" argument(s): "Invalid algorithm specified."
+ FullyQualifiedErrorId : <.NET exception name, see attached file, "SCENARIO 2, full agent log error">

The cert was found and the MID got to the signing step, but the signing itself failed. This almost always means the certificate was issued using a CSP that doesn't support this signing method. It's common with PKI certs from older Microsoft CA templates.

Validate it outside of ServiceNow:

$certObj = Get-ChildItem Cert:\LocalMachine\My | Where { $_.Issuer -eq 'CN=jea-disco@servicenow.com' }
$raw     = [System.Text.Encoding]::UTF8.GetBytes('testString')
$sign    = $certObj.PrivateKey.SignData($raw, 'SHA512')

If that fails, the cert needs to be replaced with one using the exact provider name from the attached JEA_Discovery_Exact_Commands.txt ("STEP 2, required CSP name"). You'll need to update or create a new Certificate Template in your CA.

Scenario 3: EXECUTION_DENIED, signature is missing

The probe output or log contains:

jea-disco certificate or private key not found
EXECUTION_DENIED; error=signature is missing

The script made it to the target via JEAExecute-Script, but it wasn't signed. The private key wasn't accessible when the MID tried to sign. Either the cert was imported without the private key, or the MID service account doesn't have permission to use it.

Check Scenario 2 first; that error can sometimes appear alongside this one. If Scenario 2 isn't present, run the private key permission script from Step 3 to fix the permissions. If the private key is genuinely missing from the cert, re import with the PFX.

Scenario 4: SSL, CN does not match the hostname

When using WinRM with SSL you may see:

New-PSSession : Connecting to remote server <IP> failed...
The SSL certificate contains a common name (CN) that does not match the hostname.
+ FullyQualifiedErrorId : 12175,PSSessionOpenFailed

Discovery connects by IP. The CN check compares the cert CN against the value passed to New-PSSession, which is the IP. Unless the cert was issued for that IP, it won't match. The three workarounds are covered in the SSL section above.

Scenario 5: AuthorizationManager check failed, probe times out

The credential test or WMI Classify probe just hangs, then times out. In the agent log:

New-PSSession : Running startup script threw an error: AuthorizationManager check failed.

Confusingly, if you manually run New-PSSession on the target host itself it works fine. The issue only shows up when connecting remotely.

The cause: init.ps1 was copied from another machine (for example via a network share) and Windows has blocked it at the file level. Right click the file, go to Properties, and if you see "This file came from another computer" at the bottom, click Unblock.

Or just do it in PowerShell:

Unblock-File -Path "C:\Windows\System32\WindowsPowerShell\v1.0\Modules\<jea_folder>\init.ps1"

Re register the endpoint and restart WinRM after unblocking.

 

A Few Final Things Worth Knowing

  • Re register the endpoint every time you change the .pssc file. The changes won't be picked up until you do
  • Restart WinRM every time you change .psrc or .pssc
  • When switching from a self signed cert to a PKI cert, update the lookup query in both JEAUtils.psm1 and init.ps1 before re registering. Missing either one and things will break in a confusing way
  • If you're troubleshooting and nothing is obvious, the combination of the WMI Classify probe output plus MID agent log at debug level will almost always tell you what's going wrong

Hope this saves someone a few hours. If you hit a scenario not covered here or have a different fix for one of the ones above, drop it in the comments. Happy to update the article.

References:  KB0965705

 

Thanks for reading if you have gained anything please do give a like 

0 REPLIES 0