Jump to page sections
Often as a Windows system administrator, you will want to get a list of computer/host names from (an OU in) Active Directory. Here are a few ways of doing it with PowerShell, using System.DirectoryServices.DirectorySearcher ([adsisearcher]) with an LDAP query, Get-ADComputer from the Microsoft ActiveDirectory module cmdlets and Get-QADComputer from Quest ActiveRoles.

These MS AD cmdlets that Get-ADComputer and Get-ADObject are part of are installed as a feature under the category "Remote Server Administration Tools" (RSAT) from Server Manager on Windows Server 2008 R2 and can be downloaded for Windows 7. There's more information about Remote Server Administration Tools here and even more RSAT info here.

All the code in this article should be compatible with PowerShell version 2, which comes by default with Server 2008 R2 and Windows 7 (but you can also upgrade to PSv3 or v4 on those operating systems if you want).

See this article for getting usernames from AD.

Using Get-ADComputer

Firstly, unless you're on PSv3 or later (autoloads modules when you use cmdlets), you need to import the ActiveDirectory module after having installed it as a feature. It's under "RSAT" in Server Manager -> Features -> Add feature -> Remote Server Administration Tools. Also see Get-Help Add-WindowsFeature and Get-Help Get-WindowsFeature.
PS C:\prog\PowerShell> Import-Module ActiveDirectory
PS C:\prog\PowerShell>

Using the Microsoft active directory module requires that AD Web Services are running on the domain controller. This is non-default for older 2003 (R2) domain controllers, but can be installed. You can use the Quest cmdlets, which are also briefly described in this article, against domain controllers without ADWS.

Also, don't forget to look at '''Get-Help Get-ADComputer -Full'''! Here is a web version of that help.

To just dump every single computer in a domain, and possibly redirect to a file by adding " | Set-Content -enc utf8 -Path computers.txt", or just " > computers.txt". you can do it like this:
PS C:\prog\PowerShell> Get-ADComputer -Filter 'ObjectClass -eq "Computer"' | Select -Expand DNSHostName
2008R2ESXI2.svendsen.local
2008r2esxi.svendsen.local
srv2003r2esxi.svendsen.local
siemens.svendsen.local
WIN7ESXI.svendsen.local
winxpesxi.svendsen.local
win2k.svendsen.local
vista64esxi.svendsen.local
server2003.svendsen.local
xptanket.svendsen.local
SERVER2008.svendsen.local
esxi.svendsen.local
winxpssd.svendsen.local

Technically, that filter is (bloody well should be) redundant. You can also use -like instead of -eq. I'm having some trouble with wildcards in combination with text, though (such as in 'ObjectClass -like "*ompute*"'). Turns out the syntax with wildcards is quirky like that.

If you don't use Select-Object and pipe to Export-Csv, you will get a CSV file with the default values that are retrieved ("... | Export-Csv -NoTypeInformation -Encoding UTF8 -Path somefile.csv").

You can also redirect to a file, and specify encoding, by piping to Set-Content rather than using the redirection operator ">", which does the same thing as Out-File (Out-File allows you to specify -Encoding). When working with text/strings, they will work equivalently, and since we're using Select-Object with the parameter -ExpandProperty ("-Exp" in the examples), we get strings. There's a lot more to be said about this, but it does not belong in this article.

Getting Just The Host Name

Above I selected the property "DNSHostName". If you use the property "Name" instead, you will get just the host name:

PS C:\prog\PowerShell> Get-ADComputer -Filter * | Select -Expand Name
2008R2ESXI2
2008R2ESXI
SRV2003R2ESXI
SIEMENS
WIN7ESXI
WINXPESXI
WIN2K
VISTA64ESXI
SERVER2003
XPTANKET
SERVER2008
esxi
WINXPSSD

I also used a different filter here. Basically, when you use the Get-ADComputer cmdlet, there should be built-in filtering that targets computer objects, so you can use any property and a wildcard match with "-like '*'" or similar. I experimented with just a wildcard ("*") to target anything, and it seems to work as intended.

Getting A Specific Computer

Here I get the DistinguishedName and LastLogonTimestamp for a specific computer. You can replace the last "Format-Table -AutoSize" part with Export-Csv in all these examples.

PS C:\> Get-ADComputer -Filter { Name -eq 'server2012' } -Propert LastLogonTimestamp | 
    Select DistinguishedName, LastLogonTimestamp | Format-Table -AutoSize

DistinguishedName                                       LastLogonTimestamp
-----------------                                       ------------------
CN=SERVER2012,OU=Servers,OU=Hjemme,DC=svendsen,DC=local 131063105680111740

PS C:\> $var = Get-ADComputer -Filter { Name -eq 'server2012' }
PS C:\> $var.DistinguishedName
CN=SERVER2012,OU=Servers,OU=Hjemme,DC=svendsen,DC=local

PS C:\> $comp = Get-ADComputer server2008
PS C:\> $comp.SamAccountName
SERVER2008$

To see what OU your computer is in, here's a cute, little one-liner where we use Get-ADComputer and the $env:ComputerName environment variable via its PowerShell provider (equivalent to %COMPUTERNAME% in cmd.exe/"DOS"), include all properties with the parameter "-Propert *" (you could just specify "-Propert DistinguishedName", too, and it would be included in addition to the default properties), and then enclose this expression in parentheses, causing an object to be returned, and for us to index into its "DistinguishedName" property. Remove that part and pipe the object to " | Format-List *" (or "fl *" for short) to see all properties returned.

PS C:\> (Get-ADComputer -Identity $env:COMPUTERNAME -Propert *).DistinguishedName CN=2008R2ESXI2,OU=Domain Controllers,DC=svendsen,DC=local

Getting Server Or Client Operating Systems Only

To get server operating systems only, I currently don't know of any (much) better way than filtering on the "OperatingSystem" text containing "*Windows*Server*". Note that the wildcard in the middle is necessary for some Windows version that have a (R) for registered trademark between the words. Seen with "Windows Serverr 2008 Standard" in the output below. You can also use a script block for the filter, which helps avoid nested quoting, and can be done like this:


PS C:\> Get-ADComputer -Filter { OperatingSystem -Like '*Windows*Server*' } -Properties OperatingSystem | >> Select Name, OperatingSystem | Format-Table -AutoSize >> Name OperatingSystem ---- --------------- 2008R2ESXI2 Windows Server 2008 R2 Standard 2008R2ESXI Windows Server 2008 R2 Standard SRV2003R2ESXI Windows Server 2003 SERVER2003 Windows Server 2003 SERVER2008 Windows Serverr 2008 Standard SERVER2012RC Windows Server 2012 Release Candidate Datacenter PS C:\>

As we can see, this works for Server 2003 through Server 2012 versions of Windows Server operating systems.

To do the opposite, and only get client operating systems, you can use -NotLike instead of -Like. If you're in an environment with AD-joined Linux computers, NAS, etc., you will also see these here.
PS C:\> Get-ADComputer -Filter { OperatingSystem -NotLike '*Windows*Server*' } -Properties OperatingSystem | 
>> Select Name, OperatingSystem | Format-Table -AutoSize
>>

Name        OperatingSystem
----        ---------------
SIEMENS     Windows XP Professional
WIN7ESXI    Windows 7 Professional
WINXPESXI   Windows XP Professional
WIN2K       Windows 2000 Professional
VISTA64ESXI Windows VistaT Ultimate
XPTANKET    Windows XP Professional
esxi        unknown
WINXPSSD    Windows XP Professional
VMWAREWIN7  Windows 7 Professional
SS-WIN7     Windows 7 Professional
WIN8VM      Windows 8 Pro

PS C:\>

Getting Service Pack Version

With Quest ActiveRoles' Get-QADComputer, the property is named "OSServicePack", while with Get-ADComputer from Microsoft, it's named "OperatingSystemServicePack".
PS C:\> Get-ADComputer -Filter 'name -eq "2008r2esxi"' -Properties OperatingSystemServicePack
   | select Name, OperatingSystemServicePack | ft -AutoSize

Name       OperatingSystemServicePack
----       --------------------------
2008R2ESXI Service Pack 1

Targeting A Specific OU

Here I target the OU "svendsen.local/Hjemme/Clients" using the -SearchBase parameter and the LDAP distinguished name for this OU.
PS C:\> Get-ADComputer -SearchBase 'OU=Clients,OU=Hjemme,dc=svendsen,dc=local' -Filter '*' | Select -Exp Name
XPTANKET
WINXPSSD
SIEMENS
WIN7ESXI
WINXPESXI
WIN2K
VISTA64ESXI

This OU:

Getting computers from AD example

Using An LDAP Query

Using an LDAP query effectively similar to the filter in the first example, you can do something like this:
PS C:\prog\PowerShell> Get-ADComputer -LDAPFilter '(objectClass=Computer)' | Select -Expand Name
2008R2ESXI2
2008R2ESXI
SRV2003R2ESXI
SIEMENS
WIN7ESXI
WINXPESXI
WIN2K
VISTA64ESXI
SERVER2003
XPTANKET
SERVER2008
esxi
WINXPSSD

Using An LDAP Query And Get-ADObject

You can use the same filter with Get-ADObject:
PS C:\prog\PowerShell> Get-ADObject -LDAPFilter '(objectClass=Computer)' | Select -Expand Name
2008R2ESXI2
2008R2ESXI
SRV2003R2ESXI
SIEMENS
WIN7ESXI
WINXPESXI
WIN2K
VISTA64ESXI
SERVER2003
XPTANKET
SERVER2008
esxi
WINXPSSD

Filtering Using Wildcards And Where-Object

Here I filter on computer names that start with "2008":
PS C:\prog\PowerShell> Get-ADComputer -Filter 'SamAccountName -like "2008*"' | Select -Exp Name
2008R2ESXI
2008R2ESXI2

And here I filter on computer names that contain "2008":

PS C:\prog\PowerShell> Get-ADComputer -Filter 'SamAccountName -like "*2008*"' | Select -Exp Name
2008R2ESXI2
2008R2ESXI
SERVER2008

Of course you can also filter in the pipeline using PowerShell's Where-Object/Where, but it will be less efficient, which matters with expensive queries:

PS C:\prog\PowerShell> Get-ADComputer -Filter * | Where { $_.Name -imatch '2008' } | Select -Exp Name
2008R2ESXI2
2008R2ESXI
SERVER2008

Getting Active Or Inactive Computer Objects

When someone from management requests a list of active clients, they often elegantly omit to define what defines an "active client". What I currently do is to filter on the "Enabled" property being true, and that the computer's "LastLogonTimestamp" value has been updated within the last 90 or 180 days (I tend to send both numbers).

The LastLogonTimestamp you get from Get-ADComputer is apparently a "FileTime" type date. You can use [datetime]::FromFileTime() to convert it to a regular DateTime class object, and then perform calculations/comparisons.

Note from 2021-10-13: By the way, a helpful way to think about date logic is to keep in mind that "now" is always the highest/largest/latest possible time, so when comparing dates, "now" (the epoch number) is always the greatest value possible, forever. It can make your head spin a bit, like infinity tends to do. If you want to experience the latest time ever in the history of our universe, you can simply enter the present moment. Typically by focusing/anchoring on your breathing and feeling the body from the inside, based on mindfulness teachings. I digress.

Here's an example for getting active/enabled computer objects that have been active in the last 90 days (+/- 9-14 days, unless you changed the LastLogonTimestamp sync interval in AD - some set it to sync every day).

(Get-ADComputer -Filter * -Properties LastLogonTimestamp -SearchBase 'OU=Servers,dc=ad,dc=example,dc=com' | 
    Where-Object { $_.Enabled -and [datetime]::FromFileTime($_.LastLogonTimestamp) -gt `
        [datetime]::Now.AddDays(-90) }).Count
3748

There I check that it's Enabled (the same as -eq $true) and then that the last logon was after 90 days ago.

To do the opposite and get inactive/disabled computer objects, change to see if the "Enabled" boolean (true or false) value is $false OR that the last logon was more than 90 days ago. Of course you can change the number from 90 to something else. There's also .AddHours(), .AddMinutes(), etc. for more fine-grained filtering (rarely needed, I'd expect).
(Get-ADComputer -Filter * -Properties LastLogonTimestamp -SearchBase 'OU=Servers,dc=ad,dc=example,dc=org' | 
    Where { $_.Enabled -eq $false -or [datetime]::FromFileTime($_.LastLogonTimestamp) -lt `
        [datetime]::Now.AddDays(-90) }).Count
233

I recommend caching the date for a performance gain in advance:

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

... and then compare to that in the Where-Object filter.

To preserve the results for exporting to CSV or similar, simply assign the pipeline to a variable:
$OldComputers = Get-ADComputer .........
$OldComputers.Count
$OldComputers | Export-Csv ..........

Using DirectorySearcher/AdsiSearcher

If this is the absolute best way of doing this, I do not know, but it seems quite likely that it's no crime against humanity given my experimentation and inspection of the (computer) objects returned by DirectoryServices.DirectorySearcher. But beware of large ADs where the query is indeed expensive.

Here's the output from my home lab's AD (these indeed are all the computers in the Active Directory):

PS C:\prog\PowerShell> .\Get-Computers-DS.ps1
2008R2ESXI2
2008R2ESXI
SRV2003R2ESXI
SIEMENS
WIN7ESXI
WINXPESXI
WIN2K
VISTA64ESXI
SERVER2003
XPTANKET
SERVER2008
esxi
WINXPSSD

Basically with DirectoryServices.DirectorySearcher, the real magic is in the LDAP query, and you might have to inspect returned objects to see what their properties are. The LDAP query is specified via the Filter property of the directory searcher object, and in this case it is "(objectClass=Computer)".

Also be aware that the property you index is case-sensitive, and that they're all listed as lower-case when you look at the returned System.DirectoryServices.ResultPropertyCollection object.

Here is a ready-made script that'll just dump every single computer from the AD of the first available domain controller, using Windows' method for detecting domain controllers:

#Set-StrictMode -Version Latest
#$ErrorActionPreference = 'Stop'

#$DirSearcher = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ArgumentList ([adsi]'')
$DirSearcher = [adsisearcher]""
$DirSearcher.Filter = '(objectClass=Computer)'

# These properties are part of a DirectoryServices.ResultPropertyCollection
# NB! These properties need to be all lowercase!
$DirSearcher.FindAll().GetEnumerator() | ForEach-Object { $_.Properties.name }

I should also mention that there's a type accelerator in PowerShell v2 (I don't know about v1.x), which allows you to skip a few steps like this (notice the two quotes after the type accelerator):

PS C:\> $DirSearcher = [adsisearcher]""
PS C:\>

Targeting A Specific OU With ADSI/LDAP/DirectorySearcher

To target a specific OU/container, change the following line in the script above (this example is valid in my home lab):
$DirSearcher = New-Object -TypeName DirectoryServices.DirectorySearcher -ArgumentList ([adsi]'LDAP://OU=Clients,OU=Hjemme,DC=svendsen,DC=local')

Here I prepended "LDAP://" to the distinguished name of the OU, enclosed it in a string and cast it to an ADSI object, which was then used to to create a System.DirectoryServices.DirectorySearcher object. This targets the OU svendsen.local/Hjemme/Clients. Using "System." first when declaring the object type is optional.

If you were to use the [adsisearcher] type accelerator, it would look like this:
$DirSearcher = [adsisearcher][adsi]'LDAP://OU=Clients,OU=Hjemme,DC=svendsen,DC=local'

Parsing dsquery.exe Output

If, for some reason, you're stuck parsing dsquery.exe output, here's just a random example of me retrieving server names for everything under a certain OU (made a bit anonymous: OU=Servere,OU=Boxes,dc=ad,dc=company,dc=no / ad.company.no/Boxes/Servere):
PS C:\> $dsq = dsquery computer -limit 0

PS C:\> $dsq[0]
"SERVERDCNAME,OU=Domain Controllers,DC=ad,DC=company,DC=no"

PS C:\> $SrvStrings = $dsq | where { $_ -like '*OU=Servere,OU=Boxes,dc=ad,dc=company,dc=no*' }

PS C:\> $SrvStrings[0] "CN=X2-443-OS0020,OU=Servere,OU=Boxes,DC=ad,DC=company,DC=no" # You can use "OU=" (or whatever is appropriate) rather than "\w{1,2}=" - # it's an attempt at being flexible - and to allow for commas in CNs PS C:\> $Servers = $SrvStrings | %{ if ($_ -match '^"CN=(.+?),\s*\w{1,2}=') { $matches[1] } } PS C:\> $Servers[0] X2-443-OS0020 PS C:\>

Using Quest ActiveRoles Get-QADComputer

This example, and the script below, uses Get-QADComputer from Quest ActiveRoles Management Shell - a very handy and seemingly mature set of cmdlets for working with Active Directory. They used to be free (you can download the last free version from my link) - and they also work against ADs running on Server 2003 without Active Directory Web Services. Now that this article is almost a decade old as I'm moving my wiki/blog on 2021-10-13, this is mostly obsolete, but it's still a godsend for people working against legacy environments.
PS C:\powershell> Get-QADComputer -SearchRoot internal.domain.com/someOU/someSubOU -SizeLimit 0 | %{ $_.Name }
comp1
comp2
comp3
comp4
comp5
PS C:\powershell> Get-QADComputer -SearchRoot internal.domain.com/someOU/someSubOU -SizeLimit 0 | %{ $_.Name } > foo.txt
PS C:\powershell> (Get-Content .\foo.txt).Count
5
PS C:\powershell> (Get-Content .\foo.txt)[1,-1]
comp1
comp5

Rather than " | %{ $_.Name }", you can probably more conventionally use " | Select-Object -ExpandProperty Name". You can also use " | Out-File foo.txt" instead of " > foo.txt" - or Add/Set-Content. (Do we love or hate choices? They sort of suck when documenting.)

Random Get-QADComputer Sample

This script will get some data from AD and redirect it to a file. Easily adapted to other needs. This will retrieve the fields Name, OSName, OSVersion, OSServicePack and LastLogonTimeStamp. See the sample output below for what it looks like. I find sorting on LastLogonTimeStamp useful (read about the LastLogonTimestamp attribute and what it was designed for here on the Technet blog and here on MSDN (LastLogonTimestamp)). I use this script to manually inspect and just remove "old" computers if I'm going to run a command against multiple online computers, for instance.

More conventionally you will probably want to export to CSV or process objects in memory on the command line. Check Get-Help Export-Csv -Full. Basically, you'd replace the Format-Table and Out-File with something like: Get-QADComputer ... bla bla | Export-Csv -Encoding UTF8 -Delimiter ";" -NoTypeInfo computers.csv

Download

*Get-all-ad-computers.ps1.txt (right-click, "save as" and rename to .ps1). Source code below.

Sample Output

Again, now in 2021-10-13, almost a decade after this was written, this is an obsolete/outdated method of doing things mostly. Certainly parsing this output is pretty ridiculous when you can use CSV and objects in memory before you export, if you are indeed creating a report. If you're streamlining or automating some process, obviously you will not use a file as an intermediary step... I do not have access to a test environment for Quest currently so this will remain unfixed, but now at least commented more wisely.
PS C:\powershell> .\get-all-ad-computers.ps1

cmdlet get-all-ad-computers.ps1 at command pipeline position 1
Supply values for the following parameters:
OutputFile: test.txt
Running Get-QADComputer...
Start time:  05/19/2011 23:36:14
End time:    05/19/2011 23:36:28
Output file: test.txt
PS C:\powershell> notepad .\test.txt
PS C:\powershell> type .\test.txt | Select-Object -first 10

Name            OSName                  OSVersion  OSServicePack          lastLogonTimestamp
----            ------                  ---------  -------------          ------------------
comp1           Windows XP Professional 5.1 (2600) Service Pack 2         17.03.2010 03:07:14
comp2           Windows XP Professional 5.1 (2600) Service Pack 2         18.03.2010 08:04:52
comp3           Windows XP Professional 5.1 (2600) Service Pack 3         18.03.2010 12:27:59
comp4           Windows XP Professional 5.1 (2600) Service Pack 3         18.03.2010 12:35:31
comp5           Windows XP Professional 5.1 (2600) Service Pack 3         18.03.2010 14:07:31
comp6           Windows XP Professional 5.1 (2600) Service Pack 3         19.03.2010 11:46:23
comp7           Windows XP Professional 5.1 (2600) Service Pack 3         20.03.2010 19:50:29

Getting Just The Computer Names From That Output

NB. Again, 10 years down the line, this seems very suboptimal/dumb. Do not use a file as an intermediary step like this. Use the object structure of PowerShell for what it's worth. Just scroll down to the example below and adapt it to your needs. I have grown wiser about what people want/need. This article tailors for a strange audience indeed seen in retrospect.

I figure you might want to get just the computer names from that output, and of course you can just run a query where you pipe Get-QADComputer to Select-Object -ExpandProperty Name, but I'll throw in a little example of getting the first "word" (non-whitespace characters in sequence) on a line. In this case you will have to remove the headers manually with notepad or another editor (or Get-Content and Select-Object -First/-Last/-Skip if you like).

Using Split

Read more about the PowerShell split operator here. (note 2021-10-13: That article is actually pretty darn informational)

PS E:\temp> (gc .\computers.txt)[0,-1]
comp1           Windows XP Professional 5.1 (2600) Service Pack 2         17.03.2010 03:07:14
comp7           Windows XP Professional 5.1 (2600) Service Pack 3         20.03.2010 19:50:29

PS E:\temp> gc .\computers.txt | %{ ($_ -split '\s+')[0] }
comp1
comp2
comp3
comp4
comp5
comp6
comp7

Using A Regular Expression

Note made 2021-10-13: Again, more stupid stuff from way back in the past. No longer really relevant. Please do not use a file as an intermediary step. See the Get-QADComputer query examples and adapt them to your needs.

Read more about PowerShell regular expressions here.

PS E:\temp> (gc .\computers.txt)[0,-1]
comp1           Windows XP Professional 5.1 (2600) Service Pack 2         17.03.2010 03:07:14
comp7           Windows XP Professional 5.1 (2600) Service Pack 3         20.03.2010 19:50:29

PS E:\temp> gc .\computers.txt | %{ if ($_ -match '^(\S+)') { $matches[1] } }
comp1
comp2
comp3
comp4
comp5
comp6
comp7

Script Code

Note 2021-10-13: This is just an example. Adapt it as needed.
param([Parameter(Mandatory=$true)][string] $OutputFile)

$StartTime = Get-Date

# Check that the Quest.ActiveRoles.ADManagement snapin is available if (-not (Get-PSSnapin Quest.ActiveRoles.ADManagement -registered -ErrorAction SilentlyContinue)) { 'You need the Quest ActiveRoles AD Management Powershell snapin to use this script' "www.quest.com`n" 'Please install and register this snapin. Exiting...' exit 0 } # Add the snapin and don't display an error if it's already added. # If it's not registered, this will be handled above, so this should succeed. Add-PSSnapin Quest.ActiveRoles.ADManagement -ErrorAction SilentlyContinue

'Running Get-QADComputer...'

Get-QADComputer -SizeLimit 0 -IncludedProperties LastLogonTimeStamp | Select-Object Name, OSName, OSVersion, OSServicePack, LastLogonTimeStamp | #Where-Object { ($_.OSVersion -match '^5\.1') } | # Filter on XP/2003 Sort-Object @{Expression={$_.LastLogonTimeStamp};Ascending=$true} | #Export-Csv -Encoding UTF8 -Delimiter ";" -NoTypeInfo computers.csv Format-Table -AutoSize -Property Name, OSName, OSVersion, OSServicePack, LastLogonTimeStamp | Out-File $OutputFile @" Start time: $StartTime End time: $(Get-Date) Output file: $OutputFile "@


''Keywords: Active Directory, AD, Powershell, Exporting, CSV, LastLogonTimeStamp, LastLogon, Quest ActiveRoles cmdlets, Get-ADComputer, DirectorySearcher, LDAP query for computers'' Powershell      Windows      AD          All Categories

Google custom search of this website only

Minimum cookies is the standard setting. This website uses Google Analytics and Google Ads, and these products may set cookies. By continuing to use this website, you accept this.

If you want to reward my efforts