Introduction

Azure IAAS (Infrastructure As A Service) can be used to host your own Domain Controller / DNS servers. This could be done by starting a new domain from scratch or extending your existing on-promise domain to the cloud.

Name resolution is a critical configuration item for DCs and domain-joined computers where DCs should be able to register their resource records and all domain resources should be able to resolve them properly. In case of malfunction in this resolution system, your AD environment may face authentication failures or become not available.

On Windows Azure, Microsoft does not support using static IPs as all the VMs are managed by DHCP service. By fixing a static IP, the VM may become unstable or not available anytime. This led many AD administrators to ask the following question: How can I properly manage DNS resolution in Windows Azure while my DNS servers cannot have a static IP?

This Wiki article provides an answer for this question by sharing one way to manage DNS servers with Dynamic IPs in the cloud. This is explained via two Scenarios.

Before starting the two scenarios, please note that Windows Azure provides a dynamic IP that remains the same for a VM as long as it was not rebooted. Once rebooted, the VM may lose its IP and get a new one.

Scenario 1: Creating a new AD domain in the cloud

Requirements

CONTOSO is a company that would like to create a new AD domain named contoso.com in Windows Azure. This domain will be maintained by two Domain Controllers that will have AD-integrated DNS zones. CONTOSO is also planning to host 100 AD-integrated servers in Windows Azure. Both Domain Controllers will be used for DNS resolution and the implementation should not require making manual updates on the IP configuration of the servers if one of the Domain Controllers reboots and does not keep its old IP.

The Domain Controllers in the cloud will be named DC1 and DC2.

Solution

As Microsoft does not support using static IPs for VMs in Windows Azure and having static IPs is required for DC1 and DC2 to be used as DNS servers, CONTOSO can use the following approach to manage the IP settings DNS configuration of their Domain Controllers and AD-integrated servers:

  1. A file Server named FS1 will be provisioned in Windows Azure and will be member of a WORKGROUP
  2. The Domain Controllers DC1 and DC2 will publish their IPs in a text file stored in a shared folder on FS1
  3. The Domain Controllers and AD-integrated servers will use the shared text file to keep their IP settings up-to-date: The updates of IP settings will be managed automatically by Powershell scripts

Implementation

The first step would be to provision a new file server named FS1.

Once provisioned, we will be able to use the File Server to store the IPs of the DCs. To do that, all we need are:

  • A shared folder on FS1: In our case, it will be \\192.168.0.6\DNSIPs (where 192.168.0.6 is the current IP address of FS1)
  • A text file named DNSIPs.txt under \\192.168.0.6\DNSIPs that contains the name of used DCs and their current IPs using <DC_Name>:<IP_Address> format:


  • A text file named FileServerIP.txt that is stored on DC1 and DC2. It will be used to store the IP address of the File server which is an alternative we use to identify the File Server as NetBIOS broadcasting is not supported on Windows Azure.


  • The following Powershell script on DC1 and DC2: The script will update the IP addresses of the DCs in DNSIPs.txt. If they were changed. It identifies the File Server FS1 by reading the IP from FileServerIP.txt file. The script can be scheduled to run every five (5) minutes on both DCs.

##########Variables################

$FileServerIP = Get-Content C:\Script_Repository\FileServerIP.txt

###################################

##########Main#####################

$FilePath = "\\" + $FileServerIP + "\DNSIPs\DNSIPs.txt"

If (Test-Path($FilePath))

{

                $IPList = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=TRUE -ComputerName .

                $Hostname = [System.Net.Dns]::GetHostName()

                $HostnameIP = $Hostname + ":" + $IPList.IPAddress[0]

                $FileEntry = (Select-String -Path $FilePath -pattern $Hostname).Line

                if ($FileEntry -ne $HostnameIP)

                {

                                (gc $FilePath).replace($FileEntry,$HostnameIP) | Out-File $FilePath

                }

}

 

Now, we have DNSIPs.txt text file on FS1 that is contains the DCs IP addresses and is updated in a maximum of five (5) minutes if a DC has a new IP address.

DNSIPs.txt will then be used to implement an automatic update of IP settings DNS configuration for DCs and AD-joined servers in Windows Azure.

As CONTOSO only have two DCs in Windows Azure, the following IP settings DNS configuration for the DCs will be used:

DC name

IP settings DNS configuration

DC1

Primary DNS server: DC2 IP address

Secondary DNS server: DC1 IP address

Third DNS server: 127.0.0.1

DC2

Primary DNS server: DC1 IP address

Secondary DNS server: DC2 IP address

Third DNS server: 127.0.0.1

 

This IP settings DNS configuration could be maintained on both DCs by using the following Powershell script: The script will read the DCs’ IP addresses from DNSIPs.txt. If a change was detected then the IP settings DNS configuration of the DCs will be updated. $server1 and $server2 variables need to be updated to reflect the desired configuration per DC (See the previous table). The script can be scheduled run every five (5) minutes on both DCs

##########Variables################

$FileServerIP = Get-Content C:\Script_Repository\FileServerIP.txt

$server1 = "DC1"

$server2 = "DC2"

###################################

##########Main#####################

$FilePath = "\\" + $FileServerIP + "\DNSIPs\DNSIPs.txt "

If (Test-Path($FilePath))

{

$server1search = $server1 +":"

$server2search = $server2 +":"

$server1IP = ((Select-String -Path $FilePath -pattern $server1).Line).replace($server1search,"")

$server2IP = ((Select-String -Path $FilePath -pattern $server2).Line).replace($server2search,"")

$server3IP = "127.0.0.1"

$IPList = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=TRUE -ComputerName .

$i = 0

$nicwithdnscount = 0

$conformity = "Not Compliant"

if ($IPList.Count -ne $null)

{

    while ($i -ne $IPList.Count)

    {

    try{

       if ($IPList[$i].DNSServerSearchOrder[0] -ne $null)

       {

        $nicwithdnscount = $nicwithdnscount + 1

       }

        if (($IPList[$i].DNSServerSearchOrder[2] -eq $null))

        {

          if (($IPList[$i].DNSServerSearchOrder[0] -ne $null) -AND ($IPList[$i].DNSServerSearchOrder[1] -ne $null))

           {

              if (($IPList[$i].DNSServerSearchOrder[0] -eq $server1IP) -AND ($IPList[$i].DNSServerSearchOrder[1] -eq $server2IP))

              {

                  $conformity = "Compliant"

              }

              else

              {

                  $conformity = "Not Compliant"

              }

         }

     }

    }

    catch

    {

    }

    $i = $i + 1

    if ($nicwithdnscount -ne 1)

    {

        $conformity = "Not Compliant"

    }

    }

}

else

{

        try{

        if ($IPList.DNSServerSearchOrder[0] -ne $null)

        {

            $nicwithdnscount = $nicwithdnscount + 1

        }

        if (($IPList.DNSServerSearchOrder[2] -eq $null))

        {

          if (($IPList.DNSServerSearchOrder[0] -ne $null) -AND ($IPList.DNSServerSearchOrder[1] -ne $null))

           {

              if (($IPList.DNSServerSearchOrder[0] -eq $server1IP) -AND ($IPList.DNSServerSearchOrder[1] -eq $server2IP))

              {

                  $conformity = "Compliant"

              }

              else

              {

                  $conformity = "Not Compliant"

              }

         }

     }

    }

    catch

    {

    }

}

$i = 0

if (($conformity -eq "Not Compliant") -AND ($nicwithdnscount -eq 1))

    {

        if ($IPList.Count -ne $null)

        {

           while ($i -ne $IPList.Count)

           {

           try{

               if ($IPList[$i].DNSServerSearchOrder[0] -ne $null)

               {

                   $arrDNSServers = $server1IP, $server2IP, $server3IP

                   $IPList[$i].SetDNSServerSearchOrder($arrDNSServers)

               }

           }

           catch

           {

           }

        $i = $i + 1

        }

    }

    else

    {

        try{

            if ($IPList.DNSServerSearchOrder[0] -ne $null)

            {

                $arrDNSServers = $server1IP, $server2IP, $server3IP

                $IPList.SetDNSServerSearchOrder($arrDNSServers)

            }

        }

        catch

        {

        }

    }

}

}

 

Now, the IP settings DNS configuration is automatically updated on DCs to reflect the correct IPs of DNS servers for CONTOSO. The next step would be to do the same but for domain-joined servers.

All you need are:

  • A text file named FileServerIP.txt that will store the current IP of the File Server FS1
  • The following Powershell script to run on every domain-joined server: The script will read the DCs’ IP addresses from DNSIPs.txt. If a change was detected then the IP settings of the domain-joined servers will be updated. $server1 and $server2 variables need to be updated to reflect the desired configuration – In this case, we make DC1 used as primary DNS server and DC2 as secondary. The script can be scheduled run every five (5) minutes on domain-joined servers.

##########Variables################

$FileServerIP = Get-Content C:\Script_Repository\FileServerIP.txt

$server1 = "DC1"

$server2 = "DC2"

###################################

##########Main#####################

$FilePath = "\\" + $FileServerIP + "\DNSIPs\DNSIPs.txt"

If (Test-Path($FilePath))

{

$server1search = $server1 +":"

$server2search = $server2 +":"

$server1IP = ((Select-String -Path $FilePath -pattern $server1).Line).replace($server1search,"")

$server2IP = ((Select-String -Path $FilePath -pattern $server2).Line).replace($server2search,"")

$IPList = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=TRUE -ComputerName .

$i = 0

$nicwithdnscount = 0

$conformity = "Not Compliant"

if ($IPList.Count -ne $null)

{

    while ($i -ne $IPList.Count)

    {

    try{

       if ($IPList[$i].DNSServerSearchOrder[0] -ne $null)

       {

        $nicwithdnscount = $nicwithdnscount + 1

       }

        if (($IPList[$i].DNSServerSearchOrder[2] -eq $null))

        {

          if (($IPList[$i].DNSServerSearchOrder[0] -ne $null) -AND ($IPList[$i].DNSServerSearchOrder[1] -ne $null))

           {

              if (($IPList[$i].DNSServerSearchOrder[0] -eq $server1IP) -AND ($IPList[$i].DNSServerSearchOrder[1] -eq $server2IP))

              {

                  $conformity = "Compliant"

              }

              else

              {

                  $conformity = "Not Compliant"

              }

         }

     }

    }

    catch

    {

    }

    $i = $i + 1

    if ($nicwithdnscount -ne 1)

    {

        $conformity = "Not Compliant"

    }

    }

}

else

{

        try{

        if ($IPList.DNSServerSearchOrder[0] -ne $null)

        {

            $nicwithdnscount = $nicwithdnscount + 1

        }

        if (($IPList.DNSServerSearchOrder[2] -eq $null))

        {

          if (($IPList.DNSServerSearchOrder[0] -ne $null) -AND ($IPList.DNSServerSearchOrder[1] -ne $null))

           {

              if (($IPList.DNSServerSearchOrder[0] -eq $server1IP) -AND ($IPList.DNSServerSearchOrder[1] -eq $server2IP))

              {

                  $conformity = "Compliant"

              }

              else

              {

                  $conformity = "Not Compliant"

              }

         }

     }

    }

    catch

    {

    }

}

$i = 0

if (($conformity -eq "Not Compliant") -AND ($nicwithdnscount -eq 1))

    {

        if ($IPList.Count -ne $null)

        {

           while ($i -ne $IPList.Count)

           {

           try{

               if ($IPList[$i].DNSServerSearchOrder[0] -ne $null)

               {

                   $arrDNSServers = $server1IP, $server2IP

                   $IPList[$i].SetDNSServerSearchOrder($arrDNSServers)

               }

           }

           catch

           {

           }

        $i = $i + 1

        }

    }

    else

    {

        try{

            if ($IPList.DNSServerSearchOrder[0] -ne $null)

            {

                $arrDNSServers = $server1IP, $server2IP

                $IPList.SetDNSServerSearchOrder($arrDNSServers)

            }

        }

        catch

        {

        }

    }

}

}

 

After implementing the script, the IP settings DNS configuration will be up-to-date on domain-joined servers.

Let’s suppose now that DC1 was rebooted and has now 192.168.0.8 as IP address. Within five (5) minutes, DC1 will update DNSIPs.txt text file to reflect its new IP:


Once updated, the Domain Controllers and domain-joined servers will adjust their IP settings after detecting the changes. This is done within five (5) minutes after the update of the text file.


This implementation will work properly as long as the File Server FS1 has not lost its current IP. This means that, as long as the server was not rebooted, the Domain Controllers and domain-joined servers can keep their IP settings DNS configuration up-to-date.

To periodically check that FS1 is still using the same IP address, all you need are:

  • A text file named FileServerIP.txt on FS1 that will store the last detected IP address of the server


  • A Powershell script that will be used to periodically check the IP address of the file server, compares it with the one registered in FileServerIP.txt and then inform the administrator by mail if the IP was changed. $smtpServer variable need to be updated to have the DNS name / IP of the SMTP server to use while $sendermail and $receivermail need to be updated to have the e-mail addresses to use for sending / receiving the mail notifications.

##########Variables################

$smtpServer = "mail.contoso.com"

$sendermail = "no-reply@contoso.com"

$receivermail = "administrator@contoso.com"

###################################

##########Main#####################

$IPList = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=TRUE -ComputerName .

$IP = $IPList.IPAddress[0]

$RegisteredIP = Get-Content C:\Script_Repository\FileServerIP.txt

if ($RegisteredIP -ne $IP)

{

                $IP | Out-File C:\Script_Repository\FileServerIP.txt

                $msg = new-object Net.Mail.MailMessage

                $msg.From = $sendermail

                $msg.To.Add($receivermail)

                $msg.Subject = "The IP address of " + [System.Net.Dns]::GetHostName() + " was changed"

                $msg.Body = "The IP address of " + [System.Net.Dns]::GetHostName() + " changed from " + $RegisteredIP + " to " + $IP + "."

                $smtp = new-object Net.Mail.SmtpClient($smtpServer)

                $smtp.Send($msg)

}

 

The following script can be launched manually on an AD-Integrated server having Active Directory Powershell module to update the local FileServerIP.txt file on DCs and domain-joined servers (The file becomes to copy up-to-date on the File Server after that the server detects that its IP was changed). $DN need to be updated to specify the start of search for servers in AD while $newFileServerIP need to be updated to specify the new File Server IP address.

##########Variables################

$DN = "DC=CONTOSO,DC=COM"

$newFileServerIP = “192.168.0.6”

###################################

##########Main#####################

$FileServerfile = “\\” + $newFileServerIP + “\C$\Script_Repository\FileServerIP.txt”

import-module activedirectory

$AzureServers = Get-ADComputer -SearchBase $DN -Filter *

foreach ($server in $AzureServers)

{

                $destinationfile = "\\" + $server.name + "\c$\Script_Repository\FileServerIP.txt"

                xcopy $FileServerfile $destinationfile /y

}

 

Scenario 2: Extending an existing on-promise AD domain to the cloud

Requirements

CONTOSO is a company that would like to extend its existing on-promise AD domain to Windows Azure. CONTOSO already have two on-promises Domain Controllers and would like to add two Domain Controllers in Windows Azure that will belong to the same domain. CONTOSO is also planning to host 100 AD-integrated servers in Windows Azure. Both Domain Controllers in the cloud will be used for DNS resolution and LDAP queries / authentication if the on-promise Domain Controllers are no longer reachable. The implementation should not require making manual updates on the IP configuration of the servers if one of the Domain Controllers in Windows Azure reboots and does not keep its old IP.

CONTOSO have implemented a site to site VPN tunnel between their Datacenter and Windows Azure.

The on-promise Domain Controllers are named DC1 and DC2 while the new Domain Controllers in Windows Azure will be named DC3 and DC4.

Solution

The solution would be the same as the one described in the previous scenario. On-promise servers will not be in the scope of automatic updates for IP settings DNS configuration.

Implementation

The method discussed for the previous scenario can be re-used here. However, it is recommended to have at least one on-promise DNS servers configured as the primary DNS server for DCs and AD-integrated servers in Windows Azure. DCs in Windows Azure need to be configured as secondary DNS servers so that if the Site to Site VPN tunnel between CONTOSO HQ and Windows Azure fails, the DCs in Windows Azure can be used for DNS resolution and LDAP authentication / queries.

 

Conclusion

This article shared an idea that can be used to manage DC/DNS servers with dynamic IPs in Windows Azure. The described implantation is like implementing an “intelligent” HOSTS file that can be used by DCs and AD-integrated servers in Windows Azure to identify the IP addresses of the DNS servers and automatically correct their IP configuration. By following this method, AD administrators can host Domain Controllers in Windows Azure for a new domain or as an extension of an existing domain that can be reliable for DNS resolution.

 


See Also