Pragmatic PowerShell Scripting – Functions to gather DNS Data

As I get work done with PowerShell and we continue to prepare for the migration to the AD environment of the company that a acquired us it occurs to me that it will helpful for the team to have ready to hand some DNS related details about our server inventory. Seems like a good task to write using functions.

Required PowerShell modules:

  • DNSServer – Both functions require the PowerShell DNSServer module and membership in the DNSAdmins group. The minimum Windows version is 8/2012. The DNSServer module provides the Get-DnsServerResourceRecord cmdlt. It is useful for both functions.

There are multiple scenarios I should cover.

  1. A server has one IP address and one DNS A record
  2. A server has one IP address and multiple DNS A records with different hostnames
  3. A server has multiple IP addresses with one DNS A record per address
  4. A server has multiple IP addresses and multiple DNS A records per address

We use DNS CNAME records to reduce the impact to applications of server name changes that occur over the life of the application. There are multiple CNAME scenarios to be handled.

  1. An A record can have one or more CNAME records that reference it
  2. A CNAME record can have one or more CNAME records that reference it

I’m starting with a file that has a list of servers pulled from AD and their IP addresses. For each of these servers I need to collect data on A and CNAME records so as we rename servers we can make any modifications to DNS that are required. It looks to me like I need two functions. One that accepts as input a server name and returns a list of the associated CNAME names, including those that are CNAME records that reference other CNAME records. I’ll call this function Get-Cname. The other function will accept as input an IP address and return all the DNS A record names that resolve to that IP address. I’ll call this function Get-Arecord. Both functions require the PowerShell DNSServer module and the DNS admin role in AD. The minimum Windows version is 8/2012. The DNSServer module provides the Get-DnsServerResourceRecord cmdlt. It is useful for both functions.

For the Get-Arecord function I set the RRType parameter to ‘A’. Note that to get the IPAddress as a String I use RecordData.IPv4Address.IPAddressToString.

# Start get-arecord function
function get-Arecord
{

<#
   .SYNOPSIS
   get-Arecord is a PowerShell function to get the DNS a-records of a server

   .DESCRIPTION
   get-ARecord has 3 required parameters: -Address, -ZoneName, and -ADServerName

   .PARAMETER Address
   Specifies the IPv4 address you want to get the a-Records for

   .PARAMETER ZoneName
   Specifies the Zone you want to search. Usually the domain name.

      .PARAMETER ADServerName
   Specifies the DNS server to query

   .EXAMPLE
   PS C:\$a-record = get-ARecord -Address 192.168.100.10 -ZoneName 'myzone.local' -ADServerName 'mydnsserver' 

#>

[cmdletbinding()]
Param(
   [parameter(Mandatory = $true,position=0)]
    $ServerName,
   [parameter(Mandatory = $true,position=1)]
    $ZoneName,
    [parameter(Mandatory = $true,position=2)]
    $ADServerName
 )
 
# nothing to do here	
   begin {}

   process {
   $names = @()
# DNS admin role required. Added the RRType parameter to search for A records
$c = Get-DnsServerResourceRecord -ZoneName $ZoneName -ComputerName  -RRType "A" | ForEach-Object{ if ($_.RecordData.IPv4Address.IPAddressToString -eq $address) { $_.Hostname } }

      if ($c -ge 1)
       {		
         foreach ($n in $c)
            {
               $names += $n		
            }	
      }
   
      Write-Output $names	
   }	
	
   end {}
}
#End a-record function

I make use of the same cmdlet to get all the cname records. In this case I have to handle cname records that point to other cname records and make that apparent in the output. So instead of just returning the HostNameAlias property I associate it with the server name. The output is not great but it works for my purpose. I make use of recursion to check if any of the CNAME records have CNAME records.

# Start get-cName function
function get-CName
{
<#
   .SYNOPSIS
   get-CName is a PowerShell function to get the DNS CName (aliases) of a server

   .DESCRIPTION
   get-CName has 3 required parameters: -ServerName, -ZoneName, -ADServerName 
   The function performs a recursion to return scenario of CNames having CNames.
   Because of this scenario the function returns an array of a custom object.
   ADServerName should be a Domain Controller that also has the DNS server role installed. 
   

   .PARAMETER ServerName
   Specifies the DNS hostname you want to get the CName records for

   .PARAMETER ZoneName
   Specifies the Zone you want to search.

      .PARAMETER ADServerName
   Specifies the DNS server to query

   .EXAMPLE
   The following example shows the output for a hostname that has a CName that itself has a CName pointing to it.
   brickhouse -> strawhouse -> woodhouse
   PS C:\$cnames = get-CName -ServerName 'brickhouse' -ZoneName 'myzone.local' -ADServerName 'mydnsserver' 
   PS C:\$cnames
   CName  HostName
   -----  --------
   strawhouse brickhouse
   woodhouse strawhouse
#>

   [cmdletbinding()]
   Param(
    [parameter(Mandatory = $true,position=0)]
     $ServerName,
    [parameter(Mandatory = $true,position=1)]
     $ZoneName,
     [parameter(Mandatory = $true,position=2)]
     $ADServerName
  )

# Nothing to to here
   begin {}

      process {
         $cnames = @()
         $names = @{
            HostName = ''
            CName = ''
         }
#DNS admin role required
         $c = Get-DnsServerResourceRecord -ZoneName $ZoneName -ComputerName $ADServerName -RRType "CName" | ForEach-Object{ 
            if ($_.RecordData.HostNameAlias.Contains($ServerName)) {$_.Hostname } }
	 if ($c -ge 1)
           {		
               foreach ($n in $c)
                  {
                     $names['Hostname'] = $ServerName
                     $names['CName'] = $n
                     New-Object -Property $names -TypeName psobject
                     CNames += $names
                  }
               }
     if ($c)
       {
         foreach ($n in $c)
            {
# recurse 
               get-CName -ServerName $n -ZoneName $ZoneName -ADServerName $ADServerName
            }
        }
#Return the results    
     $CNames
     }	
   end {}
}