Monday, January 24, 2011

Dump-DNS

"Red Rover, Red Rover, Send Server1 right over!"

Due to poor DNS management, how many have felt as if they were playing this childrens game when trying to find a single device on the network?  And, all because "that one guy" didn't believe in deleting old static DNS records when a machine was renamed or decomissioned.

It's easy enough to clear out...as long as you have permissions in DNS.  What if you don't, and what if you want to be pro-active?  PowerShell can help your line hold tight as the DNS bully runs into your line.

Lets help him become part of our team, and put him to use with this script:

CODE

#Dump-DNS
#Get the current domain name
$domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().Name
#Create the query string.
$query = "Select IPAddress,OwnerName from MicrosoftDNS_AType WHERE DomainName='$($domain)'"
#Retrieve the DNS data desired
$data = gwmi -namespace "root\MicrosoftDNS" -query $query -ComputerName $domain | Select IPAddress,OwnerName | ? { $_.OwnerName -ne $domain }

$hash = @{}
#Find all duplicated IPs
$data | % {$hash[$_.IPAddress] = $hash[$_.IPAddress] + 1 }
$ips = $hash.GetEnumerator() | ? { $_.value -gt 1 } | Select -Expand Name
$hash.Clear()
#Find all duplicated names
$data | % {$hash[$_.OwnerName] = $hash[$_.OwnerName] + 1 }
$machines = $hash.GetEnumerator() | ? { $_.value -gt 1 } | Select -Expand Name

#Display the data
$data | Select IPAddress,OwnerName,@{Name="Unique";Expression={($ips -notcontains $_.IPAddress -and $machines -notcontains $_.OwnerName)}} | Sort IPAddress

 code: copy : expand : collapse

EXPLANATION

Many people have been using the [System.Net.DNS] class to gather DNS information.  However, this does not always give the most accurate information, and you must request the information using a known IP or Name.  MS, in their infinite wisdom, provided the MicrosoftDNS WMI classes to pull directly from a MS DNS server the full list of DNS information.

Pulling all A Records from a DNS will gather a HUGE list of data.  It will retrieve every single resource record the server knows about.  So, whats the big deal?  Every record that any machine on the network has ever requested, both private and public.  Meaning, every query out to a website that has embeded content from any number of other sites - those are all recorded in the local DNS server.  We don't want those million records, only the private addresses.

To do this, we have to know what the current domain name is.  *Knock*Knock* ".Net, do you know how to do that?"  "Sure, use [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()"

Hence:
$domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().Name
Domain name: Check.

Now, it's time for a query:
$query = "Select IPAddress,OwnerName from MicrosoftDNS_AType WHERE DomainName='$($domain)'"

Simply use the domain name gathered with standard WQL to ... Select the IPAddress and OwnerName properties from the MicrosoftDNS_AType class where the DomainName property is equal to our local domain.  Easy, right?

Domain name: Check.
Query: Check.

Because the MicrosoftDNS classes are not a default WMI set of classes, we must connect into them by specifying the -namespace for PoSh to know where to point.  That would be "root\MicrosoftDNS". 

Using WQL, even though we've specified in our $query to only retrieve IPAddress and OwerName, will return a lot of WMI class information and connection information.  We don't care about that, so lets again select only IPAddress and OwnerName.  Much better.

"But, there is now a false positive for my domain name", you say.  Well, lets not select that either by filtering out selecting only entries where the OwnerName property is not equal to your domain name.

$data = gwmi -namespace "root\MicrosoftDNS" -query $query -ComputerName $domain | Select IPAddress,OwnerName | ? { $_.OwnerName -ne $domain }

Domain name: Check.
Query: Check.
Data: Check.

So, there you have it.  All the DNS A Records in your domain.  Now you just have to export to a CSV, create a new colum, build reports to show duplicate IP's and machine names merge the report with the CSV to show all the duplicates...  Oh, you don't want to do all that extra work?  Ok.  Let's bring PoSh back.

How do we find duplicate entries only?  We'll just use Sort-Object's -duplicate switch or Get-Duplicate.  Wait.  I forgot.  Those don't exist.  Well, we'll have to do it the hard way.

Create a hash table, parse through the $data object already created, and count up the value for each element.  Then, spit out the Name property for any item with a Value over 1.  Sounds easy - looks messy.

The main reason is that a has value must be enumerated in order to select the Name of an entry.  Other wise you would get a list of numbers (values) associated to the entries (names).

Yup.  This has to be done two times:  One time for IPAddress and one time for OwnerName.  With a $hash.Clear() in the middle to do just that.  Clear the entries.

$hash = @{}
#Find all duplicated IPs
$data | % {$hash[$_.IPAddress] = $hash[$_.IPAddress] + 1 }
$ips = $hash.GetEnumerator() | ? { $_.value -gt 1 } | Select -Expand Name
$hash.Clear()
#Find all duplicated names
$data | % {$hash[$_.OwnerName] = $hash[$_.OwnerName] + 1 }
$machines = $hash.GetEnumerator() | ? { $_.value -gt 1 } | Select -Expand Name

Domain Name: Check.
Query: Check.
Data: Check.
Duplicates: Check.
Output: Almost.

The next piece is very straight forward - although not by first appearance.

Here we're going to use Calculated Properties to display a status of True or False based on the $ips and $machines objects created previously.  Calculated Properties are very easy to work with.  They are simply hash tables (notice they are surrounded by @{}) with a Name and Expression of your choosing.  Expression can be any valid PoSh code and does take data from the pipe.

$data | Select IPAddress,OwnerName,@{Name="Unique";Expression={($ips -notcontains $_.IPAddress -and $machines -notcontains $_.OwnerName)}} | Sort IPAddress

Domain Name: Check.
Query: Check.
Data: Check.
Duplicates: Check.
Output: Check.

Done!

Remember.  The key to mastering PowerShell is not in your infalable ability to use Get-Help on every cmdlet over and over, or crutching on Step-Into.  It's research and knowledge of .NET and WMI.  The two greatest pieces of the PoSh puzzle.  Learn them well and you'll keep adding more members to your line.

"Red Rover, Red Rover, send...anything right over!"

0 comments: