Getting computer names from AD using Powershell

From Svendsen Tech Powershell Wiki
Jump to: navigation, search

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 Windows 7. There's more information about this here and here.

See this article for getting usernames.

Contents






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 (under RSAT in Server Manager -> Features -> Add feature -> Remote Server Administration Tools):

PS C:\prog\PowerShell> Import-Module ActiveDirectory
PS C:\prog\PowerShell>



Also, don't forget to look at Get-Help Get-ADComputer -Full!

To just dump every single computer in a domain, and possibly redirect to a file by adding " > 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*"').

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. 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 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". 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:

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:\>

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:

Get-computers-from-ad-ad-screenshot.png

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 on large queries:

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

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.

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.

For your convenience, 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 System.DirectoryServices.DirectorySearcher([adsi]'')
$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 DirectoryServices.DirectorySearcher([adsi]'LDAP://OU=Clients,OU=Hjemme,DC=svendsen,DC=local')

So 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"

PS C:\> $Servers = $SrvStrings | %{ if ($_ -match '^"CN=([^,]+)') { $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 - a very handy and seemingly mature set of cmdlets for working with Active Directory. They are free - and also work against ADs running on Server 2003 without Active Directory Web Services.

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 it here and here). 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. 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

A little ninja tip, especially if you're preparing CSV reports for non-technical people to read, is to use a semicolon as the delimiter rather than a comma as this makes it load properly divided into columns automatically, in Excel, whereas with a comma I have to import it manually through the wizard. Explaining how to do this (possibly with 3-4 different versions of Excel) is inconvenient, and a lot of people just won't do it; they'll just double-click the CSV file and assume what they're looking at is a proper representation of the data, even if it's all in one column, with quotes intact...

Download

Sample Output

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

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.

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

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

param([Parameter(Mandatory=$true)][string] $OutputFile)

$StartTime = Get-Date

# Check that the Quest.ActiveRoles.ADManagement snapin is available
if (!(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

Personal tools