Skip to content

Add dmc-prepop AD module (9 queries via ldap3)#19

Draft
adamswbrown wants to merge 2 commits into
claude/dmc-prepopfrom
claude/dmc-prepop-ad
Draft

Add dmc-prepop AD module (9 queries via ldap3)#19
adamswbrown wants to merge 2 commits into
claude/dmc-prepopfrom
claude/dmc-prepop-ad

Conversation

@adamswbrown
Copy link
Copy Markdown
Owner

Summary

  • Adds the AD module to dmc-prepop, alongside the existing SCCM module.
  • All 9 queries from the AD pre-population spec, implemented in Python via ldap3.
  • New dmc-prepop ad subcommand. Same conventions as sccm: streaming CSV with UTF-8 BOM, hostname normalisation, JSON coverage summary, --timestamp-output for snapshots.

Stacked on #16. Base branch is claude/dmc-prepop (the SCCM PR). After #16 merges, retarget this PR to main — diff will then be just the AD module.

Architecture

  • dmc_prepop/ad/connect.py — LDAP session helper. Default LDAPS to a DC; supports simple bind (--user/--password) and Kerberos SASL (--auth kerberos). Auto-detects base DN from rootDSE. --gc switches to Global Catalog (3268/3269) for forest-wide queries.
  • dmc_prepop/ad/spn.py — SPN parser (<class>/<host>[:port][/svcname]) plus a WORKLOAD_BY_CLASS lookup table mirroring the spec's SPN reference (MSSQLSvc → SQL Server, HTTP → Web, exchangeMDB/exchangeRFR/SMTPSVC → Exchange, TERMSRV → RDS, etc.). The host SPN is tagged ignore per spec.
  • dmc_prepop/ad/extract.py — All 9 queries as small functions, plus an orchestrator. Helpers for Windows-filetime conversion (lastLogonTimestamp → datetime), DN parsing (OU=… extraction), and userAccountControl bit decoding (DISABLED bit 2, SERVER_TRUST_ACCOUNT bit 8192 for DC detection).
  • dmc_prepop/coverage.py — Extended to include an ad section in coverage_summary.json and to write the spec-required flat ad_coverage_summary.csv (Query 9) by reading the other AD CSVs.
  • dmc_prepop/cli.py — Adds the ad subcommand. Refactors the shared _resolve_out helper out of sccm.

Outputs (10 files)

File Spec query Notes
ad_servers.csv 1 Spine. Hostname normalised, OS, last-logon age, OU path, ManagedBy CN.
ad_spns_computer.csv 2 SPNs on computer objects + workload label.
ad_spns_service_accounts.csv 3 SPNs on user service accounts — SQL discovery angle.
ad_spns_gmsa.csv 3 (variant) SPNs on gMSA / sMSA accounts.
ad_sites.csv 4 Read from CN=Sites,CN=Configuration,….
ad_subnets.csv 5 Site reference resolved to CN.
ad_ou_distribution.csv 6 Top-3-OU-level grouping.
ad_stale_servers.csv 7 90-day cutoff via filetime; sorted by staleness desc.
ad_domain_controllers.csv 8 Identified via userAccountControl & 8192.
ad_coverage_summary.csv 9 Flat single-row summary.

LDAP filter translations (PowerShell → ldap3)

The spec is PowerShell + RSAT; this module re-implements equivalent filters as raw LDAP. Notable translations:

  • Enabled -eq 'True'(!(userAccountControl:1.2.840.113556.1.4.803:=2)) (LDAP_MATCHING_RULE_BIT_AND, ACCOUNTDISABLE flag)
  • Get-ADDomainController(userAccountControl:1.2.840.113556.1.4.803:=8192) (SERVER_TRUST_ACCOUNT bit)
  • Get-ADReplicationSite → search CN=Sites,CN=Configuration,<base> for (objectClass=site)
  • Get-ADReplicationSubnet → search CN=Subnets,CN=Sites,… for (objectClass=subnet)
  • Get-ADServiceAccount(|(objectClass=msDS-GroupManagedServiceAccount)(objectClass=msDS-ManagedServiceAccount)) — covers both gMSA and legacy sMSA

lastLogonTimestamp filtering uses Windows file-time (100ns ticks since 1601-01-01 UTC); converted via _filetime_cutoff().

Spec adherence notes

  • LastLogonTimestamp replication caveat documented in README; 9–14 day lag is acceptable for migration assessment.
  • LAPS passwords intentionally not extracted per spec caveat 6.
  • Tombstoned objects out of scope per spec caveat 10.
  • Disabled servers included in stale-server output per spec caveat 9 (often soft-decoms worth surfacing).
  • Multi-domain forests can be served by running once per domain --server or by binding to the GC with --gc for forest-wide reads.

Smoke tests run

  • Package installs cleanly with new ldap3 dependency.
  • dmc-prepop --help shows both ad and sccm subcommands.
  • dmc-prepop ad --help renders all options correctly.
  • SPN parser passes 6 round-trip cases (named instances, ports, exchange multi-segment, host SPN, etc.).
  • Filetime round-trip passes for a known UTC datetime.
  • DN parser correctly extracts OU path and managedBy CN.
  • All 9 AD queries register in ALL_QUERIES; SCCM module unaffected.

Test plan

  • pip install -e dmc-prepop succeeds with the new ldap3 dep
  • dmc-prepop ad --help renders
  • Run against a real (non-prod) AD domain: dmc-prepop ad --server dc01.<domain> --user '<dom>\\reader' --password '...' --out output/
  • Verify all 10 CSVs are produced
  • Open ad_servers.csv: confirm hostname normalisation (lowercase, NetBIOS) and OU column populated
  • Open ad_spns_computer.csv and ad_spns_service_accounts.csv: spot-check MSSQLSvc rows resolve to Workload = SQL Server
  • ad_domain_controllers.csv matches the actual DCs in the domain
  • ad_stale_servers.csv is sorted descending by LastLogonDays
  • ad_coverage_summary.csv row totals match the underlying CSVs
  • Try --gc against a Global Catalog and confirm forest-wide results
  • Try --auth kerberos from a host with a valid TGT (Linux with kinit or domain-joined Windows)

https://claude.ai/code/session_01JgkCjwqTFGQBra1unq3d8v


Generated by Claude Code

claude added 2 commits May 1, 2026 12:39
Implements all 9 queries from the AD pre-population spec via Python ldap3:
servers (Query 1), computer SPNs (2), service-account + gMSA SPNs (3),
sites (4), subnets (5), OU distribution (6), stale servers (7), domain
controllers (8), and the single-row coverage summary (9). Adds the `ad`
subcommand to the existing dmc-prepop CLI alongside `sccm`. SPN service
classes are mapped to friendly workload labels (SQL Server, Exchange,
RDS, etc.) per the spec's reference table.
Removes dataclasses.field and typing.Iterable imports that became unused
during the AD module addition. No behaviour change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants