Dex
Local Active Directory · Connector

Configure the Local Active Directory connector

45–60 min5 phases · 14 steps
You'll useDexWindows
Before you begin
  • One domain-joined Windows host to run the connector — a member server is recommended over a Domain Controller.
  • Domain Admin (or equivalently delegated) rights to create a gMSA and delegate OU permissions.
  • The connector host can reach a Domain Controller on TCP 9389 (AD Web Services), plus Kerberos and LDAP.
  • Outbound HTTPS (TCP 443) from the host to https://dex365.ai, and the tenant's enrollment token from the Dex console.
Phase 1
Prepare host & Dex
Pick the connector host, install the AD PowerShell tools, and get the tenant enrollment token. The connector is the same MSI as the regular Dex agent — an install-time profile makes it an AD connector.

Choose & prepare the connector host

~10 min
In Windows

Install RSAT, picking the command for the host OS. The connector host must also reach a DC on TCP 9389 (AD Web Services).

Where will the connector run?
Windows Server
Install-WindowsFeature RSAT-AD-PowerShell
Windows 10 / 11 client
Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0
Picking the right RSAT command
If Get-WindowsCapability -Online -Name Rsat.ActiveDirectory* returns nothing at all (not even a NotPresent entry), the host is almost certainly Windows Server — Add-WindowsCapability silently does nothing there, so use Install-WindowsFeature RSAT-AD-PowerShell instead.
Add-WindowsCapability fails with 0x800f0954
On Windows 10/11 the Features-on-Demand payload comes from Windows Update; this error means the host can't reach it (common behind WSUS). Set the Group Policy "Specify settings for optional component installation and component repair" → "Download repair content… directly from Windows Update".

Verify the module and ADWS connectivity

~2 min
In Windows

On the connector host, confirm the ActiveDirectory module is available and AD Web Services is reachable.

Verify (elevated PowerShell)
Get-Module -ListAvailable ActiveDirectory   # module is listed
Get-ADDomain                                # confirms ADWS connectivity
Module listed but cmdlets "not recognized"
If the module shows under Get-Module -ListAvailable but commands like Install-ADServiceAccount fail with "not recognized as the name of a cmdlet," it just isn't loaded in the current session — open a new PowerShell window or run Import-Module ActiveDirectory.
Get-ADDomain fails / ADWS unreachable
The host can't reach AD Web Services. Open TCP 9389 to a Domain Controller and confirm the Active Directory Web Services service is running on the DC.

Generate an enrollment token in Dex

~2 min
Now in Dex

The connector enrolls with a tenant enrollment token, the same as any Dex Device Agent. Sign in to the Dex console, go to Settings → Devices, and under Enrollment tokens generate (or reveal) a token. You'll put this value in the connector config as enrollment_token.

Enable the Local Active Directory app last — not now
Don't turn on the Local Active Directory app yet. Enable it only after the connector is installed, enrolled, and reporting adws_reachable: true (final phase). Enabling it earlier just leaves it with no connector to route commands to.
Treat the token as a secret
After enrollment the agent exchanges it for a per-device secret and never logs either. You can revoke a token any time from the same list.
Phase 2
Create the gMSA
The gMSA is the identity the connector authenticates to AD as — no stored password; AD rotates it and the host fetches it on demand.

Create the KDS root key (one-time per forest)

~5 min
Now in Windows

gMSAs require a Key Distribution Service (KDS) root key. Skip this if a usable key already exists — check first. A new key isn't usable until 10 hours after its effective time (to allow replication across DCs); -EffectiveImmediately does not bypass this wait.

Check for an existing key
Get-KdsRootKey | Select KeyId, EffectiveTime
Production (multi-DC) — then wait ~10 hours
Add-KdsRootKey -EffectiveImmediately
Single-DC lab only — backdate so it's usable immediately
Add-KdsRootKey -EffectiveTime ((Get-Date).AddHours(-10))
"Bad Key" when creating the gMSA
If New-ADServiceAccount fails with "Bad Key" (-2146893821 / NTE_BAD_KEY), the cause is almost always the KDS root key — either none exists or one exists but its EffectiveTime is within the last 10 hours and isn't yet usable. Check with Get-KdsRootKey | Select KeyId, EffectiveTime.
Backdating is safe on a single DC
There is no replication delay on a single DC, so a backdated key is usable at once. Adding a second, backdated key is harmless — AD uses whichever key is valid. If creation still fails immediately after, run Restart-Service KdsSvc and retry.

Create the gMSA and authorize the host

~5 min
In Windows

Run on a Domain Controller (or any host with AD admin rights). -PrincipalsAllowedToRetrieveManagedPassword must be the computer account of the host that will run the connector — its machine name with a trailing $. This is not a literal placeholder; substitute your real host name.

Create dexgmsa (replace the placeholders)
# On the connector host, get its name:
$env:COMPUTERNAME

# Confirm the computer object exists in AD (use the name from above):
Get-ADComputer <CONNECTOR_HOSTNAME>

# Create the gMSA, authorizing that host (trailing $ is required):
New-ADServiceAccount -Name dexgmsa `
  -DNSHostName dexgmsa.<corp.example.com> `
  -PrincipalsAllowedToRetrieveManagedPassword "<CONNECTOR_HOSTNAME>$"
"could not be resolved" / "Cannot find an object with identity"
The name you passed to -PrincipalsAllowedToRetrieveManagedPassword doesn't match a real computer account. Don't pass the literal word CONNECTORHOST — use the actual machine name with a trailing $. If the connector runs on the DC itself, use the DC's computer account, e.g. DC01$.
The trailing $ is required
It denotes the computer account. If PowerShell misbehaves around the $ inside double quotes, use single quotes instead: '<CONNECTOR_HOSTNAME>$'.

Install and verify the gMSA on the host

~3 min
In Windows

On the connector host (elevated PowerShell), install the gMSA and confirm it. Install-ADServiceAccount copies no password — it verifies the host is authorized and lets the LSA fetch the rotating password from AD on demand. Test-ADServiceAccount must return True before continuing.

Install + verify (on the connector host)
Install-ADServiceAccount -Identity dexgmsa
Test-ADServiceAccount    -Identity dexgmsa   # must return True
Get-ADServiceAccount     dexgmsa             # confirms the account exists
The account name you give the installer
It is <DOMAIN>\dexgmsa$ — NetBIOS domain, then the account with a trailing $. You'll reuse this exact value in the config and the MSI.
Test-ADServiceAccount returns False
The host isn't authorized to retrieve the password, or the gMSA wasn't created with this host in -PrincipalsAllowedToRetrieveManagedPassword. Recheck the previous step and confirm the computer account name (with $) is correct.
Phase 3
Delegate rights
Grant only the rights the connector needs, only on the OUs it manages. This delegation is the primary security boundary; allowed_ous is the second layer.

Identify the OU(s) to manage

~3 min
In Windows

Pick (or create) the OU(s) the connector will manage and note each Distinguished Name — these go into the connector config's allowed_ous later, and you delegate rights on exactly these OUs.

Example — create an OU and note its DN
New-ADOrganizationalUnit -Name "Staff" -Path "DC=corp,DC=example,DC=com"
# DN: OU=Staff,DC=corp,DC=example,DC=com
Objects in the default CN=Users container are not covered
Delegation applies to the OUs you target. Users sitting in CN=Users (not an OU) won't be manageable — move them into a delegated OU, or delegate the container explicitly.

Delegate least-privilege rights with dsacls

~10 min
In Windows

Run in an elevated PowerShell on a DC (or host with AD admin rights). Do not add the gMSA to Domain Admins. Set $ou to your target OU's DN and $gmsa to your <DOMAIN>\dexgmsa$, then run the complete grant block — it covers every supported connector action. Repeat for each OU by changing $ou and re-running.

12 permissionsLocal Active Directory · dsacls grants
Read (queries)
GR (generic read)ad_user_exists, ad_get_user, ad_find_users_in_ou, ad_get_group_members, ad_list_user_groups, ad_find_groups.
User lifecycle
CC;user + WP;;user + CA;Reset Passwordad_create_user (object + initial property writes + initial password).
DC;userad_delete_user.
WP;;user (userAccountControl)ad_enable_user, ad_disable_user.
WP;;user (lockoutTime)ad_unlock_user.
CA;Reset Passwordad_reset_password.
WP;;userad_update_user (name, email, title, department, manager, …).
WP;;user (accountExpires)ad_set_account_expiration.
Groups & OUs
RPWP;member;groupad_add_group_member, ad_remove_group_member.
CCDC;groupad_create_group, ad_delete_group.
CCDC;organizationalUnitad_create_ou, ad_delete_ou.
CC/DC on user/group (see note)ad_move_object — within the delegated subtree. A move to an OU outside your delegated OUs needs CC of that object class on the destination too; a move that also renames needs WP;name;user / WP;name;group.
These grants are the complete, explicit set covering every connector action. ACL changes take effect on the gMSA's next operation — no service restart needed.
Delegate all connector rights on one OU
$ou   = "OU=Staff,DC=corp,DC=example,DC=com"
$gmsa = "<DOMAIN>\dexgmsa$"

# READ — user_exists, get_user, find_users_in_ou,
#        get_group_members, list_user_groups, find_groups
dsacls $ou /I:T /G "${gmsa}:GR"

# USER create + delete — create_user, delete_user
dsacls $ou /I:T /G "${gmsa}:CCDC;user"

# USER property writes — update_user, enable_user, disable_user,
#   unlock_user, set_account_expiration, force-change-at-logon
#   (covers userAccountControl, lockoutTime, accountExpires, pwdLastSet, etc.)
dsacls $ou /I:S /G "${gmsa}:WP;;user"

# Reset password — reset_password (+ initial password on create_user)
dsacls $ou /I:S /G "${gmsa}:CA;Reset Password;user"

# GROUP create + delete — create_group, delete_group
dsacls $ou /I:T /G "${gmsa}:CCDC;group"

# GROUP membership — add_group_member, remove_group_member
dsacls $ou /I:S /G "${gmsa}:RPWP;member;group"

# OU create + delete — create_ou, delete_ou
dsacls $ou /I:T /G "${gmsa}:CCDC;organizationalUnit"
Verify the ACEs landed
dsacls "OU=Staff,DC=corp,DC=example,DC=com" | Select-String dexgmsa
Do not rely on Generic All (GA) for enable/disable
A blanket GA;;user scoped with an object-type filter does not reliably expand to the property writes needed for enable/disable (which write userAccountControl). If you see "Insufficient access rights," use the explicit grants above (WP;;user plus CA;Reset Password) rather than GA.
Flag reference
/I:T = this object and the whole subtree (needed for create/delete to apply to child objects); /I:S = child objects of the specified type; CC/DC = create/delete child; WP/RP = write/read property; CA = control-access (extended) right; GR = generic read. The ${gmsa} braces are required so PowerShell doesn't read a variable named gmsa:.
The Delegate Control wizard can't select a gMSA on many builds
Its object picker is scoped to Users, Groups, or Built-in security principals; a gMSA is an msDS-GroupManagedServiceAccount and won't resolve. The scripted dsacls method above is more reliable and repeatable.
"Insufficient access rights" when the rights look correct
Check two things before adding more permissions: (1) Is the target object actually inside a delegated OU? Run Get-ADUser <name> -Properties CanonicalName. (2) Is adminCount = 1 on the object? Members (current or former) of protected groups have their ACLs reset by AdminSDHolder, which strips delegated rights until that protection is cleared.
Trim WP;;user for production
WP;;user grants write to all user properties — broad. For production, consider trimming to just the attributes the connector writes (userAccountControl, lockoutTime, accountExpires, pwdLastSet, plus profile fields like displayName, mail, department) and dropping the blanket WP;;user.
Phase 4
Configure & install
Write the connector config, then install the MSI with the AD profile so the service runs as the gMSA.

Write the connector config

~3 min
In Windows

Create C:\ProgramData\DexDeviceAgent\config.yaml (the installer creates the directory). Both enabled: true and the ad-connector profile are required before the agent will accept AD commands. List your real OU DNs in allowed_ous, matching exactly what you delegated.

C:\ProgramData\DexDeviceAgent\config.yaml
cloud_service:
  base_url: "https://dex365.ai"
  enrollment_token: "<TENANT_ENROLLMENT_TOKEN>"
connector:
  profile: "ad-connector"
  service_account: "<DOMAIN>\\dexgmsa$"   # informational
  ad:
    enabled: true                           # master switch — required
    allowed_ous:                            # empty = deny ALL writes (safe default)
      - "OU=Staff,DC=corp,DC=example,DC=com"
    allow_raw_powershell: false             # leave OFF unless you truly need it
Empty allowed_ous denies all writes — a safe default
With no OUs listed, all mutations are denied. Add only the OU DNs you delegated rights on in the previous phase; keep the two lists in sync.
Keep exactly one config.yaml
The agent reads config.yaml only. Stray copies (e.g. "config - Copy.yaml") are ignored but cause confusion — remove them.

Install the agent with the AD profile

~5 min
In Windows

The connector is the same MSI as the regular Dex agent — the AD profile is selected by an install-time property. Pre-flight the gMSA first, then install. With no PROFILE property the MSI installs the ordinary endpoint agent unchanged.

Pre-flight the gMSA (on the connector host)
DexDeviceAgent.exe -check-gmsa -service-account "<DOMAIN>\dexgmsa$"
Install (one MSI; AD profile selected by property)
msiexec /i DexDeviceAgent.msi /qn `
  PROFILE=ad-connector AD_SERVICE_ACCOUNT="<DOMAIN>\dexgmsa$"
Intune / Win32 packaging
Install-DexAgent.ps1 -Token <TENANT_ENROLLMENT_TOKEN> `
  -Profile ad-connector -ServiceAccount "<DOMAIN>\dexgmsa$"
A wrong AD_SERVICE_ACCOUNT leaves the service misconfigured
A value pointing at an account that doesn't exist in your domain will leave the service unable to authenticate to AD. Use the exact <DOMAIN>\dexgmsa$ that Test-ADServiceAccount returned True for.

Service identity & upgrades

as needed
In Windows

Re-running the installer (e.g. to upgrade) re-registers the Windows service and resets its logon identity back to LocalSystem — silently undoing the gMSA assignment. LocalSystem has no rights in AD, so every AD operation then fails with ACCESS_DENIED even though your OU delegation is perfectly intact. This looks exactly like a permissions problem but is not. Make re-pointing the service a standard post-upgrade check. The PowerShell-native method handles the empty gMSA password correctly; the sc.exe alternative needs single quotes around the account and no password argument. If the custom action never ran (e.g. the MSI was interrupted), also re-grant "Log on as a service" via secpol.msc → Local Policies → User Rights Assignment (type the gMSA name in directly — the picker won't resolve it cleanly) and ensure the profile policy key exists.

PowerShell-native re-bind (recommended)
Stop-Service DexDeviceAgent
$svc = Get-CimInstance Win32_Service -Filter "Name='DexDeviceAgent'"
$svc | Invoke-CimMethod -MethodName Change -Arguments @{
  StartName     = '<DOMAIN>\dexgmsa$'
  StartPassword = $null
}   # ReturnValue 0 = success
Start-Service DexDeviceAgent
Alternative — sc.exe (note the spacing: space after each =, none before)
sc.exe config DexDeviceAgent obj= '<DOMAIN>\dexgmsa$'
Restart-Service DexDeviceAgent
sc.exe qc DexDeviceAgent   # SERVICE_START_NAME must read <DOMAIN>\dexgmsa$
Confirm the profile policy key
Get-ItemProperty "HKLM:\SOFTWARE\Policies\DexDeviceAgent"
# If Profile is missing or not "ad-connector", the agent is idle and rejects AD commands.
Profile key missing or not ad-connector
If HKLM\SOFTWARE\Policies\DexDeviceAgent has no Profile value (or it isn't ad-connector), the agent runs but stays idle and rejects AD commands. Set it, then Start-Service DexDeviceAgent.
Never use $args as a variable name
It's an automatic PowerShell variable. The $ in the account name also breaks sc.exe parsing under PowerShell — single-quote the account.
Phase 5
Verify & enable
Confirm the service identity and ADWS reachability, then approve the device and enable the Local Active Directory app in Dex.

Verify the connector

~3 min
In Windows

Confirm the service runs as the gMSA, then from the Dex console send AD_CAPABILITIES to the device — expect module_available: true, the correct deployment_mode, and adws_reachable: true. Logs are at C:\ProgramData\DexDeviceAgent\logs\DexDeviceAgent.log (passwords are redacted).

Confirm the service identity
sc.exe qc DexDeviceAgent      # SERVICE_START_NAME should be <DOMAIN>\dexgmsa$
Get-Service DexDeviceAgent    # Status: Running
Inspect recent activity
Get-Content C:\ProgramData\DexDeviceAgent\logs\DexDeviceAgent.log -Tail 60
Service won't start (logon failure 1069)
The gMSA lacks "Log on as a service." Re-run install, or grant it via secpol.msc, and confirm Test-ADServiceAccount returns True.
AD commands rejected entirely
profile or enabled isn't set — check config.yaml and the policy key Profile value before going further.

Approve the device and enable the Local Active Directory app

~3 min
Now in Dex

Once AD_CAPABILITIES reports adws_reachable: true, approve the device as an AD operator in the Dex console, then enable the Local Active Directory app so Dex routes AD commands to it. Until the app is enabled, Dex has the connector but no tools, skills, or actions to act with.

Screenshot needed
Screenshot not provided yet
Drop the Local Active Directory app-card screenshot at public/guides/dex-app-local-ad.png.
Commands auto-route to the authorized connector
Once enabled, AD commands automatically route to the org's approved AD connector — you don't target the device by hand.
Tune autonomy per action
Configure app exposes each action's approval policy. Destructive actions (delete user/group/OU) and the raw-PowerShell escape hatch are admin-only and require approval; leave allow_raw_powershell off unless you truly need it.

Troubleshooting

reference
Now in Windows

Common symptoms, their cause, and the fix. Most "permissions" failures after an upgrade are actually the service reverting to LocalSystem — check the service identity first.

Logs
C:\ProgramData\DexDeviceAgent\logs\DexDeviceAgent.log holds connector activity (passwords redacted). If logs are missing, the path is wrong or the agent is idle (no profile/config).

Local Active Directory is live

The connector runs as a least-privilege gMSA, the device is approved as an AD operator, and the Local Active Directory app is enabled — Dex can now create, disable, and manage users, groups, and OUs in your on-prem AD, routed automatically to this connector. For production, run it on a member server (not a DC), sign the agent build, and trim user-property delegation to the specific attributes in use.