ADMappingToolkit Hybrid SI mapping — AD inventory + active network scan
ADMappingToolkit is a hybrid SI mapping toolkit. Classic AD-based inventory only sees machines it already knows. In any real enterprise network, AD is structurally incomplete: unmanaged devices, IoT, printers, Linux servers, NAS, and shadow IT never register in AD. ADMappingToolkit closes this visibility gap by combining AD queries with active network probing in a single unified report.
Three runner scripts cover the three most common scopes: all machines, servers only, or clients only. Each script is a thin wrapper around the module functions and exposes the full parameter set directly from the command line.
AD correlation chain
When scanning a raw IP target, the tool does not just report "this IP responded". It runs a full correlation chain:
- Reverse DNS lookup (
Resolve-DnsName -Type PTR) to retrieve the FQDN - AD lookup by
DNSHostNameorNameto find a match - If found: entry enriched with OS, OU, LastLogon, and all AD metadata
- If not found: a synthetic object is created — the device is flagged as non-AD
A single subnet scan returns both your managed Windows fleet and every unmanaged device on the same network in one report.
Architecture
ADMappingToolkit/
ADMappingToolkit.psm1 # Module — all functions
scripts/
Check-All.ps1 # Wrapper: Get-MachineInventory (all OS)
Check-Servers.ps1 # Wrapper: Get-ServerInventory (Server OS)
Check-Clients.ps1 # Wrapper: Get-ClientInventory (Client OS)
tools/
Invoke-PSEncoder.py # Base64 UTF-16LE encoder for -EncodedCommand
Sign-Scripts.ps1 # Authenticode signing with RFC 3161 timestamp
The module exports three inventory functions. Get-MachineInventory queries all AD computers. Get-ServerInventory and Get-ClientInventory are thin wrappers that pre-filter by OS family before passing the target list to the shared inventory engine.
Installation
Requirements
- PowerShell 5.1 or PowerShell 7+
- RSAT ActiveDirectory module (
Import-Module ActiveDirectory) - Domain-joined machine or explicit
-Credential
Clone
# Clone the repo
git clone https://github.com/franckferman/ADMappingToolkit.git
cd ADMappingToolkit
# Unblock if downloaded from the internet
Get-ChildItem -Recurse *.ps1,*.psm1 | Unblock-File
Import the module directly
Import-Module .\ADMappingToolkit.psm1 -Force
The runner scripts import the module automatically from their parent directory. You do not need to import manually when using the scripts.
Quick Start
# 1. Discover all reachable machines in the domain
.\scripts\Check-All.ps1 -OnlyReachable -Stats
# 2. Probe servers for SMB, RDP, WinRM
.\scripts\Check-Servers.ps1 -TcpCheck -Ports 445,3389,5985 -OnlyReachable
# 3. Flag end-of-support workstations
.\scripts\Check-Clients.ps1 -CheckEOS -ShowOU -AutoCsv
# 4. Export full inventory to CSV
.\scripts\Check-All.ps1 -OutCsv inventory.csv
Check-All.ps1
Wrapper for Get-MachineInventory. Queries all AD computer accounts regardless of OS. Use this for full-domain discovery.
.\scripts\Check-All.ps1 [parameters...]
Common invocations
# Reachable machines only, print stats
.\scripts\Check-All.ps1 -OnlyReachable -Stats
# Top 20 port scan, turbo mode (PS7+)
.\scripts\Check-All.ps1 -TcpCheck -Ports Top20 -Turbo
# Unconstrained delegation check
.\scripts\Check-All.ps1 -ADOnly -CheckUnconstrained -ShowOU
# Inactive hosts (not seen in 90 days)
.\scripts\Check-All.ps1 -MinDaysInactive 90 -ADOnly
Check-Servers.ps1
Wrapper for Get-ServerInventory. Pre-filters to AD computers with a Windows Server OS before scanning. Ideal for targeting domain controllers, file servers, and application servers.
.\scripts\Check-Servers.ps1 [parameters...]
Common invocations
# SMB + RDP + WinRM probe
.\scripts\Check-Servers.ps1 -TcpCheck -Ports 445,3389,5985 -OnlyReachable
# LDAP availability check
.\scripts\Check-Servers.ps1 -CheckLDAP -OnlyReachable
# Check WinRM connectivity
.\scripts\Check-Servers.ps1 -CheckWinRM -OnlyReachable
# EOS + unconstrained delegation
.\scripts\Check-Servers.ps1 -ADOnly -CheckEOS -CheckUnconstrained -ShowOU
Check-Clients.ps1
Wrapper for Get-ClientInventory. Pre-filters to AD computers with a Windows client OS (Windows 10/11/7/XP etc.) before scanning.
.\scripts\Check-Clients.ps1 [parameters...]
Common invocations
# Flag end-of-support workstations
.\scripts\Check-Clients.ps1 -CheckEOS -ShowOU -ADOnly
# RDP exposure on clients
.\scripts\Check-Clients.ps1 -Report OpenRDP
# Inactive clients (no logon in 60 days)
.\scripts\Check-Clients.ps1 -MinDaysInactive 60 -ADOnly
# Export all clients to CSV
.\scripts\Check-Clients.ps1 -AutoCsv -ADOnly
Parameters Reference
All three scripts accept the same parameter set.
AD Query
| Parameter | Default | Description |
|---|---|---|
| -SearchBase | "" | LDAP OU/DN to scope the AD query. Empty = entire domain. |
| -IncludeDisabled | off | Include disabled computer accounts in results. |
| -OnlyDisabled | off | Return only disabled computer accounts. |
| -MinDaysInactive | 0 | Filter to computers inactive for at least N days (0 = no filter). |
| -Targets | — | Override AD query: supply hostnames, IPs, CIDR, ranges, or wildcards. |
Network Probing
| Parameter | Default | Description |
|---|---|---|
| -NoIcmp | off | Skip ICMP ping. Use TCP check or AD-only mode instead. |
| -PingCount | 1 | Number of ICMP echo requests per host. |
| -TimeoutMs | 1200 | TCP connect timeout in milliseconds. |
| -TcpCheck | off | Probe TCP ports defined by -Ports. |
| -Ports | 445,3389,5985 | Comma-separated ports or Top5/Top10/Top20/Top50/Top100 keyword. |
| -CheckLDAP | off | Probe TCP 389 and 636 (LDAP / LDAPS). |
| -CheckWinRM | off | Test WinRM connectivity (Test-WSMan). |
| -CheckPSSession | off | Attempt New-PSSession to verify remote PS access. |
| -ResolveDns | off | Reverse-resolve IP targets to hostnames for AD correlation. |
| -ADOnly | off | Skip all network probes, return AD attributes only. |
Filtering
| Parameter | Default | Description |
|---|---|---|
| -OnlyReachable | off | Return only hosts that responded to ICMP or TCP. |
| -OnlyUnreachable | off | Return only hosts that did NOT respond. |
Performance
| Parameter | Default | Description |
|---|---|---|
| -Turbo | off | Enable ForEach-Object -Parallel scanning. Requires PowerShell 7+. Mutually exclusive with -Stealth. |
| -Threads | 50 | Thread count for Turbo mode (capped at 1000). |
| -Stealth | off | Sequential scan with random delay between hosts. Disables Turbo automatically. |
| -StealthDelay | 2,10 | Min/max random delay in seconds for Stealth mode. E.g. -StealthDelay 5,20. |
AD Enrichment
| Parameter | Default | Description |
|---|---|---|
| -CheckUnconstrained | off | Retrieve TrustedForDelegation attribute. Flag machines with unconstrained delegation. |
| -CheckEOS | off | Flag end-of-support OS (Windows XP, 7, 2003, 2008, etc.). |
| -ShowOU | off | Include OU path in output. |
| -ShowDescription | off | Include AD computer description field. |
| -ShowEnabled | off | Include Enabled column. |
| -Credential | — | PSCredential for AD queries and WinRM checks. |
Output
| Parameter | Default | Description |
|---|---|---|
| -Properties | * | Select specific columns from the result set. |
| -RawOutput | off | Return raw PSObject array (pipeline-friendly). Suppresses table formatting. |
| -Stats | off | Print summary line: total / reachable / unreachable / reachability %. |
| -AutoCsv | off | Auto-generate timestamped CSV filename and export. |
| -OutCsv | "" | Export results to specified CSV path. |
Reports
| Parameter | Default | Description |
|---|---|---|
| -Report | — | Named report: Inactive or OpenRDP. |
| -ReportDays | 30 | Lookback window in days for the Inactive report. |
Port Keywords
Pass a keyword to -Ports instead of a manual list. Setting a Top keyword automatically enables -TcpCheck.
| Keyword | Ports |
|---|---|
| Top5 | 21, 22, 80, 443, 3389 |
| Top10 | Top5 + 25, 53, 135, 139, 445 |
| Top20 | Top10 + 110, 143, 389, 1433, 3306, 5900, 5985, 8080, 8443 |
| Top50 | Top20 + 23, 69, 88, 111, 119, 161, 162, 179, 199, … |
| Top100 | Top50 + 1025, 1194, 1521, 1723, 2000, 2049, 4444, 5432, … |
Target Formats
The -Targets parameter bypasses the AD query and accepts mixed input:
| Format | Example | Behavior |
|---|---|---|
| Hostname / AD name | DC01 | Queried directly in AD by name. |
| Wildcard | SRV-* | AD filter: Name -like 'SRV-*'. |
| Single IP | 192.168.1.10 | Probed directly; optional PTR reverse lookup. |
| CIDR | 192.168.1.0/24 | All host IPs in range (capped at 65536). |
| IP range | 192.168.1.1-50 | Short range: resolves to .1 through .50. |
| IP range (full) | 10.0.0.1-10.0.0.100 | Full range between two IPs. |
# Mixed: wildcard + CIDR
.\scripts\Check-All.ps1 -Targets SRV-*, 192.168.10.0/24 -TcpCheck -Ports 445,3389
# IP range with DNS reverse lookup
.\scripts\Check-All.ps1 -Targets 10.0.0.1-50 -ResolveDns -TcpCheck
Reports
Inactive
Returns machines whose last logon is older than -ReportDays days (default 30).
.\scripts\Check-All.ps1 -Report Inactive -ReportDays 90 -ADOnly
OpenRDP
Returns machines with TCP 3389 open. Automatically enables -TcpCheck and adds port 3389 to the probe list if missing.
.\scripts\Check-Clients.ps1 -Report OpenRDP
.\scripts\Check-Servers.ps1 -Report OpenRDP -OnlyReachable
AD Recon Examples
# Full domain inventory, AD attributes only
.\scripts\Check-All.ps1 -ADOnly -ShowOU -ShowDescription -ShowEnabled
# Find computers with unconstrained delegation
.\scripts\Check-All.ps1 -ADOnly -CheckUnconstrained -ShowOU
# Scope to a specific OU
.\scripts\Check-Servers.ps1 -SearchBase "OU=Servers,DC=corp,DC=local" -ADOnly
# Hosts inactive for 30+ days, export CSV
.\scripts\Check-All.ps1 -Report Inactive -ReportDays 30 -ADOnly -AutoCsv
# Use alternate credentials
$cred = Get-Credential
.\scripts\Check-All.ps1 -ADOnly -Credential $cred
Network Scan Examples
# Ping sweep + TCP Top20
.\scripts\Check-All.ps1 -TcpCheck -Ports Top20 -OnlyReachable -Stats
# LDAP availability on all servers
.\scripts\Check-Servers.ps1 -CheckLDAP -OnlyReachable
# WinRM + PSSession check
.\scripts\Check-Servers.ps1 -CheckWinRM -CheckPSSession -OnlyReachable
# Pure IP range scan (no AD)
.\scripts\Check-All.ps1 -Targets 192.168.1.0/24 -TcpCheck -Ports 445,3389 -Turbo
# RDP exposure report across all clients
.\scripts\Check-Clients.ps1 -Report OpenRDP -Stats
Stealth Examples
# Slow sweep, 5-15 s random delay between hosts
.\scripts\Check-All.ps1 -Stealth -StealthDelay 5,15 -OnlyReachable
# Stealth + no ICMP (TCP-only, even less noise)
.\scripts\Check-All.ps1 -Stealth -NoIcmp -TcpCheck -Ports 445
# Stealth on a specific target range
.\scripts\Check-All.ps1 -Targets SRV-* -Stealth -StealthDelay 10,30
Export Examples
# Auto-generate timestamped CSV
.\scripts\Check-All.ps1 -AutoCsv
# Explicit output path
.\scripts\Check-Servers.ps1 -OutCsv C:\Audit\servers.csv -TcpCheck -Ports 445,3389,5985
# Select specific columns
.\scripts\Check-All.ps1 -ADOnly -Properties Name,OS,LastLogon,OU -OutCsv names.csv
# Pipeline: filter and export in PS
$r = .\scripts\Check-All.ps1 -RawOutput -TcpCheck -Ports 3389
$r | Where-Object { $_.TCP_3389 } | Export-Csv rdp-exposed.csv -NoTypeInformation
Use Cases
For security teams / RSSI
| Need | How |
|---|---|
| How many machines are on the network? | -Targets 10.0.0.0/24 -ResolveDns -Turbo |
| Do we have end-of-support OS? | -CheckEOS flags XP, Vista, 7, 2003, 2008, 2012 |
| Which servers have RDP exposed? | -Report OpenRDP |
| Ghost machines inactive for 90+ days? | -MinDaysInactive 90 -ADOnly |
| Can we remotely administer a host? | -CheckWinRM -CheckPSSession |
| Kerberos unconstrained delegation risk? | -CheckUnconstrained |
| ISO 27001 / ANSSI asset inventory? | Full subnet scan + -AutoCsv for timestamped evidence |
| Shadow IT / unmanaged devices? | Non-AD objects returned as synthetic entries in same report |
Real-world results
Scan of a /24 subnet (254 hosts) in a production environment:
| Category | Count | Details |
|---|---|---|
| Total responding hosts | 126 | Out of 254 scanned |
| Windows machines (AD-matched) | ~50 | Workstations and servers enriched with OS, OU, LastLogon |
| Non-AD devices | ~76 | Printers, Raspberry Pi, NAS, Linux servers, IoT, appliances, smartphones |
| EOS servers detected | 2 | Windows Server 2012 R2, Windows Server 2008 R2 |
In one command: a complete 360-degree view of the network — managed and unmanaged alike.
Security Context
Unconstrained Kerberos Delegation (-CheckUnconstrained)
Unconstrained delegation (TrustedForDelegation = True) is a Kerberos configuration that allows a service to impersonate any authenticated user to any other service in the domain. When a user authenticates to a host configured with unconstrained delegation, the domain controller embeds a full copy of the user's TGT (Ticket Granting Ticket) in the service ticket. The receiving host can then use that TGT to request service tickets on behalf of the user — without restriction.
Attack path: if an attacker compromises a host with unconstrained delegation and forces a privileged account (e.g., a Domain Controller computer account) to authenticate to it — via printer bug, PetitPotam, or coercion — the attacker captures a TGT for that account. A DC TGT enables a DCSync attack and full domain compromise.
-CheckUnconstrained queries TrustedForDelegation from AD. Any non-DC computer with this flag set is a high-risk finding. Domain Controllers legitimately carry this attribute — filter them out. Remediation: switch to constrained delegation (msDS-AllowedToDelegateTo) or resource-based constrained delegation (RBCD).
End-of-Support Operating Systems (-CheckEOS)
Microsoft end-of-support (EOS) dates mark the point after which no security patches are issued for a product. EOS systems accumulate unpatched CVEs indefinitely. The vulnerability surface grows with every new public disclosure while the system remains permanently exposed.
| OS | End of Support | Known CVE exposure |
|---|---|---|
| Windows XP | April 2014 | MS17-010 (EternalBlue), MS08-067, dozens of unpatched RCEs |
| Windows Vista | April 2017 | EternalBlue, EternalRomance, multiple privilege escalations |
| Windows 7 | January 2020 | EternalBlue, PrintNightmare (partial), BlueKeep (CVE-2019-0708) |
| Windows Server 2003 | July 2015 | MS17-010, MS08-067, multiple unpatched RPC and SMB RCEs |
| Windows Server 2008 (R2) | January 2020 | EternalBlue, PrintNightmare, Zerologon (CVE-2020-1472) |
| Windows Server 2012 (R2) | October 2023 | Growing unpatched CVE backlog post-EOL |
RDP Exposure (-Report OpenRDP)
TCP 3389 (Remote Desktop Protocol) exposed on workstations and servers is one of the most targeted attack surfaces in enterprise environments. RDP brute-force, credential stuffing, and unauthenticated pre-auth vulnerabilities (BlueKeep, CVE-2019-0708; DejaBlue, CVE-2019-1181/1182) have driven widespread ransomware deployments.
The OpenRDP report identifies all hosts with TCP 3389 open and reachable, regardless of whether they are in AD. This includes workstations with RDP inadvertently enabled, servers with RDP exposed to internal segments, and legacy hosts.
Stealth Mode and Detection Evasion (-Stealth)
Standard sequential network scanning generates predictable, high-volume traffic patterns. ICMP sweeps produce a burst of echo requests from a single source IP to sequential destinations — a trivial signature for IDS, SIEM correlation rules, and NDR (Network Detection and Response) products.
Stealth mode applies two mitigations:
- Random scan order (
Sort-Object { Get-Random }): avoids the sequential sweep signature that IDS/SIEM rules commonly alert on - Random inter-host delay (
-StealthDelay): reduces packets-per-second rate, staying below typical threshold-based alerting
Turbo mode (parallel scanning) is intentionally incompatible with Stealth — the two objectives are irreconcilable. OPSEC takes priority: if both are set, Turbo is disabled with an explicit warning.
MITRE ATT&CK Mapping
| Technique | ID | ADMappingToolkit feature |
|---|---|---|
| Remote System Discovery | T1018 | AD enumeration, -Targets subnet/range scan |
| Network Service Scanning | T1046 | -TcpCheck, -CheckLDAP, -CheckWinRM, -CheckPSSession |
| System Network Configuration Discovery | T1016 | -ResolveDns, IPv4Address, DNSHostName enumeration |
| Account Discovery: Domain Account | T1087.002 | AD computer account enumeration via Get-ADComputer |
| Domain Trust Discovery | T1482 | -CheckUnconstrained (TrustedForDelegation) |
| Steal or Forge Kerberos Tickets | T1558 | Unconstrained delegation detection context |
Invoke-PSEncoder.py
Generates PowerShell -EncodedCommand oneliners from a PS1 file or a raw string. The encoded command is Base64 UTF-16LE, which bypasses Restricted and AllSigned execution policies because PowerShell treats it as an in-memory string, not a file read from disk.
# Encode a script file
python3 tools/Invoke-PSEncoder.py scripts/Check-All.ps1
# Encode a raw command string
python3 tools/Invoke-PSEncoder.py -c ".\scripts\Check-All.ps1 -OnlyReachable -Stats"
# Add flags
python3 tools/Invoke-PSEncoder.py scripts/Check-All.ps1 --hidden --bypass
# Quiet mode (oneliner only, no decoration)
python3 tools/Invoke-PSEncoder.py -q -c ".\scripts\Check-All.ps1 -TcpCheck -Ports Top20"
# Copy to clipboard on Linux
python3 tools/Invoke-PSEncoder.py scripts/Check-All.ps1 | xclip -selection clipboard
| Flag | Description |
|---|---|
| file | Path to a PS1 file to encode. |
| -c / --command | Inline PowerShell command string to encode. |
| --hidden | Append -WindowStyle Hidden. |
| --bypass | Append -ExecutionPolicy Bypass. |
| --32 | Use 32-bit PowerShell (SysWOW64). |
| --noexit | Append -NoExit. |
| -q / --quiet | Output only the oneliner. |
Sign-Scripts.ps1
Signs all scripts in scripts/ and ADMappingToolkit.psm1 with Authenticode + RFC 3161 timestamp. The countersignature keeps signatures valid after certificate expiry.
Authenticode signing reduces detection surface: signed scripts bypass AllSigned execution policy without -ExecutionPolicy Bypass, and a valid signature lowers static heuristic scores in EDRs and AV engines that weigh unsigned scripts more aggressively.
# Auto-generate temporary self-signed cert, sign, then remove cert
.\tools\Sign-Scripts.ps1
# Use an existing cert by thumbprint
.\tools\Sign-Scripts.ps1 -CertThumbprint "AB12CD..."
# Use a different timestamp server
.\tools\Sign-Scripts.ps1 -TimestampServer "http://timestamp.sectigo.com"
Cert type comparison
| Cert type | Execution policy | SmartScreen | EDR static score |
|---|---|---|---|
| None | Requires -ExecutionPolicy Bypass | Flagged | Higher risk weight |
| Self-signed | Passes AllSigned if cert is trusted locally | Still flagged | Slight improvement |
| OV code-signing | Passes AllSigned | Reduced warnings | Lower risk weight |
| EV code-signing | Passes AllSigned | Instant reputation | Best reduction |