Skip to content

Commit d60b169

Browse files
Add post: One-Line Server Inventory & AD Audit Report
1 parent 6dfe74e commit d60b169

1 file changed

Lines changed: 162 additions & 0 deletions

File tree

  • content/posts/server-inventory-audit-report
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
---
2+
title: "One-Line Server Inventory & AD Audit Report"
3+
date: 2025-10-04T10:00:00+05:30
4+
draft: false
5+
description: "Bootstrap VB.ServerInventory from a gist and produce a full server + AD audit report — stale accounts, BitLocker, GPOs, patches — in one run."
6+
tags: ["PowerShell", "Active Directory", "Windows Server", "Automation", "Security", "Audit"]
7+
categories: ["PowerShell Automation"]
8+
author:
9+
name: Vibhu Bhatnagar
10+
---
11+
12+
## Purpose
13+
14+
Every Windows environment I inherit has the same problem — nobody knows what's actually on the servers. Who's got a password older than a year? Which computers haven't checked in for six months? Is BitLocker actually on? This one-liner bootstraps `VB.ServerInventory` and produces a full server + AD audit report in a single run. No agent, no console, no license.
15+
16+
## What This Covers
17+
18+
- What the bootstrap script actually does (line by line)
19+
- The report it produces and what's in it
20+
- Which security baselines the audit checks map to
21+
- How to run it and verify the output
22+
23+
## Before You Start
24+
25+
- Windows Server 2016+ or Windows 10/11 with PowerShell 5.1 or later
26+
- Local admin on the target machine
27+
- Domain admin (or delegated read) if you want the AD, GPO, and DHCP sections populated
28+
- Internet access to reach PSGallery and the gist
29+
- ExecutionPolicy needs to allow the run — the script sets `Unrestricted` for CurrentUser scope
30+
31+
## The Bootstrap
32+
33+
```powershell
34+
Set-ExecutionPolicy Unrestricted -Scope CurrentUser -Force
35+
36+
# Enforce TLS 1.2 — PSGallery rejects older protocols
37+
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
38+
39+
# Register PSGallery if it's missing (fresh servers often have no repos)
40+
if (-not (Get-PSRepository -Name "PSGallery" -ErrorAction SilentlyContinue)) {
41+
Register-PSRepository -Default -ErrorAction Stop
42+
}
43+
44+
# Trust PSGallery so Install-Module doesn't prompt
45+
Set-PSRepository -Name "PSGallery" -InstallationPolicy Trusted
46+
47+
# Widen the console buffer so nothing scrolls off screen
48+
$host.UI.RawUI.BufferSize = New-Object System.Management.Automation.Host.Size(500, 9000)
49+
50+
Get-PSRepository
51+
52+
# Pull and execute the inventory script from the gist
53+
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
54+
iex (Invoke-WebRequest "https://gist.githubusercontent.com/Vibhu2/9a4ecf03d30c35d27073c71a2d5f0d4d/raw/b0541f3663701a2b16d0e1a8fd4a4030f9bf70ee/gistfile1.txt" -UseBasicParsing).Content
55+
```
56+
57+
The first block is prep — TLS, repo trust, buffer size. The last line is the actual work: it pulls the `Get-ServerInventory` script from a gist and runs it in the current session.
58+
59+
> **[!] WARNING:** `iex` on remote content is convenient but risky. Only run this against a gist you trust — or better, download the raw file, read it, and dot-source it locally.
60+
61+
## What the Report Contains
62+
63+
`Get-ServerInventory` runs a wide sweep across the machine and (if `-IncludeAD` is set) the domain. The output is grouped into sections:
64+
65+
**System & Hardware**
66+
67+
- OS build, BIOS, CPU, memory, uptime, last boot
68+
- Disk usage per logical drive
69+
- Network adapters — IP, DHCP vs static, DNS
70+
71+
**Software & Roles**
72+
73+
- Installed applications (from the registry uninstall keys — the reliable source)
74+
- Windows Store apps
75+
- Windows Features and Roles
76+
- Installed hotfixes and updates
77+
- Printers, drivers, and recent print jobs
78+
79+
**Directory & Network Services**
80+
81+
- AD domain info
82+
- All GPOs
83+
- DHCP scopes, reservations, exclusions
84+
- DNS zones and forwarders
85+
- SMB shares
86+
- Active RDS / terminal sessions
87+
88+
**Security Posture**
89+
90+
- Azure AD / Entra join status (`dsregcmd /status`)
91+
- BitLocker status and recovery key presence
92+
- Windows Defender configuration
93+
94+
**Audit Findings** — this is the part that matters most
95+
96+
- Users inactive 90+ days
97+
- Computers inactive 90+ days
98+
- Accounts that have never logged in
99+
- Expired user accounts
100+
- Users with **Password Never Expires**
101+
- Admin accounts with passwords older than 1 year
102+
- Empty AD groups
103+
- Accounts with **Password Not Required**
104+
105+
The report renders to console with colored section headers by default. Pass `-ExportCSV` and it writes to `$env:USERPROFILE\Desktop\ServerInventory-[timestamp]`.
106+
107+
## Why This Report Matters
108+
109+
Most SMBs and even mid-size shops don't run a proper audit until something breaks — a ransomware hit, a failed audit, a departing admin. This report gets you 80% of that visibility in one run. Specifically:
110+
111+
- **The stale-account checks catch abandoned identities** — the single most common initial-access vector in AD compromises
112+
- **Password-age and never-expires findings** surface accounts that violate every modern baseline
113+
- **BitLocker + Defender status** confirms two of the checks every cyber insurance renewal asks for
114+
- **Inventory of installed apps and hotfixes** is what you hand a vulnerability team on day one
115+
116+
Run it quarterly on every server. Diff the CSVs. That's your change control.
117+
118+
## Standards This Aligns With
119+
120+
The script doesn't cite any standard directly — but the audit checks map cleanly to controls in every mainstream security baseline. The 90-day inactivity threshold, password-age checks, and privileged-account monitoring are lifted straight from these:
121+
122+
| Check | Aligns with |
123+
| :--- | :--- |
124+
| Inactive users / computers (90+ days) | CIS Controls v8 §6.2, NIST 800-53 AC-2(3), ISO 27001 A.9.2.6 |
125+
| Password Never Expires / no password required | CIS Windows Benchmark §1.1.x, NIST 800-63B, Essential Eight ML1 |
126+
| Admin passwords older than 1 year | CIS Controls v8 §5.4, NIST 800-53 IA-5(1) |
127+
| BitLocker enforcement | CIS Windows Benchmark §18, NIST 800-171 3.13.11 |
128+
| Windows Defender enabled | CIS Controls v8 §10.1, Essential Eight ML1 |
129+
| Empty AD groups / group hygiene | CIS Controls v8 §6.8, ISO 27001 A.9.2.5 |
130+
| Installed patches / hotfixes | CIS Controls v8 §7.3, NIST 800-53 SI-2 |
131+
132+
None of this replaces a formal audit. It gets you ready for one.
133+
134+
## Verify
135+
136+
After the script finishes:
137+
138+
```powershell
139+
# Confirm the CSVs landed
140+
Get-ChildItem "$env:USERPROFILE\Desktop\ServerInventory-*" | Select-Object Name, LastWriteTime
141+
142+
# Spot-check the audit section
143+
Get-Content "$env:USERPROFILE\Desktop\ServerInventory-*\AuditFindings*.csv" | Select-Object -First 20
144+
```
145+
146+
If the AD sections are empty, you're either not on a domain-joined machine or the account running the script doesn't have read access to AD. Re-run under an account with `Domain Users` at minimum.
147+
148+
## Notes
149+
150+
- Skip the Windows Store scan on servers — it's slow and returns nothing useful. Use `-SkipStore`
151+
- On DCs, always pass `-IncludeAD` — otherwise you're only inventorying the box
152+
- For fleet runs, wrap the bootstrap in `Invoke-Command` against a list of servers and dump each report to a share
153+
154+
## References
155+
156+
- [PSGallery — VB.ServerInventory](https://www.powershellgallery.com/packages/VB.ServerInventory)
157+
- [CIS Controls v8](https://www.cisecurity.org/controls/v8)
158+
- [NIST SP 800-53 Rev. 5](https://csrc.nist.gov/publications/detail/sp/800-53/rev-5/final)
159+
- [ACSC Essential Eight](https://www.cyber.gov.au/resources-business-and-government/essential-cyber-security/essential-eight)
160+
- [Microsoft — dsregcmd status](https://learn.microsoft.com/en-us/azure/active-directory/devices/troubleshoot-device-dsregcmd)
161+
162+
{{< post-cta module="VB.ServerInventory" module_url="https://www.powershellgallery.com/packages/VB.ServerInventory" >}}

0 commit comments

Comments
 (0)