====== 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.
- Check input: maybe some mistakes in the request
- Provide a preCheck report to the requester
This is the example we are going to deal with:
A_Record,Provider
google.ca,p1
google.com,p1
google.fr,p2
google.toto,p3
===== Trap to avoid =====
Sysadmins are used to execute nslookup command to query DNS servers. Let's put it into a powershell script.
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:
- Quite difficult to parse results
- nslookup is not a built-in Powershell command, as a result we don't get answer as kind of DNS object
- 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.
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.
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.
$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 :-)
"Name","Type","IPAddress","Status"
"google.ca","A","172.217.13.131","ok"
"google.com","A","172.217.13.110","ok"
"google.fr","A","172.217.13.163","ok"
"google.toto","A","","ko"