Determining AD Site of Computer Objects

Active Directory is awesome. It is used to store many things about many things, and most of the time that comes in really handy. Recently a client asked, if it was possible to write a script that will discover all computer objects in a given AD site.

The short answer is yes, of course.

Then, there are many ways to do this – but querying Active Directory for some computer object attribute is not one of them. Computer-to-site mappings are transient and this type of information isn’t stored in AD. It is rather being determined at the time the operating system needs to locate itself on the network, relative to AD topology.

Potential ways to approach the task

Write a script that queries AD site objects, AD site subnet objects, grabs a list of computer objects from AD, then resolves computer names to IP addresses, and finally compares IP information from DNS with IP information from AD site subnets. Sounds too complicated.

Write a script that grabs a list of computer objects from AD, then resolves computer names to IP addresses, and finally dumps this information to a text file. Then you import the text file, and knowing your network’s IP addressing scheme you can probably figure what’s where using Excel. Sounds too low-tech.

Write a script (or a VB.NET app) that will do what the operating system does when it needs to find out its location – call Windows API. More specifically, netapi32.dll has a function called DsAddressToSiteNames, which as the name implies, resolves a given IP address to an AD site name.

Same as #3 but using nltest.exe

Number 3 peaked my interest. I wanted to completely offload the footwork to the Active Directory and not reinvent any wheels. #3 is the only option that determines site location of the computer THE SAME WAY Windows OS does, which also factors in how well AD sites and subnets are defined/maintained.

The code

I’ll have to admit that programming unmanaged, low-level OS API function calls and dealing with buffers and pointers is not my strong skill, so I had to look around. Eventually I found something that looked like what I was looking for, and adapted it further to suit my specific needs. Code below uses VB.NET syntax, and I use Visual Studio Express 2012 IDE.

You can get the full Visual Studio project from the Downloads page. Feel free to modify and redistribute but use at your own risk.

This next sizeable piece of code defines external API functions and data structures that we are going to call after computer names are obtained from AD, and IP addresses are resolved in DNS. This code was adapted from the link referenced (Thanks Mattias and Brian for sharing).

Simple as that… That’s all it takes to bind to an Active Directory – one line of code. Of course, we could make it more complex, as the user for input (domain name and/or credentials, etc) but for illustrative purposes we will keep it in line with assumption that this code is running on a domain-joined PC with administrative credentials (in theory admin isn’t needed to query AD so this should work with plain user account as well).

This piece of code does a lot more work. It calls Active directory, with LDAP string that finds all computer objects that ARE NOT domain controllers. Then we go into a loop through the result set and 1) GetIpFromHostname and 2) GetSite. This logic makes assumption that domain-joined computers come online occasionally and register their DNS names dynamically. Detecting sites will fail for laptops that have been offline for longer than DNS scavenging interval, and names of which cannot be resolved in internal DNS.

Querying IP address from DNS is simply beautiful in managed code. We are making assumptions here that 1) computers do not have static records that associate more than one IP address with a computer name, and 2) computers aren’t multihomed. If these assumptions are not true, you may have to add some logic here to deal with IP address arrays that are potentially longer than one address.

Finally, the secret sauce. GetSite. This is the code that makes the call into Windows API and performs IP to AD site name resolution. This function uses hardcoded FQDN DNS name of a domain controller that will be used to resolve site names. You can either type in FQDN of your closest domain controller, or using similar approach shown above, query the list of all domain controllers, determine the closest domain controller relative to IP address of your machine, and then dynamically select a domain controller. This way this code would be completely free of any AD-related hardcoding; a thing of beauty.

Results

Run adsibrowser.exe and redirect output to a text file (adsibrowser.exe > c:\output.txt). The resulting text file can be imported into Excel and filtered by AD site name, very quickly and to the point.

What if you don’t want to code

Then use a shortcut, in command line or powershell (on Windows Server 2012):

nltest /dsgetsite

This will fetch the current site of the machine you are on, using the same API call as described in the code above.

nltest /dsaddresstosite:x.x.x.x

This will resolve any IP address on the network to a matching AD site as per sites and subnets definition.

It’s that simple, but if you need more firepower then VB.NET saves the day. By the way, another way to do this is use VB.NET to query and manipulate AD data, but instead of writing complex API calls, you could just shell out and call NLTEST…

No Responses to "Determining AD Site of Computer Objects"

[…] This code sample takes computer name variable and does the WMI related work. You would need to query computer objects from the network – Active Directory, for example. Or you could feed a text file. For a sample of how to connect to and query Active Directory, see this post. […]