Hey, Scripting Guy! We are having name resolution issues at work. It is so bad that when my wife calls me, the guy in the other cube gets the call. This manifests itself in concrete ways as well. Opening Office Outlook seems to take forever; going to internal Web sites takes a long time; trying to connect to an Office SharePoint site is about impossible. To make matters worse, the pointy headed boss (PHB) just spent a small fortune on buying new switches. The project was supposed to “make the network fast,” but he never bothered to talk to us first. After five minutes with Netmon, it was obvious what was happening. Well, the good news is after we get DNS fixed, we really will have a fast network. The first thing I want to check are the A (host name) records. You got a script to do that?
– JE
Hi JE,
A person after my own heart! I wrote the book on Netmon. Literally. Before I got into scripting, I used to do lots of network stuff. By the way, did you know we released Network Monitor (Netmon) 3.2 not too long ago? DNS problems often masquerade as network problems because we often need to find the host providing the information. If we only have a name, we need to translate the name into an IP address, but if it takes a long time to make that translation, even the fastest network in the world will seem like an old fashioned dial-up modem.
This week we are talking about DNS. For more information about DNS, you can refer to the Windows Server 2008 Networking and Network Access Protection (NAP) book in the Windows Server 2008 Resource Kit. There is also a chapter on scripting network services in the Windows PowerShell Scripting Guide. A number of good VBScript examples are in the Script Center Script Repository. The WMI DNS provider is documented here on MSDN. |
Because you want to check out A records, take a look at the GetDnsARecords.ps1 script. There is a VBScript version of this script in the Script Center Script Repository.
GetDnsARecords.ps1
Function Get-DnsServer { Begin {Write-Host -ForeGroundColor Cyan "Obtaining local DNS Server information..."} Process { (Get-WmiObject -Class ` win32_networkadapterconfiguration -filter "ipenabled = $true").DnsServerSearchOrder[0] } #end Process } #end Get-DnsServer Function Get-DomainName { Begin {Write-Host -ForeGroundColor DarkCyan "Obtaining local DNS Domain information..."} Process { (Get-WmiObject -Class win32_networkadapterconfiguration -filter "ipenabled = $true").DnsDomain } #end Process } #end Get-DnsServer Function Get-ARecords($DnsServer) { Begin { Write-Host -ForeGroundColor Yellow "Connecting to $DnsServer ..." } Process { Write-Host -ForeGroundColor Green "Retrieving A records ..." Get-WmiObject -Class MicrosoftDNS_AType -NameSpace Root\MicrosoftDNS -ComputerName $DnsServer -Filter "DomainName = 'nwtraders.com'" | Select-Object -property Ownername, ipaddress } #end process } #end Get-ARecords # *** Entry Point to Script *** $DnsServer = Get-DnsServer $DnsDomain = Get-DomainName Get-ARecords -DnsDomain $DnsDomain -DnsServer $DnsServer
The first thing we need to do is to create a function that will obtain the address of the DNS server. A good name for such a function is Get-DnsServer. To create a function, we use the function keyword and give it a name. There are no inputs required for the function because it will simply query the TCP/IP configuration information on the local computer.
Function Get-DnsServer {
We are going to now use a feature of a function that we have not previously written about—the Begin parameter for a function. The Begin parameter runs once for items on the pipeline. Most of the time, we are using the process parameter for a function. It is, in fact, the default parameter for a function and as a result is normally left off.
We normally do not like relying on default parameters. There are several reasons for this, but the biggest reason is that we like to know what a script is doing. If we do not know that we are using a default parameter, we may not understand when the script fails when we attempt a different parameter. One other reason is that if we do not know we are using a default parameter, we may be similarly unaware of the presence of other parameters.
The parts of a function are seen in DemoFunctionParameters.ps1. An array of numbers is created by using the 1..4 notation. This creates an array of 4 numbers: 1,2,3,4. This array is pipelined to the test function. The first thing that happens is the display of the message, “Beginning test”. Then each number that comes across the pipeline is added to the number 1, and the results are displayed. When the process block is completed, the last thing that happens is the display of the message, “Completed test”. The result of running the DemoFunctionParameters.ps1 script is seen here:
Beginning test 2 3 4 5 Completed test
The complete DemoFunctionParameters.ps1 script is seen here:
Function test { Begin {"Beginning test" } Process {$_ + 1 } End { "Completed test" } } #end test 1..4 | test
Back to our real script. The first thing we do is use the Begin statement list to print out a message in Cyan that informs the user that we are obtaining local DNS server information. This is done by using the Write-Host cmdlet with the ForegroundColor parameter. The Begin statement list is seen here:
Begin {Write-Host -ForeGroundColor Cyan "Obtaining local DNS Server information..."}
When we have produced the first status message, it is time to obtain the DNS server information. We can get this information from the Win32_NetworkAdapterConfiguration WMI class. (Here’s a good series of articles on scripting networking. They are all VBScript examples, but the techniques remain the same.) For additional information about working with network adapters using Windows PowerShell, you can see this article.
With Windows PowerShell, the primary tool to use when working with WMI is the Get-WmiObject cmdlet. There are a number of parameters for this cmdlet, but the primary ones are seen in Table 1.
Table 1 Get-WmiObject cmdlet | |||||
Get-WmiObject | Class | NameSpace | ComputerName | Filter | Credential |
Cmdlet |
WMI class |
WMI Namespace |
Remote computername |
WQL filter |
Credentials to use with remote connection |
When the Get-WmiObject cmdlet is run, a management object is returned. If there is only one instance of that object, we can directly access properties of the object by using a dotted notation. In our script, we are querying the Win32_NetworkAdapterConfiguration WMI class. On most modern computers, there are nearly a dozen “things” that get reported as network adapters. Most computers only have one or two “real” network adapters, but everything from VPN connections to Bluetooth devices end up in the list. To return a single instance of a network adapter can often be a problem. One way that often works is to see if the adapter is IP enabled. That is, does it have an Internet Protocol stack enabled on it? This will filter out many of the bogus adapters.
I am deliberately being obtuse here because if you have multiple network cards enabled on your computer, this function could very well fail because of the way it is written. Because there are so many different network configurations available among our readers, I want you to be aware of the potential limitation here.
After we have the network adapter, we retrieve the IP address for the first DNS server. The DnsServerSearchOrder property is stored as an array. We are selecting the first one to query. The process section of the function is seen here (note that the second line should be one line of code; using the backtick character, we have broken it into two lines of code so that this page will not scroll horizontally in your browser):
Process { (Get-WmiObject -Class ` win32_networkadapterconfiguration -filter "ipenabled = $true").DnsServerSearchOrder[0] } #end Process } #end Get-DnsServer
Next we want to retrieve the domain name for the local computer. To do this, we will once again query the Win32_NetworkAdapterConfiguration WMI class. Once again we use the Begin statement list to print out a status message to indicate that we are obtaining local DNS domain information. This time, we print out the message in DarkCyan. By the way, if you are curious about which colors are available, you can use the GetNames static method from the System.Enum .NET Framework class, as seen here:
PS C:\> [enum]::GetNames("System.ConsoleColor") Black DarkBlue DarkGreen DarkCyan DarkRed DarkMagenta DarkYellow Gray DarkGray Blue Green Cyan Red Magenta Yellow White
The Get Begin statement list is seen here:
Function Get-DomainName { Begin {Write-Host -ForeGroundColor DarkCyan "Obtaining local DNS Domain information..."}
It is time to create our Process block. The code is exactly the same as the previous Process block, except that this time we return the DnsDomain property. This will give us the domain name to use when we need to filter the results later on in the next function:
Process { (Get-WmiObject -Class win32_networkadapterconfiguration -filter "ipenabled = $true").DnsDomain } #end Process } #end Get-DnsServer
Now we come to the Get-ARecords function. This function is used to retrieve A records from the DNS server. We once again use Begin to print out a status message. This is seen here:
Function Get-ARecords($DnsServer) { Begin { Write-Host -ForeGroundColor Yellow "Connecting to $DnsServer ..." }
Then in the Process block, we write another status message (this time in green) that we are retrieving the A records, and we get to the WMI query. The WMI DNS provider resides in the Root\MicrosoftDNS WMI namespace. To work with DNS, we need to specify that WMI namespace. The default WMI namespace is Root\CimV2.
Most of the WMI classes that are used by IT pros live in that namespace, and as a result many are unaware of the other WMI namespaces that exist.
The following is a listing of DNS classes from my Windows Server 2008 computer:
MicrosoftDNS_AAAAType MicrosoftDNS_AFSDBType MicrosoftDNS_ATMAType MicrosoftDNS_AType MicrosoftDNS_Cache MicrosoftDNS_CNAMEType MicrosoftDNS_Domain MicrosoftDNS_DomainDomainContainment MicrosoftDNS_DomainResourceRecordContainment MicrosoftDNS_HINFOType MicrosoftDNS_ISDNType MicrosoftDNS_KEYType MicrosoftDNS_MBType MicrosoftDNS_MDType MicrosoftDNS_MFType MicrosoftDNS_MGType MicrosoftDNS_MINFOType MicrosoftDNS_MRType MicrosoftDNS_MXType MicrosoftDNS_NSType MicrosoftDNS_NXTType MicrosoftDNS_PTRType MicrosoftDNS_ResourceRecord MicrosoftDNS_RootHints MicrosoftDNS_RPType MicrosoftDNS_RTType MicrosoftDNS_Server MicrosoftDNS_ServerDomainContainment MicrosoftDNS_SIGType MicrosoftDNS_SOAType MicrosoftDNS_SRVType MicrosoftDNS_Statistic MicrosoftDNS_TXTType MicrosoftDNS_WINSRType MicrosoftDNS_WINSType MicrosoftDNS_WKSType MicrosoftDNS_X25Type MicrosoftDNS_Zone
The WMI class we are going to query to obtain A records is the MicrosoftDNS_AType. These WMI class names are pretty easy to work with because they all begin with MicrosoftDNS and and underscore. If we wanted to list the MX records, the class would be MicrosoftDNS_MXType. In fact, you could easily replace the code we are using here with the DNS record type you were interested in obtaining. After specifying the WMI class name, we specify the namespace, which as you recall for DNS is Root\MicrosoftDNS. Then the computername parameter is supplied. This is why we used the earlier function to obtain the name of the DNS server, because the Root\MicrosoftDNS namespace does not exist unless the server is running DNS. However, using WMI we can connect to a namespace on a remote machine that does not exist on our local machine, and we can query WMI classes on a remote machine that do not exist on the local computer.
On Windows 2000, you need to download Dnsprov.zip and use MOFCOMP to install the DNS provider. On Windows Server 2003 and later, it is installed automatically.
We use the Filter parameter to limit the A records to those in our own domain. We then use the Select-Object cmdlet to retrieve the Ownername and the IP address. This is seen here:
Process { Write-Host -ForeGroundColor Green "Retrieving A records ..." Get-WmiObject -Class MicrosoftDNS_AType -NameSpace Root\MicrosoftDNS -ComputerName $DnsServer -Filter "DomainName = 'nwtraders.com'" | Select-Object -property Ownername, ipaddress } #end process } #end Get-ARecords
Next we come to the entry point of the script. We call the Get-DnsServer function and store the DNS server name in the $DnsServer variable. We call the Get-DomainName function and store the domain name in the $DnsDomain variable. We then pass both of these values to the GetARecords function. This is seen here:
$DnsServer = Get-DnsServer $DnsDomain = Get-DomainName Get-ARecords -DnsDomain $DnsDomain -DnsServer $DnsServer
The results of running the script are seen here:
Well, JE, that is all there is to using the DNS provider to let us find A records. Join us tomorrow as DNS week continues.
Ed Wilson and Craig Liebendorfer, Scripting Guys
0 comments