Active Directory Inventory
Overview
Active Directory Inventory is a pull-based source that takes a periodic snapshot of the computer objects in an Active Directory domain and sends one record per computer into a Praxis pipeline: every Windows server, workstation and domain controller joined to the domain, plus non-Windows machines joined through Samba, Centrify and similar tools. These records are asset context: they describe what exists, not what happened. A SIEM uses them to enrich events with the machine's operating system, role and account status.
| Data | How it is read | Record attribute |
|---|---|---|
| Computer accounts: name, DNS host name, operating system and version, security identifier, GUID, group memberships, service principal names, last logon, account status | One paged LDAP search for (objectCategory=computer) against a domain controller | ad.inventory.resource = computer |
Each record is classified as domain_controller, server, workstation or unknown (ad.computer.category). Active Directory stores nothing about the hardware, so laptops and desktops are both workstation; tell them apart downstream from a naming convention, an OU, or an endpoint-management source.
The collector reads the directory over LDAP, so it can run anywhere that reaches a domain controller: a Linux host, a member server, or the domain controller itself. It does not need to be domain-joined unless you use Windows integrated authentication.
Like other sources, Active Directory Inventory does not set a Google SecOps log type. See Sending to Google SecOps.
Supported types: Logs
Supported platforms: Linux, Windows, macOS (sspi authentication: Windows only)
Minimum collector version: 0.5.74
Service account
Read-only. Any domain user can read computer objects with the default Active Directory permissions, so a plain, non-privileged account is enough. Do not use a Domain Admin. Recommended: a dedicated account with a long random password and Password never expires, denied interactive logon through Group Policy. Where the collector runs on Windows, a group Managed Service Account or the collector's own machine identity avoids storing a password at all (see Windows integrated authentication).
Authentication
Authentication picks how the collector proves the account to the domain controller. The credential attached to the node supplies the account.
| Authentication | Credential to attach | How it works | Runs on |
|---|---|---|---|
| Simple bind (default) | Basic Auth: username as DOMAIN\user, user@domain or a DN; password | The password itself is sent, so use an ldaps:// domain controller or StartTLS. Modern domain controllers refuse a simple bind over plain ldap://. | Anywhere |
| NTLM | Basic Auth: username as DOMAIN\user, or user with NetBIOS Domain set | NTLMv2 challenge/response; the password never crosses the wire. | Anywhere |
| Kerberos | Basic Auth (password), or No Auth with Keytab Path and Principal | A built-in Kerberos client gets a ticket from a KDC, then an LDAP service ticket for the domain controller. No system Kerberos libraries, kinit or domain join needed. | Anywhere |
| Windows integrated (SSPI) | No Auth (or Basic Auth for an explicit account) | Windows authenticates with the account the praxis-collector service runs as. Nothing secret in the configuration. | Windows only |
NTLM and Kerberos failures count against the account like a wrong password does, so the collector stops trying other domain controllers after any rejected bind rather than risk a lockout.
Kerberos
-
List the domain controllers by their registered FQDN, not by IP or alias: the service ticket is requested for
ldap/<host>. Service Principal Name (Advanced) overrides it. -
Realm is the AD DNS domain in upper case, e.g.
CORP.EXAMPLE.COM. It can be left empty when the credential username isuser@REALM. -
Every writable domain controller is a KDC, so KDCs is only needed when the KDCs differ from the domain controllers you read from. Kerberos uses TCP 88.
-
For an unattended service, a keytab is the usual choice. Create it on a domain controller:
ktpass /princ [email protected] /mapuser CORP\svc-praxis /crypto AES256-SHA1 /ptype KRB5_NT_PRINCIPAL /pass * /out svc-praxis.keytabCopy it to the collector host readable by the collector only, set Keytab Path and Principal (
[email protected]), and attach a No Auth credential. Rotating the account password invalidates the keytab. -
Clocks must be within 5 minutes of the domain controller.
Windows integrated authentication (SSPI)
Choose Windows integrated (SSPI) with a No Auth credential when the collector runs on a domain-joined Windows host or on a domain controller. The bind then uses the Windows account the collector service runs as:
| Service runs as | Identity used | Works? |
|---|---|---|
LocalSystem (the installer default) or NetworkService | The computer account (CORP\DC01$, CORP\WS01$), which reads the directory like any domain member | Yes |
| A domain account or a group Managed Service Account | That account | Yes, and this is the least-privilege choice |
| A local account | None: a local account has no domain identity | No. Use Kerberos, NTLM or Simple bind with a domain account |
On the domain controller itself, point Domain Controllers at the DC's own FQDN (ldaps://dc01.corp.example.com:636), not localhost: the TLS certificate and the Kerberos service principal are issued for the FQDN. No account, password or keytab is created or stored.
Signing and channel binding policies
| Domain controller policy | Simple bind | NTLM | Kerberos | SSPI |
|---|---|---|---|---|
| LDAP server signing requirements = Require signing | Needs TLS (ldaps:// or StartTLS) | Needs TLS | Needs TLS | Needs TLS |
| LDAP channel binding = Always (TLS only) | Unaffected | Rejected | Rejected | Accepted (the collector sends the channel binding token when using the service identity) |
With Microsoft's defaults (signing Negotiate, channel binding Never) every combination works. With the strictest hardening, use Simple bind over TLS from any host, or SSPI over TLS from a Windows host.
Basic configuration
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
endpoints | list | Yes | Domain Controllers as LDAP URLs, tried in order: ldaps://dc01.corp.example.com:636 or ldap://dc01.corp.example.com:389. The first one that accepts the connection serves the whole snapshot. | |
search_base | string | No | whole domain | DN to start from, e.g. OU=Servers,DC=corp,DC=example,DC=com. |
include_disabled | bool | No | true | Keep disabled computer accounts (marked accountEnabled: false). |
auth_mechanism | simple | ntlm | kerberos | sspi | No | simple | See Authentication. |
domain | string | No | NetBIOS domain for NTLM and SSPI when the credential username has no DOMAIN\ prefix. | |
kerberos.realm | string | Kerberos | from user@REALM | Kerberos realm. |
kerberos.kdcs | list | No | the domain controllers, port 88 | KDCs as host or host:port. |
kerberos.keytab | path | No | Keytab on the collector host, instead of a password (with a No Auth credential). | |
kerberos.principal | string | with keytab | Account in the keytab, user or user@REALM. | |
start_tls | bool | No | false | Upgrade ldap:// connections to TLS before binding. |
tls.ca_file | path | No | host trust store | Enterprise CA certificate (PEM) that issued the domain controller certificates. |
tls.insecure_skip_verify | bool | No | false | Accept any certificate. Lab use only. |
mode | scheduled | manual | No | scheduled | scheduled: snapshot every interval. manual: only with Fetch Inventory Now. |
interval | integer (seconds) | No | 21600 (6h) | Time between snapshots. Minimum 900. |
sync_request | integer | No | 0 | Fetch Inventory Now: manual mode only. |
Advanced
| Parameter | Type | Default | Description |
|---|---|---|---|
filter | string | Extra LDAP filter ANDed with the computer filter, e.g. (operatingSystem=*Server*). | |
attributes | list | LDAP attributes to read on top of the default set. | |
timeout | integer (seconds) | 60 | Limit for the connection and for each LDAP operation. |
page_size | integer | 1000 | Computers per LDAP page, each page one batch. AD caps it at its MaxPageSize policy (1000 by default). |
kerberos.spn | string | ldap/<host> | LDAP service principal override, for Kerberos and SSPI. |
kerberos.ccache | path | A kinit credential cache instead of a password or keytab. Not renewed; for tests. | |
kerberos.config_file | path | A krb5.conf to use instead of the built-in realm and KDC settings. |
Scheduled and manual fetching
Works the same way as the AWS Inventory and Okta sources.
| Fetch Mode | When snapshots run |
|---|---|
| scheduled (default) | At start-up, then every Snapshot Interval. The time of the last snapshot is saved on the collector, so a restart waits out the rest of the interval instead of fetching again. |
| manual | Only when you raise Fetch Inventory Now. Nothing runs on a timer. |
To fetch on demand: set Fetch Mode to manual, enter a number higher than the current one in Fetch Inventory Now, and deploy. The completed number is saved on the collector, so a restart does not fetch again; a failed snapshot stays pending and is retried.
Record format
Each computer becomes one log record whose body is the computer's LDAP attributes as JSON, keyed by attribute name as the domain controller returns it.
Default attributes: cn, name, sAMAccountName, dNSHostName, distinguishedName, objectGUID, objectSid, objectClass, operatingSystem, operatingSystemVersion, operatingSystemServicePack, description, location, managedBy, userAccountControl, primaryGroupID, lastLogonTimestamp, pwdLastSet, accountExpires, whenCreated, whenChanged, logonCount, servicePrincipalName, memberOf, msDS-SupportedEncryptionTypes, isCriticalSystemObject.
| Attribute kind | In the body |
|---|---|
objectGUID | GUID text, 01234567-89ab-cdef-0123-456789abcdef |
objectSid | SID text, S-1-5-21-...-1000 |
Timestamps (lastLogonTimestamp, pwdLastSet, accountExpires, whenCreated, whenChanged, ...) | RFC 3339 UTC; AD's "never" values are left out |
Integers (userAccountControl, logonCount, ...) | Numbers |
servicePrincipalName, memberOf, objectClass | Arrays |
| Anything else | A string, or an array when multi-valued |
Three derived fields are added: accountEnabled, deviceCategory (domain_controller, server, workstation, unknown) and collectionTime.
| Field | OTel destination |
|---|---|
| (computer JSON) | LogRecord.Body (string) |
| (snapshot start time) | LogRecord.Timestamp |
| (constant) | LogRecord.Attributes["ad.inventory.resource"] = "computer" |
| DNS host name, SAM account name, operating system, GUID | LogRecord.Attributes["ad.computer.dns_host_name"], ["ad.computer.sam_account_name"], ["ad.computer.operating_system"], ["ad.computer.object_guid"] |
| Category, enabled | LogRecord.Attributes["ad.computer.category"], ["ad.computer.enabled"] |
| Domain, forest, serving domain controller | Resource.Attributes["ad.domain"], ["ad.forest"], ["ad.domain_controller"] |
Sending to Google SecOps
Between the source and the Google SecOps destination, add a Google SecOps Standardization processor with the condition log.attributes["ad.inventory.resource"] == "computer" and the log type your tenant uses for Active Directory computer context (WINDOWS_AD is the common choice). Check the parser's expected field names against the body above.
Example configuration
{
"endpoints": ["ldaps://dc01.corp.example.com:636", "ldaps://dc02.corp.example.com:636"],
"search_base": "", // default: the whole domain
"include_disabled": true, // default: true
"auth_mechanism": "kerberos", // default: simple
"kerberos": { "realm": "CORP.EXAMPLE.COM" },
"mode": "scheduled", // default: scheduled (or "manual")
"interval": 21600, // default: 21600 (6h), minimum 900
"sync_request": 0 // manual mode: raise and deploy to fetch now
}
With a Basic Auth credential holding svc-praxis / its password.
Operational notes
- Snapshots, not streaming. Every snapshot re-sends the whole inventory, one batch per LDAP page.
- Failures. A snapshot is a single search, so any failure (no domain controller reachable, bind rejected, a page failing) aborts it and it is retried after 5 minutes and then with backoff up to 1 hour. Pages already sent are not withdrawn.
- Scale. A 50,000-computer domain is 50 pages of 1,000 at roughly 2 KB per record; the collector holds one page at a time.
- Multiple domains. One source reads one domain. Add a source per domain. A global catalog (
ldaps://dc:3269) returns a partial attribute set for the whole forest.
Metrics
| Metric Name | Description |
|---|---|
prx_src_records_received_total{signal="logs"} | Log records emitted, one per computer. |
prx_src_bytes_received_total | Bytes of the emitted record bodies. |
prx_src_pipeline_latency_seconds | Arrival → downstream handoff. |
prx_src_errors_total{reason} | Connection, bind or search failures by reason: auth (bind rejected, signing or channel binding required, Kerberos pre-authentication failed), permission_denied, timeout, io. |
prx_src_records_dropped_total{reason="backpressure"} | Records dropped because the downstream pipeline refused them. |