PowerShell AD Query: Find Computers in Active Directory

PowerShell AD Query: Find Computers in Active Directory

PowerShell Tips Editor 3 min read
PowerShell AD Query: Find Computers in Active Directory

How many Windows 10 machines remain in your domain before the end-of-life deadline? Which computers in the Servers OU have not checked in for 90 days and might be decommissioned candidates? These are questions that take minutes to answer with PowerShell active directory computers queries using Get-ADComputer. This post covers filtering by OS version, OU scope, last logon date, identifying stale accounts, and exporting a full computer inventory to CSV for hardware refresh planning.

Get All Computers in AD

Import the Active Directory module and retrieve all computer objects. Be explicit about which properties you need — not all properties are returned by default:

Import-Module ActiveDirectory

# Basic computer list
Get-ADComputer -Filter * | Select-Object Name, DNSHostName, Enabled | Format-Table -AutoSize

# Count computers by enabled status
Get-ADComputer -Filter * | Group-Object Enabled | Select-Object Name, Count
Name  Count
----  -----
True   1842
False    93

Filter by Operating System

Use the -Filter parameter with LDAP-style attribute queries, or -LDAPFilter for complex filters. Combine with -Properties to return the OS attributes:

# Find Windows 10 computers
Get-ADComputer -Filter { OperatingSystem -like "*Windows 10*" } `
    -Properties OperatingSystem, OperatingSystemVersion |
    Select-Object Name, OperatingSystem, OperatingSystemVersion |
    Sort-Object Name

# Count by OS version — upgrade planning dashboard
Get-ADComputer -Filter * -Properties OperatingSystem |
    Group-Object OperatingSystem |
    Select-Object Count, Name |
    Sort-Object Count -Descending |
    Format-Table -AutoSize
Count Name
----- ----
 1234 Windows 10 Enterprise
  421 Windows 11 Enterprise
  187 Windows Server 2019 Standard

Get Computers in a Specific OU

Use -SearchBase with the OU’s distinguished name to scope the query. Use -SearchScope Subtree (default) for all nested OUs, or OneLevel for direct children only:

$ou = "OU=Servers,OU=Corporate,DC=corp,DC=local"

Get-ADComputer -Filter * -SearchBase $ou -SearchScope Subtree `
    -Properties OperatingSystem, OperatingSystemVersion, LastLogonDate |
    Select-Object Name, OperatingSystem, LastLogonDate |
    Sort-Object LastLogonDate -Descending

Find Stale Computer Accounts

Computers that have not authenticated against the domain for an extended period are candidates for cleanup. Use LastLogonDate (which is replicated) rather than LastLogon (which is not):

$cutoffDate = (Get-Date).AddDays(-90)

$staleComputers = Get-ADComputer -Filter { Enabled -eq $true } `
    -Properties LastLogonDate, OperatingSystem |
    Where-Object { $_.LastLogonDate -lt $cutoffDate -or $_.LastLogonDate -eq $null }

Write-Host "Stale computers (no logon in 90+ days): $($staleComputers.Count)"
$staleComputers | Select-Object Name, LastLogonDate, OperatingSystem |
    Sort-Object LastLogonDate | Format-Table -AutoSize

Get Computer Last Login

The most accurate last-logon time requires querying every domain controller and taking the most recent value. The LastLogonDate attribute is a replicated approximation (replicated every 14 days):

function Get-ComputerLastLogon {
    param([string]$ComputerName)

    $domainControllers = Get-ADDomainController -Filter *

    $logonTimes = foreach ($dc in $domainControllers) {
        $comp = Get-ADComputer -Identity $ComputerName -Server $dc.Hostname `
            -Properties LastLogon -ErrorAction SilentlyContinue
        if ($comp.LastLogon) {
            [DateTime]::FromFileTime($comp.LastLogon)
        }
    }
    $logonTimes | Sort-Object -Descending | Select-Object -First 1
}

$lastLogon = Get-ComputerLastLogon -ComputerName "PC001"
Write-Host "PC001 last authenticated: $lastLogon"

Export Computer Inventory to CSV

Build a comprehensive computer inventory report for asset management or upgrade planning:

$reportPath = "C:\Reports\ComputerInventory_$(Get-Date -Format 'yyyyMMdd').csv"

Get-ADComputer -Filter * -Properties Name, DNSHostName, OperatingSystem,
    OperatingSystemVersion, LastLogonDate, Enabled, Description,
    'msDS-LastSuccessfulInteractiveLogonTime' |
    Select-Object Name, DNSHostName, OperatingSystem, OperatingSystemVersion,
        LastLogonDate, Enabled, Description,
        @{N='OU'; E={ ($_.DistinguishedName -split ',',2)[1] }} |
    Export-Csv -Path $reportPath -NoTypeInformation

Write-Host "Inventory exported: $reportPath ($( (Import-Csv $reportPath).Count ) records)"

Common Errors and Fixes

  • Properties like OperatingSystem not returned by default — use -Properties. Get-ADComputer -Filter * only returns a subset of attributes by default (Name, DistinguishedName, Enabled, etc.). Requesting OperatingSystem, LastLogonDate, or any other attribute requires explicitly listing it in -Properties. Use -Properties * to return everything, but note this is much slower on large domains.
  • LastLogonDate replication delay gives stale results. LastLogonDate is replicated across DCs every 14 days by default, so a computer that logged in yesterday may show a logon date from two weeks ago. For accurate last-logon data, query all domain controllers and take the maximum value as shown in the Get-ComputerLastLogon function above.

Related Cmdlets / See Also

Wrapping Up

Get-ADComputer with targeted -Filter expressions and explicit -Properties lists is your tool for AD computer inventory, stale account cleanup, and OS-based upgrade planning. Always request only the properties you need for performance, use LastLogonDate for day-to-day checks, and query all DCs for precise last-authentication times.

Send-Item -To