Last week I had a requirement to perform a DNS lookup within a PowerShell script, using a DNS server other than the one that the machine in question was configured to use. This turned out to be quite a challenge and, having found a library that looked like it would do it, I ended up having to call on the expertise of my good friend and PowerShell MVP Jonathan Medd to help me out!
As I'm sure I can't be the first person that has attempted this in PowerShell using the library in question (or something similar) I felt it was worth blogging the methodology…
I won't bore you with too much of the background, but I was amending a script that I wrote a few years back which used the .NET class System.Net.Dns to perform a DNS lookup. This worked fine until my ISP changed the behaviour of their DNS servers to return a search page in the event that the host record in question was not found. Although this is probably helpful to a human being, I wanted my script to raise an error in the event that the host record wasn't found - and this was no longer happening because my ISP's DNS now returns the IP address of the server delivering the search page instead. Therefore, to my script, the DNS lookup was appearing to be successful!
The obvious answer to this is to use a DNS server that doesn't behave in this way. Google's DNS is an example of this. I didn't want to change my entire infrastructure to use Google's DNS servers, so the most sensible approach was to have just the script use them. This is where the next problem became apparent – System.Net.DNS does not allow the use of alternative DNS servers.
After some Googling, I came across the excellent and free 'SimpleDNS DNS Client Library for .NET' from JH Software here. This allows you to specify DNS servers to perform lookups against. However (perhaps because I'm not a .NET programmer) I couldn't work out for the life of me how to set the required property, and that is where Jonathan helped me out. The methodology is described below. I can't take any credit for this – it's all his work
The first step is obviously to download the SimpleDNS library from here, and put at least the Dynamic Link Library 'JHSoftware.DnsClient.dll' into a suitable location on the local machine, say 'C:\PowerShell\Libraries'. (There is a .CHM help file included in the download that is worth including too.)
Here is the entire code snippet used. An explanation of each line follows in the text below.
[System.Reflection.Assembly]::LoadFile("C:\PowerShell\Libraries\JHSoftware.DnsClient.dll") $ServerIPObj = [System.Net.IPAddress]::Parse("220.127.116.11"),[System.Net.IPAddress]::Parse("18.104.22.168") $OptionsObj = New-Object JHSoftware.DnsClient+RequestOptions $OptionsObj.DnsServers = $ServerIPObj $IPVersion = [JHSoftware.DnsClient+IPVersion]::IPv4 $HostAddress = [JHSoftware.DnsClient]::LookupHost("www.microsoft.com",$IPVersion,$OptionsObj) $HostAddress.ToString()
First, the line:
loads the library from the previously given location.
The next step is to create an array of .NET IPAddress objects which are set to Google's DNS server (22.214.171.124 and 126.96.36.199). This is done using the 'Parse' static method which converts a string into an IP address:
$ServerIPObj = [System.Net.IPAddress]::Parse("188.8.131.52"),[System.Net.IPAddress]::Parse("184.108.40.206")
Next, we need to create a new .NET object to hold the request options, and set the 'DnsServers' property of that. For VB.NET and C# examples (but no PowerShell ), see the programming guide here.
$OptionsObj = New-Object JHSoftware.DnsClient+RequestOptions $OptionsObj.DnsServers = $ServerIPObj
The 'LookupHost' method requires an additional parameter because we are using 'RequestOptions' – an enumeration of the IP protocol version to be used, which is called DnsClient.IPVersion. The base class is System.Enum. To create this in PowerShell with the correct value you need use the method below, specifying the protocol version with the appropriate member name. There is a great post from Lincoln Atkinson on using enumerations in PowerShell here.
$IPVersion = [JHSoftware.DnsClient+IPVersion]::IPv4
Once this has been done, we have all the parameters we need to call the LookupHost method and return a result:
$HostAddress = [JHSoftware.DnsClient]::LookupHost("www.microsoft.com",$IPVersion,$OptionsObj)
Now let's take a look at $HostAddress:
Address : 456734529
AddressFamily : InterNetwork
IsIPv6Multicast : False
IsIPv6LinkLocal : False
IsIPv6SiteLocal : False
IPAddressToString : 220.127.116.11
I wanted just the IP address as a string in a variable for the script to process further, and how to do this eluded me initially until I realised that the $HostAddress object was in fact being returned by the library as a System.Net.IPAddress object, and effectively as an array. The MSDN documentation for a similar method of the System.Net.Dns class, the GetHostAddresses method, helped me to work this out. So returning the IP address alone is simply a matter of:
…and that's it!
Jonathan and I decided that we would both blog this information as it could potentially be useful to a number of people. He has taken this a stage further and turned the code above into a very cool function, making it easy to drop into a script for future re-use (I will be incorporating this into my script!). You can see his blog post on this here.