SCRIPT LIBRARY · POWERSHELL
Exporting Active Directory Computer Objects to CSV
A computer inventory straight out of AD, with OS build, last logon, password age and OU in one tidy CSV, ready for audits or a stale-machine cleanup.
- What it does
- Exports computer accounts from AD to CSV with OS and build, enabled state, last logon, days since last logon, machine password age, OU and description. Optionally resolves IP addresses too.
- Requires
- Windows PowerShell 5.1 or PowerShell 7 on Windows
- ActiveDirectory module (RSAT)
- Permissions
- Any domain user can read these attributes by default.
- Runs on
- Windows 10/11 with RSAT, Windows Server 2016+
- Tested
- Parse-checked and dry-run with mocked AD cmdlets in PowerShell 7.4
Part 4 of the thread Keeping Active Directory tidy
Every environment has a few hundred computer accounts that nobody can vouch for. Some are real machines, some were reimaged under a new name two years ago, and at least one is a laptop sitting in a desk drawer with a dead battery.
This script gives you the list to start from. One row per computer, with the columns you actually end up sorting on: operating system and build, when it last logged on and how many days ago that was, how old its machine password is, which OU it's in, and whether it's enabled. It's the same export I'd reach for before a cleanup, an OS upgrade project, or an audit request.
Compared to the original version: the domain controller and output folder aren't hard-coded anymore, it adds the two "days since" columns that make stale machines jump out, and it drops a few attributes that were always blank on computers anyway. IP lookups are now optional, because they're slow and they come from DNS, not AD.
<#
.SYNOPSIS
Exports Active Directory computer objects to CSV with the attributes you actually use.
.DESCRIPTION
Pulls computer accounts from AD and flattens them into one row each: name, OS and build,
enabled state, last logon, days since last logon, password age, OU and description.
Handy for inventory, audits, and finding stale machines before a cleanup.
.PARAMETER SearchBase
Only include computers under this OU. Defaults to the whole domain.
.PARAMETER CsvPath
Output file. Defaults to ad-computers-<date>.csv in the current folder.
.PARAMETER EnabledOnly
Leave disabled computer accounts out.
.PARAMETER IncludeIPAddress
Resolve each computer's IPv4 address through DNS. Noticeably slower.
.PARAMETER Server
Domain or domain controller to query.
.PARAMETER PassThru
Return the rows as objects as well as writing the CSV.
.EXAMPLE
.\Export-ADComputerInventory.ps1
.EXAMPLE
.\Export-ADComputerInventory.ps1 -SearchBase 'OU=Workstations,DC=contoso,DC=com' -EnabledOnly -PassThru | Where-Object DaysSinceLogon -gt 90
#>
[CmdletBinding()]
param(
[string]$SearchBase,
[string]$CsvPath = (Join-Path (Get-Location) ('ad-computers-{0:yyyy-MM-dd}.csv' -f (Get-Date))),
[switch]$EnabledOnly,
[switch]$IncludeIPAddress,
[string]$Server,
[switch]$PassThru
)
$ErrorActionPreference = 'Stop'
Import-Module ActiveDirectory
$query = @{
Filter = if ($EnabledOnly) { 'Enabled -eq $true' } else { '*' }
Properties = 'CanonicalName', 'Description', 'DNSHostName', 'LastLogonDate', 'OperatingSystem', 'OperatingSystemVersion', 'PasswordLastSet', 'whenCreated', 'ManagedBy'
}
if ($SearchBase) { $query.SearchBase = $SearchBase }
if ($Server) { $query.Server = $Server }
if ($IncludeIPAddress) { $query.Properties += 'IPv4Address' }
$computers = Get-ADComputer @query
$now = Get-Date
Write-Verbose "Found $(@($computers).Count) computer objects"
$rows = foreach ($pc in $computers) {
$row = [ordered]@{
Name = $pc.Name
DNSHostName = $pc.DNSHostName
Enabled = $pc.Enabled
OperatingSystem = $pc.OperatingSystem
OSVersion = $pc.OperatingSystemVersion
LastLogonDate = $pc.LastLogonDate
DaysSinceLogon = if ($pc.LastLogonDate) { [int]($now - $pc.LastLogonDate).TotalDays } else { $null }
PasswordLastSet = $pc.PasswordLastSet
PasswordAgeDays = if ($pc.PasswordLastSet) { [int]($now - $pc.PasswordLastSet).TotalDays } else { $null }
Created = $pc.whenCreated
Description = $pc.Description
ManagedBy = $pc.ManagedBy
OU = ($pc.CanonicalName -replace '/[^/]+$', '')
DistinguishedName = $pc.DistinguishedName
}
if ($IncludeIPAddress) { $row.IPv4Address = $pc.IPv4Address }
[pscustomobject]$row
}
$rows = @($rows | Sort-Object Name)
$rows | Export-Csv -Path $CsvPath -NoTypeInformation -Encoding UTF8
Write-Host "Exported $($rows.Count) computers to $CsvPath"
if ($PassThru) { $rows }
Parameters
| Parameter | Type | Default | What it's for |
|---|---|---|---|
-SearchBase | string | whole domain | Only include computers under this OU. |
-CsvPath | string | .\ad-computers-<date>.csv | Where to save the CSV. |
-EnabledOnly | switch | — | Skip disabled computer accounts. |
-IncludeIPAddress | switch | — | Add an IPv4Address column. The AD module looks each one up in DNS, so this is a lot slower on big domains. |
-Server | string | — | Domain name or domain controller to query. |
-PassThru | switch | — | Return the rows as objects as well as saving the CSV. |
Run it
The whole domain, saved to a dated CSV in the current folder.
.\Export-ADComputerInventory.ps1Just the workstations, enabled ones only.
.\Export-ADComputerInventory.ps1 -SearchBase 'OU=Workstations,DC=contoso,DC=com' -EnabledOnlyMachines that haven't logged on in 90 days. Your cleanup shortlist.
.\Export-ADComputerInventory.ps1 -EnabledOnly -PassThru | Where-Object DaysSinceLogon -gt 90 | Sort-Object DaysSinceLogon -DescendingHow many of each OS build are still out there.
.\Export-ADComputerInventory.ps1 -PassThru | Group-Object OperatingSystem, OSVersion | Sort-Object Count -Descending | Select-Object Count, NameWhat you'll see
Exported 1,284 computers to C:\Reports\ad-computers-2026-09-29.csv
Name OperatingSystem OSVersion LastLogonDate DaysSinceLogon PasswordAgeDays OU
---- --------------- --------- ------------- -------------- --------------- --
PC-0142 Windows 11 Enterprise 10.0 (26100) 9/26/2026 3 20 contoso.com/Corp/Devices/Laptops
PC-0187 Windows 11 Enterprise 10.0 (22631) 9/12/2026 17 11 contoso.com/Corp/Devices/Laptops
PC-0311 Windows 10 Enterprise 10.0 (19045) 2/3/2026 238 238 contoso.com/Corp/Devices/Desktops
SRV-FS01 Windows Server 2022 ... 10.0 (20348) 9/28/2026 1 14 contoso.com/Servers/File
How it works
- Build the query. Filter on enabled accounts if you asked for it, add a search base and server if you gave them, and request just the properties the report needs. Asking for specific properties instead of
-Properties *keeps big exports fast. - Shape each computer into a row. The script calculates
DaysSinceLogonandPasswordAgeDaysfrom the dates, and trims the computer's own name off its canonical name to get a readable OU path. - Sort and save. Rows are sorted by name and written to CSV as UTF-8, which keeps non-English characters in descriptions intact.
- Hand the rows back if you want them. With
-PassThruyou can filter, group or pipe the results without opening the CSV at all.
Take it further
- Clean up what you find. Once you've got a shortlist of stale machines, this script deletes or disables stale computer accounts with -WhatIf support and a CSV log.
- Compare against Intune or your RMM. Join this CSV to an Intune device export on computer name to find machines that exist in one place and not the other.
- Schedule it. Run it weekly from a scheduled task and keep the files. When someone asks when a machine disappeared, you'll have an answer.
Things that'll trip you up
- Last logon is only accurate to about two weeks. LastLogonDate comes from lastLogonTimestamp, which AD deliberately updates only every 9 to 14 days to save on replication. Great for "has this been gone for months?", useless for "did it log on yesterday?"
- The OS version is the real tell. The OperatingSystem attribute is whatever the machine last reported, and the build number in OperatingSystemVersion is how you tell Windows 11 24H2 (26100) from 23H2 (22631) or Windows 10 22H2 (19045).
- Password age is a second opinion. Domain-joined Windows machines change their account password every 30 days by default. A password that's months old on an enabled machine means it hasn't talked to a DC in a long time, even if something else touched its logon timestamp.
- Opening the CSV in Excel. Excel will happily reformat dates and drop leading zeros. If the columns look odd, use Data > From Text/CSV instead of double-clicking the file.