Know exactly what you have. One command, one file, about twenty seconds.
ADOutline answers a question every organisation should be able to answer on demand and most cannot: what is actually configured in our Active Directory right now?
It reads the directory and writes the answer down — 47 sections and 11 diagrams in a single HTML file that anyone can open, email, archive, or attach to a ticket. No agents. No installation. No data leaves your network.
It is documentation, not an assessment. No scores, no grades, no traffic lights, no recommendations. It records what is configured and leaves the judgement to the people qualified to make it.
Active Directory is usually the oldest system in the building and the least documented. It has been upgraded, merged, migrated and inherited across decades and staff changes. The people who made the original decisions have often left. What remains is a system everything else depends on, that nobody can fully describe.
That gap becomes expensive at exactly the wrong moments:
- An acquisition or divestiture, where someone has to describe two directories before they can be joined or separated
- A migration or modernisation programme, where the plan is only as good as the picture of what exists today
- An audit or a cyber-insurance questionnaire, where "we believe so" is not an acceptable answer
- A handover — to a new team, a new MSP, or a new owner
- A ransomware tabletop or DR test, where the first question is what the environment looks like when it is working
ADOutline produces that description in twenty seconds, as a file, that a person who does not administer Active Directory can still read.
It is strictly read-only. No AD object, registry value, GPO or file is modified. Nothing is transmitted anywhere. It can be run during business hours without a change ticket.
Most tools answer a question you didn't ask. They score you, or they need RSAT and a domain-joined jump box, or they want an agent, or they hand back a spreadsheet you still have to interpret.
ADOutline talks to the directory over LDAP directly rather than through the
ActiveDirectory module, so it needs no RSAT and installs nothing. That
means it runs from a non-domain-joined machine over a customer VPN with
-Server and -Credential — usually the difference between collecting on day
one and waiting a week for a jump box to be provisioned.
Run it once. Open the HTML. Send it to whoever needs it.
Because this tool's job is to run once, in an environment you do not control, often on a machine somebody else owns.
| Single script | Module | |
|---|---|---|
| Getting it there | Email it, paste it into an RDP session, drop it on a share | PSGallery access, or unpack to a module path, or Import-Module .\path |
| Security review | One file. The customer's security team can read all of it before it touches a domain controller | Review a package, then trust the install |
| Install footprint | None. Nothing is written outside the output folder | Files placed on the machine |
| Change ticket | Usually not needed | Usually needed |
| Prerequisites | Windows PowerShell 5.1. Nothing else | Module present, correct version, right path |
A module is the better shape for software you run repeatedly in your own
environment — better testing, cleaner diffs, shared code, Install-Module.
Those are real advantages and this project pays for not having them.
But for a tool whose entire pitch is read-only, installs nothing, sends nothing anywhere, being a single inspectable artifact is not a packaging decision. It is part of what is being claimed. "Here is the whole thing, read it before you run it" is a materially easier conversation with a security team than "please approve this package from the internet" — and that conversation is frequently what stands between collecting today and collecting next week.
The rest of the suite works the same way, for the same reason.
| Tool | What it answers |
|---|---|
| ADOutline | Document the current state |
| ADAtlas | See the environment |
| ADCanvas | Understand the environment |
| ADLens | Track how it changes |
ADAtlas draws the picture. ADCanvas judges the health. ADOutline writes down the facts — and writes them into a state file you can re-render or compare later.
47 sections, 11 interactive diagrams, one HTML file.
- Forest & Domains Map, Trust Map, Replication Topology, FSMO Placement, Site Topology — all drawn as interactive SVG, all clickable
- Domain functional levels, trust attributes decoded, site links by cost
- Per-DC capability map: which controller answered on which protocol
- Users, computers and groups counted by state — enabled, disabled, locked, password-never-expires, unconstrained delegation, no-preauth, adminCount, inactive at 90/180/365 days
- Counts are produced by a streaming paged read that discards every object as it goes, so memory stays flat regardless of estate size
- One node per platform on the forest map, linked to the root, with every independent signal folded in as evidence
- Entra Connect, Okta, Ping, SailPoint, JumpCloud, Duo, Google Cloud Identity, AD FS — detected from directory objects, published SCPs, and agents installed on a domain controller
- Tenant and organisation names extracted where the directory publishes them
- Adprep operation clusters dating each upgrade event
- Dated milestone containers —
Protected Usersis 2012 R2,Key Adminsis 2016, and each container's creation date is the date that domainprep ran - Schema extension timeline — every schema object grouped by creation date, so Exchange, SCCM and legacy LAPS each date themselves
- Migration artifacts — the
<NetBIOS>$$$group ADMT's password export step requires, and the sIDHistory it left behind - Functional-level raise history from replication metadata
- FRS remnants alongside DFSR, and Pre-Windows 2000 Compatible Access membership
- LAPS — legacy Microsoft LAPS vs Windows LAPS by generation, expiry analysis, encrypted vs plaintext, policy read from the registry
- dMSA migration state and superseded account linkage
- Kerberos policy, encryption types, KRBTGT age, FGPP, AdminSDHolder
- Backup state from
dSASignature, not a registry key that doesn't exist - Time hierarchy — configured vs effective source, with the forest apex shown
- DNS Architecture and DNS Zone Replication — zone type is recorded per server, because the same zone is commonly Primary on one DC and Secondary on another
- PKI hierarchy, Exchange architecture, NTP hierarchy, OU hierarchy
- DHCP authorisation, AD FS farms, LSA packages, custom password filters
- Score, grade or rank anything
- Recommend remediation
- Modify any AD object, registry value, GPO or file — it is strictly read-only
- Require RSAT or the
ActiveDirectorymodule - Require WinRM or PowerShell remoting
- Send data anywhere
- Scan member servers or workstations. It reads the directory and the domain controllers, and nothing else
If you want a picture, use ADAtlas. If you want health analysis, use ADCanvas. If you want the facts written down, use ADOutline.
| Requirement | |
|---|---|
| PowerShell | Windows PowerShell 5.1 or newer |
| Modules | None. System.DirectoryServices.Protocols ships with .NET |
| Network | LDAP (389) to a DC in each domain |
| Host | Any Windows machine with line of sight to the DCs. Does not need to be domain-joined and does not need to run on a DC |
ADOutline collects in three tiers. Each degrades independently — losing one never stops the others, and the report states which sections were affected.
| Tier | Method | Rights | Covers |
|---|---|---|---|
| A | LDAP | Domain User | ~80% of the report |
| B | SMB to SYSVOL | Domain User | GPO templates, Kerberos policy |
| C | CIM / DCOM to each DC | Local Administrator on the DC | DC hardware, services, hotfixes, registry, event log summaries |
Every report includes a Permissions Required by Section table recording which rights each section needed and whether they were available — so an empty section caused by missing access is never mistaken for an empty environment.
.\ADOutline.ps1That's it.
From a non-domain-joined machine:
$cred = Get-Credential
.\ADOutline.ps1 -Server dc01.contoso.com -Credential $credDirectory data only, no local admin needed anywhere:
.\ADOutline.ps1 -SkipTierCRe-render an earlier collection without touching the directory:
.\ADOutline.ps1 -FromState '.\ADOutline_contoso_com_20260802_143915.state.json'| Parameter | Default | Purpose |
|---|---|---|
-Server |
Target a specific DC or domain. Required from a non-domain-joined host | |
-Credential |
Alternate credentials | |
-OutputPath |
script directory | Where the report, state file and log are written |
-FromState |
Re-render a saved state file. No directory access at all | |
-SkipTierC |
Skip all CIM/DCOM collection | |
-ExcludeDC |
Domain controllers to leave alone | |
-NoProbe |
Skip the startup capability probe | |
-NoHtml |
Collect and write state, but produce no report | |
-NoState |
Produce the report but write no state file | |
-ShowDetail |
Verbose console output | |
-LogPath |
auto | Log file location |
-PageSize |
1000 |
LDAP page size |
-LdapTimeoutSec |
30 |
Hard per-query timeout |
-ProbeTimeoutMs |
1500 |
Per-DC TCP probe timeout |
-ProbeThrottle |
32 |
Parallel probes |
-TierCTimeoutSec |
90 |
Per-DC Tier C timeout |
-TierCThrottle |
12 |
Parallel Tier C runspaces |
-SkipHotfixScan |
Skip hotfix enumeration (the slowest Tier C step) | |
-SkipSoftwareScan |
Skip installed-software enumeration | |
-EventDays |
7 |
Days of event log summary to collect |
A few decisions worth knowing about if you plan to read the source.
RSAT-free by design. System.DirectoryServices.Protocols throughout, never
the ActiveDirectory module. This is what makes a non-domain-joined run possible.
Counts, not objects. Populations are counters incremented during a streaming paged read. Tables survive only where the population is bounded by design — DCs, sites, trusts, FSMO, CAs, zones. Nothing accumulates a million DNs in memory.
Capability map with one-way demotion. One parallel TCP probe at startup records which protocols each DC answers on. A real failure demotes that capability, and every later section skips the host for free. Never re-probed.
Hard timeouts everywhere. Every LDAP query and every Tier C runspace has a deadline. A dead DC costs one timeout, not a stall.
Warnings vs Observations. A warning means collection was impaired. An observation is a factual note about the environment. They are never mixed, and neither is scored. "Not deployed" and "not readable" are different answers and are reported differently.
The state file is the interface. Collection owns the state; display only
reads it. -FromState re-renders with zero directory access — useful for
re-rendering after a report change without going back to the customer, and the
raw material for comparing two points in time.
Three files per run:
| File | Purpose |
|---|---|
ADOutline_<forest>_<stamp>.html |
The report. Self-contained — no CDN, no external assets |
ADOutline_<forest>_<stamp>.state.json |
Everything collected, before rendering |
ADOutline_<stamp>.log |
Timestamped collection log |
The HTML is typically 200–600 KB depending on estate size. Open it locally, archive it, attach it to a ticket, or host it as a static file.
Issues and pull requests welcome. Please test against a real (lab) forest before submitting — this script handles a lot of edge cases that only appear in actual environments, and several of its subtler bugs were only ever found by running it against a live directory.
Santhosh Sivarajan — Microsoft MVP
- LinkedIn: https://www.linkedin.com/in/sivarajan/
- GitHub: https://github.com/SanthoshSivarajan
MIT — free to use, modify, and distribute.