corvus,
an audit of who you are, across systems.
A read-only PowerShell module that triages duplicate Active Directory user objects in environments federating Oracle HCM, Workday, UKG, SailPoint, CyberArk, AD, and Entra ID.
corvus is one PowerShell module file --- ~1500 lines, 29 exported functions --- that runs against an existing AD environment and writes JSON / CSV / summary artifacts. It is read-only by design. Detectors investigate and report; nothing in the module ever calls Set-AD* or Remove-AD*. The wrapper script Invoke-Corvus.ps1 runs preflight + a full sweep and lands a run folder under %TEMP%\Corvus\<RunId>\.
Tech scope
- Detectors named
Find-Corvus*follow a strict contract: take-Inventory(output ofGet-CorvusInventory) and emit findings as a side effect. Adding a new detector means writing the function, exporting it, and inserting one call in the orchestrator. - Module-scoped state (
$script:RunContext,$script:Findings,$script:Inventory,$script:Config) is mutated across calls;Initialize-CorvusRunresets, detectors append viaNew-CorvusFinding. - Configuration ships with vendor-account name patterns (SailPoint, Workday, Oracle HCM, UKG, CyberArk, Entra Connect) and AD attribute mappings (
extensionAttribute1for source system,extensionAttribute14for source record id). Override per-environment withSet-CorvusConfiguration; never edit the defaults. - Reads Security event log (event 4720, account creation) from domain controllers when available.
-SkipEventLogfalls back to inventory-only mode.
What "read-only" means here
The module never calls a write cmdlet against the directory. Detectors investigate and report; remediation is the operator’s responsibility, performed in their own change-control process. This is preserved as an invariant — mirrored when adding new detectors, and enforced socially by code review.
Output
One run produces three artifacts in %TEMP%\Corvus\<RunId>\:
- JSON dump of every finding (machine-readable; suitable for feeding into a SIEM or ticketing system).
- CSV with one row per finding (spreadsheet-friendly).
- Markdown summary written for a human to read first.
Surface
The surface is a PowerShell module: Import-Module corvus; Invoke-CorvusTriage -Sources @(...). Read-only by contract — the module does not have write credentials to any of the 7 systems, so the worst it can do is exhaust a connector. Output is a single normalized table per source plus a cross-source duplicate ledger keyed on a deterministic identity hash.
Constraints
Every connector has its own definition of “the same person.” Workday's worker ID is canonical inside Workday and meaningless to AD; SailPoint's identityName may match Entra's userPrincipalName but only after a normalization pass. corvus does the normalization explicitly and exposes the rules as data, not code — so a triage-time rule change does not require a module re-publish. Idempotency is non-negotiable: the same input across the same 7 sources yields a byte-identical duplicate ledger.