DNS Server Enforcement across an AD Forest

Support in Active Directory for centrally enforcing client DNS settings via Group Policy Objects is at best patchy.  Many of the settings work only with specific (and often legacy) versions of Windows; the “DNS Server” is one such setting.   It is supported only on Windows XP Professional meaning no support is available for Windows Server 2000, 2003, 2008, Windows Vista or Windows 7.   In most environments workstations receive their DNS Server configuration settings automatically via DHCP.   These settings can be changed by configuring either the DHCP server global or scope settings,  (note: TCP/IP settings configured locally on a computer override the settings provided by DHCP).    While commonly servers have their TCP/IP settings statically configured.   

One possibility as to why Microsoft has not pursued widening the DNS Server GPO setting support for newer releases of Windows is that network configurations particularly on servers are often more complex, for example multiple network interface cards (NICs) or multiple VLANs trunked into a single NIC.   In such scenarios it would be difficult to apply settings in a GPO to a specific targeted NIC on a server.

When new DNS servers are implemented with new IP addresses it is necessary to reconfigure computers that have static TCP/IP configurations.   This article, although focusing on ongoing enforcement of DNS Server settings can be used as a basis to writing one off scripts to configure Domain member computers.

 A way to enforce DNS Client Settings is to create a Computer Startup Script GPO.    This is located in the GPO Editor under Computer Configuration\Policies\Windows Settings\Scripts\Startup.

Startup scripts only run when a computer boots and initialises, meaning if settings configured by the script are changed by an Administrator they will only be reset the next time the script runs which will be when the computer is next rebooted.

A consideration when configuring DNS Server settings is the computers location and identifying its local DNS servers, often these differ from AD Site to AD Site.   A GPO containing the DNS Server configuration Startup script could be linked to an AD site but of course this would mean potentially one Startup script per AD site which in a large environment would quickly become an unmanageable mess!    Most large environments strive for a unified Computer startup script which contains logic to ensure only the code that is intended to run for a specific computer, site or OU  is run on the client computer.  

Generally it is better to link the GPO containing the Computer Startup script to the base OU containing the Computer objects.   If the GPO is linked to an OU whose directory tree contains computers with DHCP as well as statically configured NICS, code must be present in the script to ignore and not enforce DNS Server settings on any DHCP configured NICs (this is not covered in this article).   To ensure the correct DNS server settings are enforced by the script, it must identify the AD site the computer belongs to.  -   The following vbScript code does this:

 

Set objADSysInfo = CreateObject(“ADSystemInfo”)
strSite = objADSysInfo.SiteName
Set objADSysInfo = Nothing

 

The vbScript variable strSite contains the AD Site name (as it appears in the AD Sites and Services snap-in) as a string.   Next using a Select Case statement an array arrDNSServers can be populated with the correct DNS server settings for each site.   A Case Else is implemented to ensure there is a catch-all for any site that is not specifically listed in the script .

 

Select Case strSite
 Case “site1-intranet”
  arrDNSServers = Array(“172.24.16.2″, “172.24.16.3″)
 Case “site1-internet”
  arrDNSServers = Array(“10.0.18.2″, “10.0.18.3″)
 Case “site2″
  arrDNSServers = Array(“172.24.17.2″, “172.24.17.3″, “172.24.16.2″)
 Case Else
  arrDNSServers = Array(“172.24.16.2″, “172.24.16.3″)
End Select

 

Now we have the configuration the correct NIC (logical or physical) must be identified and the settings applied.  This is contained within a function, so first to call the function and pass the array populated in the previous step.

 

SetDNSResolverList arrDNSServers

 

Knowing which adapter to configure is key and there is probably no ideal failsafe approach but here follows two possibilities, this first code block uses the following logic.   It configures the DNS Servers for all NICs that have TCP/IP enabled and have a DNS Server List configured (DNSServerSearchOrder).   The basis for this is on a multihomed system the bindings will determine which is the primary and which is the secondary, tertiary etc.  By default TCP/IP sends packets to the primary NIC  unless static routes determine they should pass through another NIC.  Therefore the DNS Server List need only be configured on the primary NIC.

 

Function SetDNSResolverList(ByRef arrDNSServers)

Dim objWMIService, colAdapters, objAdapter

Set objWMIService = GetObject(“winmgmts:{impersonationLevel=impersonate}//./root/cimv2″)

Set colAdapters = objWMIService.ExecQuery _
 (“SELECT * FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = True”)

For Each objAdapter in colAdapters
 If Not IsNull(objAdapter.DNSServerSearchOrder) Then
  objAdapter.SetDNSServerSearchOrder(arrDNSServers)
 End If
Next
Set colAdapters = Nothing
Set objWMIService = Nothing
wscript.echo “completed”
End Function

 

An alternate method using the Win32_PingStatus WMI object is to first ping the computer by it’s name, identify the IP Address that responds to the ping request and query Win32_NetworkAdapterConfiguration, from the collection of returned adapters identify the adapter with a matching configured IP Address and apply the DNS Servers list to it, something similar to this:

 

‘ first set arrDNSServers, use previous code
‘ examples to create site specific values

arrDNSServers = Array(“172.24..16.2″, “172.24.16.3″)

strComputer = getEnvVar(“PROCESS”, “ComputerName”)
strIPAddr = getPingAddress(strComputer)

SetDNSResolverList arrDNSServers, strIPAddr

Function getPingAddress(byRef strComputer)

 err.Clear
 Set oPingResults = GetObject(“winmgmts:{impersonationLevel=impersonate}//.” &_
  ”/root/cimv2″).ExecQuery(“SELECT * FROM Win32_PingStatus WHERE Address = ‘” &_
  + strComputer + “‘”)

 For Each oPingResult In oPingResults
  If oPingResult.StatusCode = 0 Then
   getPingAddress = oPingResult.ProtocolAddress
  Else
   getPingAddress = “noResponse”
  End If
 Next

 
 If Err.Number <> 0 Then
  getPingAddress = “notReachable”
 End If

 Set oPingResults = Nothing

End Function

Function SetDNSResolverList(ByRef arrDNSServers, ByRef strIPAddr)

 Dim objWMIService, colAdapters, objAdapter

 Set objWMIService = GetObject(“winmgmts:{impersonationLevel=impersonate}//./root/cimv2″)

 Set colAdapters = objWMIService.ExecQuery _
  (“SELECT * FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = True”)

 For Each objAdapter in colAdapters
   For Each strIPAddress in objAdapter.IPAddress
     If Not IsNull(strIPAddress = strIPAddr) Then
       objAdapter.SetDNSServerSearchOrder(arrDNSServers)
     End If
   Next
 Next

 Set colAdapters = Nothing
 Set objWMIService = Nothing
End Function

Function getEnvVar(byref strEnvType, byRef strEnvVar) ‘ strEnvType: (User|System|Process)
  Dim objWSH, objSystemVars
  Set objWSH =  CreateObject(“WScript.Shell”)
  Set objSystemVars = objWSH.Environment(“PROCESS”)
  getEnvVar = objSystemVars(strEnvVar)
  Set objWSH =  Nothing
  Set objSystemVars = Nothing
End Function

I am sure there are other approaches to achieving this, please detail in the comments if you have used alternative approaches.

  1. No comments yet.

  1. No trackbacks yet.