Running on Windows with a gMSA

This page describes how to run the Curiosity Workspace Windows service and the CLI connector (curiosity-cli monitor / monitor-with-permissions) under a Group Managed Service Account (gMSA), and what Active Directory and host-side setup is required.

A gMSA is a domain account whose password is generated and rotated by Active Directory itself (every 30 days by default). Services log on with the account name only — there is no password to type into a service definition, store in a script, or rotate by hand. This matters for two Curiosity scenarios:

  • The Workspace service (curiosity, installed by configure-windows-service.bat) runs as LocalSystem by default. A gMSA lets it run under a least-privilege domain identity instead — required when the workspace itself must reach domain resources such as an SMB path for MSK_GRAPH_STORAGE or a PEM certificate on a share.
  • The CLI connector syncing a file share needs a domain identity to read the share, and — for the -with-permissions variants — to resolve the NTFS ACL SIDs against Active Directory. Running the connector service as a gMSA replaces the --username / --password / --domain impersonation flags, so no domain credential appears in the service configuration at all.

Requirements

| Requirement | Notes | | --- | --- | | Active Directory domain | Domain functional level Windows Server 2012 or later. | | KDS root key | Created once per forest; gMSA passwords are derived from it. | | Domain-joined host | The machine running the Workspace and/or CLI connector must be joined to the domain. | | Windows Server 2012+ on the host | Client SKUs also support gMSAs, but a server SKU is typical. | | RSAT Active Directory PowerShell module | Needed on the host to install and test the account. | | AD permissions | Creating the gMSA requires rights to create msDS-GroupManagedServiceAccount objects (typically Domain Admins or a delegated OU admin). |

Create the gMSA in Active Directory

Run these on a domain controller (or any machine with the AD PowerShell module and sufficient rights).

If your forest has never used gMSAs, create the KDS root key first:

Get-KdsRootKey                      # anything listed? then skip the next line
Add-KdsRootKey -EffectiveImmediately
KDS root key propagation

-EffectiveImmediately still means a 10-hour wait before domain controllers will issue gMSA passwords, to allow AD replication. In a single-DC lab you can bypass the wait with Add-KdsRootKey -EffectiveTime ((Get-Date).AddHours(-10)) — do not do this in production.

Create a security group for the hosts allowed to retrieve the account's password, add the machine account(s), then create the gMSA:

New-ADGroup -Name "CuriosityHosts" -GroupScope Global -GroupCategory Security
Add-ADGroupMember -Identity "CuriosityHosts" -Members "WORKSPACE01$"   # the host's computer account

New-ADServiceAccount -Name "svc-curiosity" `
  -DNSHostName "svc-curiosity.corp.example.com" `
  -PrincipalsAllowedToRetrieveManagedPassword "CuriosityHosts"

Reboot the host after adding its computer account to the group — group membership is only picked up on the machine's next Kerberos ticket.

You can also point -PrincipalsAllowedToRetrieveManagedPassword at a single computer account ("WORKSPACE01$") instead of a group; the group form scales to several connector hosts sharing one account.

Prepare the host

On the machine that will run the service(s):

# RSAT AD PowerShell module (server SKU)
Install-WindowsFeature RSAT-AD-PowerShell

# Link the account to this host and verify the password can be retrieved
Install-ADServiceAccount -Identity "svc-curiosity"
Test-ADServiceAccount    -Identity "svc-curiosity"    # must return True

If Test-ADServiceAccount returns False, the host cannot retrieve the managed password — see Troubleshooting.

Grant the account the Log on as a service right: secpol.mscLocal Policies → User Rights Assignment → Log on as a service → add CORP\svc-curiosity$. (Configuring the account through the services.msc UI grants this automatically; sc.exe does not on all Windows versions, so set it explicitly.) In domains where this right is controlled by Group Policy, add the account to the corresponding GPO instead — a local grant is overwritten on the next policy refresh.

The trailing `$`

A gMSA is referenced everywhere as DOMAIN\name$ — with a trailing dollar sign and an empty password. Forgetting the $ is the most common configuration mistake.

Run the Workspace service as the gMSA

Install the Workspace as a Windows service first (see Windows Installation), then change the service identity:

sc.exe stop  curiosity
sc.exe config curiosity obj= "CORP\svc-curiosity$" password= ""

Before starting it again, make sure the account can read and write everything the service touches:

| Path | Access | Notes | | --- | --- | --- | | Install directory (binaries) | Read + execute | The installer defaults to %USERPROFILE%\AppData\Local\Curiosity Workspace\ of the installing user — grant the gMSA read access there, or install to a machine-wide path such as C:\Curiosity\app. | | MSK_GRAPH_STORAGE data directory | Full control | Move it out of a user profile (e.g. D:\Curiosity\data) if it isn't already. | | Log directory (MSK_LOG_PATH) | Modify | | | TLS certificate files (MSK_CERT_FILE, MSK_CERT_FILE_PRIVATE_KEY) | Read | Only if TLS is terminated in the workspace itself. |

icacls "C:\Curiosity\app"  /grant "CORP\svc-curiosity$:(OI)(CI)RX"
icacls "D:\Curiosity\data" /grant "CORP\svc-curiosity$:(OI)(CI)F"

Machine-scope MSK_* environment variables (see Windows Installation) apply to services regardless of the account they run as, so no configuration changes are needed there. Then:

sc.exe start curiosity
sc.exe query curiosity

Run the CLI connector as the gMSA

The typical connector is monitor-with-permissions wrapped in a Windows service (via NSSM or sc.exe), continuously syncing a file share and its Active Directory ACLs into the workspace.

1. Install the CLI machine-wide

dotnet tool install --global installs into the invoking user's profile (%USERPROFILE%\.dotnet\tools), which the gMSA cannot see. Install to a fixed path instead:

dotnet tool install Curiosity.CLI --tool-path C:\Curiosity\cli

The binary is then C:\Curiosity\cli\curiosity-cli.exe for any account on the machine.

2. Grant the gMSA access

| Resource | Access | Notes | | --- | --- | --- | | The monitored share (e.g. \\fileserver\Shared) | Read | Both the share permission and the NTFS permission must allow the gMSA (effective access is the more restrictive of the two). Grant read on the whole tree you want indexed. | | --permissions-cache file location (e.g. C:\Curiosity\acl-cache.json) | Modify | Keep the path stable across runs. | | Active Directory read | Default | Resolving ACL SIDs to users/groups only needs standard authenticated-user read access to the directory — no extra grants in a default AD. |

Because the service itself runs as the domain identity, the CLI's Windows-only --username / --password / --domain impersonation options are not needed — leave them out.

3. Create the service

With NSSM:

nssm install CuriosityMonitor "C:\Curiosity\cli\curiosity-cli.exe"
nssm set CuriosityMonitor AppParameters monitor-with-permissions ^
   --server https://my-workspace.example.com/ ^
   --token  %CURIOSITY_TOKEN% ^
   --path   \\fileserver\Shared ^
   --permissions-cache C:\Curiosity\acl-cache.json ^
   --fetch-server-state true
nssm set CuriosityMonitor AppEnvironmentExtra CURIOSITY_TOKEN=<library-token>
nssm set CuriosityMonitor ObjectName CORP\svc-curiosity$ ""
nssm start CuriosityMonitor

ObjectName CORP\svc-curiosity$ "" is the gMSA equivalent of an account + password pair — the empty string is the password. With plain sc.exe the same is sc.exe config CuriosityMonitor obj= "CORP\svc-curiosity$" password= "".

Token handling under a gMSA

The service needs a workspace Library Token (create one under Manage → Tokens → Library, ideally for a dedicated service user with minimal rights). Two ways to provide it:

  • Environment variable in the service definition (shown above with AppEnvironmentExtra) — simplest; the token lives only in the service configuration, readable by administrators.

  • store-token + --token auto — the encrypted token file is written to the running user's %APPDATA%, so it must be created as the gMSA. A gMSA cannot log on interactively; run the one-off command through a scheduled task instead:

    $a = New-ScheduledTaskAction -Execute "C:\Curiosity\cli\curiosity-cli.exe" `
          -Argument "store-token -s https://my-workspace.example.com/ -t <library-token>"
    $p = New-ScheduledTaskPrincipal -UserId "CORP\svc-curiosity$" -LogonType Password
    Register-ScheduledTask -TaskName "curiosity-store-token" -Action $a -Principal $p
    Start-ScheduledTask    -TaskName "curiosity-store-token"
    Unregister-ScheduledTask -TaskName "curiosity-store-token" -Confirm:$false
    

    Afterwards the service can use --token auto and no token appears in the service definition. (-LogonType Password is correct for gMSAs — Windows fetches the managed password itself.)

Troubleshooting

| Symptom | Likely cause | | --- | --- | | Test-ADServiceAccount returns False | The host's computer account is not in the PrincipalsAllowedToRetrieveManagedPassword group, the host was not rebooted after being added, or the KDS root key is not yet effective (10-hour wait). | | Service fails to start with error 1069 (logon failure) | Missing trailing $ in the account name, a non-empty password was configured, or the account lacks the Log on as a service right. | | Access is denied reading the share | Check both the share permission and the NTFS ACL for the gMSA; also confirm the share path is a UNC path, not a drive letter mapped for another user (drive mappings are per-logon and invisible to the service). | | Can't find token for url with --token auto | The token was stored under a different account's %APPDATA% — re-run store-token as the gMSA (see above) or pass the token explicitly. | | ACL sync produces no permissions | Confirm you run the Windows build of the CLI and the -with-permissions command variant; the non-Windows builds do not support permission sync. |

See also

© 2026 Curiosity. All rights reserved.
Powered by Neko