Table of Contents

DNS

Here I want to show how huge the difference is between a string and an object.

Initial request

Let's suppose you have to perform a lot of DNS modifications because a service provider of yours has changed IP server. Since you are wise, you are going to follow the rule: Trust but verify.

  1. Check input: maybe some mistakes in the request
  2. Provide a preCheck report to the requester

This is the example we are going to deal with:

A_RecordProvider
google.cap1
google.comp1
google.frp2
google.totop3

Trap to avoid

Sysadmins are used to execute nslookup command to query DNS servers. Let's put it into a powershell script.

dns_v1.ps1
Import-Csv -Path input_dns.csv | foreach {
  nslookup $_.A_Record 8.8.8.8
}

Output is a nightmare.

nslookup : Non-authoritative answer:
At line:1 char:1
+ nslookup google.ca 8.8.8.8
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (Non-authoritative answer::String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError
 
Server:  dns.google
Address:  8.8.8.8

Name:    google.ca
Addresses:  2607:f8b0:4020:805::2003
	  172.217.13.131

Here is an issues list:

  1. Quite difficult to parse results
  2. nslookup is not a built-in Powershell command, as a result we don't get answer as kind of DNS object
  3. Really hard to create preCheck output file

the right tool for the right problem

Recent versions of PowerShell includes the perfect command for this request: Resolve-Dns.

dns_v2.ps1
Import-Csv -Path input_dns.csv | foreach {
  Resolve-DnsName $_.A_Record -Type A -Server 8.8.8.8
}

Now script outputs something we can easily manage and export.

Name                                           Type   TTL   Section    IPAddress                                
----                                           ----   ---   -------    ---------                                
google.ca                                      A      49    Answer     172.217.13.131                           
google.com                                     A      299   Answer     172.217.13.110                           
google.fr                                      A      299   Answer     172.217.13.163                           
Resolve-DnsName : google.toto : DNS name does not exist
At line:2 char:7
+       Resolve-DnsName $_.A_Record -Type A -Server 8.8.8.8
+       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceUnavailable: (google.toto:String) [Resolve-DnsName], Win32Exception
    + FullyQualifiedErrorId : DNS_ERROR_RCODE_NAME_ERROR,Microsoft.DnsClient.Commands.ResolveDnsName

We still raise an exception from google.toto record. We are going to use a try-catch block to handle this.

dns_v3.ps1
Import-Csv -Path input_dns.csv | foreach {
    $currentLine = $_
    $record = $currentLine.A_Record
    try {
        $result = Resolve-DnsName $record -Type A -Server 8.8.8.8 2> $null
        Write-Host $result.getType().fullname
    } catch {
        Write-Host ("Error with record: {0}" -f $record)
    }   
}

When the DNS A record exists, we get an object from Microsoft.DnsClient.Commands.DnsRecord_A class. Otherwise we catch the exception properly.

Microsoft.DnsClient.Commands.DnsRecord_A
Microsoft.DnsClient.Commands.DnsRecord_A
Microsoft.DnsClient.Commands.DnsRecord_A
Error with record: google.toto

Name                                           Type   TTL   Section    IPAddress                                
----                                           ----   ---   -------    ---------                                
google.ca                                      A      299   Answer     172.217.13.131                           
google.com                                     A      299   Answer     172.217.13.110                           
google.fr                                      A      299   Answer     172.217.13.163 

Final script

Reminder: we want to verify if all DNS A records provided by the requester are correct.

dns_final.ps1
$results = @()
 
Import-Csv -Path input_dns.csv | foreach {
    $currentLine = $_
    $record = $currentLine.A_Record
    try {
        $result = Resolve-DnsName $record -Type A -Server 8.8.8.8 2> $null
        Write-Host $result.getType().fullname
        $exportLine = $result | Select-Object -Property Name,Type,IPAddress
        $exportLine | Add-Member -MemberType NoteProperty -Name "Status" -Value "ok"
    } catch {
        Write-Host ("Error with record: {0}" -f $record)
        $exportLine = New-Object psobject -Property @{
            'Name' = $record;
            'Type' = 'A';
            'IPAddress' = '';
            'Status' = 'ko';
        }
    }   
    $results += $exportLine
}
 
$results | Export-Csv -Path dnsPreCheck.csv -Delimiter "," -Encoding UTF8 -NoTypeInformation

From the result we can easily identify names without DNS A records. Mission accomplished :-)

NameTypeIPAddressStatus
google.caA172.217.13.131ok
google.comA172.217.13.110ok
google.frA172.217.13.163ok
google.totoAko