Friday, November 18, 2011

Changing Network Adapter DNS Server IP Address Remotely

I was given the task to change the DNS Server IP addresses for all our servers.

It would be easy to do it manually if there were only a few servers but changing for 250 servers would be very time consuming and probably error prone.

DNSSettings

Instead of logging on to the 250 servers one at a time and manually change the DNS IP addresses, I need to have a way to change the settings for all servers remotely.

There is one requirement that I have in this remote change solution.  Some servers have multiple network adapters enabled.  Some of these network adapters either do not have DNS IP addresses or have different DNS IP addresses.  Therefore, I need to have a way to review the settings on each server and only select those that I want to update.

Initially, I was looking at the PSExec which is part of Sysinternals Suite.  This allows me to run IPConfig on the remote server and pipe the output to a text file.  For example, psexec \\deServer1 ipconfig /all >> DNSInfo.txt.

I can have all my server names (exported from Active Directory) put in a text file (e.g. myServers.txt) and use the for command to loop through the list and run the ipconfig /all remotely.  For example, for /f %s in (myServers.txt) do psexec \\%s ipconfig /all >> DNSInfo.txt.

The problem is the output contains too much information which I don’t need for this task which make it difficult for me to review.

So I ended up using Windows Management Instrumentation (WMI) and Powershell scripts to solve my problem.  Anyway, this is also my first Powershell script.  Okay, there two Powershell scripts.  The first script collects the information I needed for review.  The second script does the update of the DNS IP addresses.

$servers = Get-Content MyServers.txt


Add-Content DNSInfo.csv "ServerName,IPAddresses,NICIndex,NICName,ExistingDNSSettings"

foreach($server in $servers)

{
    $error.clear()
   
    try
    {

        $nicConfigs = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $server -ErrorAction Stop | Where{$_.IPEnabled -eq "TRUE"}
 
        foreach($nicConfig in $nicConfigs)

        {
            $nic = Get-WmiObject Win32_NetworkAdapter -ComputerName $server -ErrorAction Stop | Where{$_.DeviceID -eq $nicConfig.Index}
            $nicName = $nic.NetConnectionID
            $nicIndex = $nicConfig.Index
            $nicIP = $nicConfig.IPAddress
            $currentDNS = $nicConfig.DNSServerSearchOrder
           
            Write-Host "$server,$nicIP,$nicIndex,$nicName,$currentDNS"
            Add-Content DNSInfo.csv "$server,$nicIP,$nicIndex,$nicName,$currentDNS"
       }      
    }
    catch [system.exception]
    {
        Write-Host "Error: $server" $error
        Add-Content DNSInfoError.txt "Error: $server $error"
    }
}

The script used the Win32_NetworkAdapterConfiguration and Win32_NetworkAdapter classes to collect the information I needed.  The Win32_NetworkAdapter class was used because it has the NetConnectionID property which is the name of the network adapter shown in the Network Connections.  The NetConnectionID property is not available in Windows 2000 according to http://msdn.microsoft.com/en-us/library/windows/desktop/aa394216%28v=VS.85%29.aspx.  We do not have Windows 2000 server so it is not an issue for us.

The Win32_NetworkAdapterConfiguration and Win32_NetworkAdapter classes are associated to each other by the Index and DeviceID properties respectively.  By using these two classes, I am able to get the server name, IP addresses set on the network adapter, index of the network adapter, the name of the network adapter and the current DNS IP addresses.

Below is a sample output (DNSInfo.csv) from the script shown in Excel .

ServerName IPAddresses NICIndex NICName ExistingDNSSettings
deServer1 192.168.1.28 192.168.1.29 192.168.1.30

9

Local Area Connection 192.168.1.2 10.10.1.1
deServer3 192.168.1.50

7

Local Area Connection 192.168.1.2 10.10.1.1
deServer5 192.168.1.60

13

LAN 192.168.1.2 10.10.1.1
deServer5 192.168.1.71 192.168.1.72

15

NLB 192.168.1.2 10.10.1.1
deServer6 192.168.1.100 192.168.1.101

1

Public 192.168.1.2 10.10.1.1
deServer6 192.168.1.200

2

Heartbeat  

Errors are logged in the DNSInfoError.txt file and the most common error that I have gotten is “The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)”.

I can then chose to remove deServer1, deSerevr3 and the Heartbeat NIC of deServer6 from the update.  After removing the three rows, I will remove the IPAddresses and ExistingDNSSettings columns and save it as DNSInfoUpdate.csv.

Below is a sample of DNSInfoUpdate.csv shown in Excel .

ServerName NICIndex NICName
deServer5

13

LAN
deServer5

15

NLB
deServer6

1

Public

I will then use the DNSInfoUpdate.csv and the second script shown below to update the DNS IP addresses.

$colServersInfo = Import-CSV DNSInfoUpdate.csv
$newDNServer = "192.168.1.5","10.10.10.11"


foreach($objServerInfo in $colServersInfo)

{
    $ServerName = $objServerInfo.ServerName
    $NICIndex = $objServerInfo.NICIndex
    $NICName = $objServerInfo.NICName
   
    $error.clear()

    try
    {

        $nicConfigs = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $ServerName -ErrorAction Stop | Where{$_.IPEnabled -eq "TRUE"}
        foreach($nicConfig in $nicConfigs)
        {
            if ($nicConfig.Index -eq $NICIndex)
            {
                Write-Host "Updating DNS Settings of NIC $NICName on $ServerName"
                Add-Content DNSUpdate.log "Updating DNS Settings of NIC $NICName on $ServerName"
                $result = $nicConfig.SetDNSServerSearchOrder($newDNServer)
               

                if($result.ReturnValue -eq 0)
                {
                    Write-Host "Successfully changed DNS Servers of NIC $NICName on $ServerName"
                    Add-Content DNSUpdate.log "Successfully changed DNS Servers of NIC $NICName on $ServerName"
                }
                else
                {
                    Write-Host "Failed to change DNS Servers of NIC $NICName on $ServerName"
                    Add-Content DNSUpdate.log "Failed to change DNS Servers of NIC $NICName on $ServerName"
                }
               
            }

        }      
   
    }
    catch [system.exception]
    {
        Write-Host "Error: $ServerName" $error
        Add-Content DNSInfoUpdateError.txt "Error: $ServerName $error"
    }

}

I am changing the DNS IP addresses to 192.168.1.5 and 10.10.10.11.  The script will connect to the server and will only update the network adapter that matches the index.  Status of the changes are logged in DNSUpdate.log and errors are logged in DNSInfoUpdateError.txt.

It does take me half a day to work out this solution but it is still better than doing it manually.

12 comments:

Sergio said...

Hi Alex, thanks for posting this information. I was looking for a way to run a script to do exactly what you did. I am going to test this on my lab prior to trying it on my production environment. Is there anything else that I should consider or change from this script that I should be aware? Thanks

Sergio said...
This comment has been removed by the author.
Sergio said...
This comment has been removed by the author.
Alex Siow said...

Hi Sergio,

Just to be safe, when you run it in production, you might want to run it against a few servers first and check the result.

Good luck.

Sergio said...

I plan on doing so tomorrow. If all goes well, I will execute the scripts this weekend. since I am also learning powershell, if I run into a problem with the script I will send you a message with the screenshots, I do appreciate your assistance. Thanks

Sergio said...

Well, I tried running the first script. My first run gave me an error "Get-Content : Cannot find path... because it doesn't exist. So I proceeded to add the -path switch in front of the file path and it executed with these errors "The RPC server is unavailable. (Exception from HRESULT: 0x80070...". I am thinking the servers firewall is preventing access. I will continue to research and update you later.

BrianswPHX said...

Hello Alex, how did you handle the systems that threw the RPC error? And, thank you very much for posting this!!

Alex Siow said...

Hi BrianwPHX, there are many things that can cause the RPC error such as firewall, required services (e.g. rpc, wmi, tcp/ip netbios helper) not running, or simply the host not reachable. For my case, the servers throwing this error are in the DMZ zone and not reachable. Since there are not a lot of them, I have manually updated them.

Pat said...

Very useful script. I slightly changed it and it worked like a charm.

If you want to REMOVE DNS server addresses on multiple remote servers just use this line instead of the one used in script:

$result = $nicConfig.SetDNSServerSearchOrder()

Alex Siow said...

Hi Pat, thanks for sharing the remove DNS tip.

Unknown said...

Great script, all I changed when I ran it was the DNS addresses and I get the error,

"Error: Cannot validate argument on parameter 'ComputerName'. The argument is null or empty. Supply an argument that is not null or empty and then try the command again."

Earn in Max said...

Sir You just simply rocks, you are my guru hats off to you. I am so happy and changed the DNS IP's in my project for all PROD and non-PROD servers. Its amazing. But what to to with the servers which are giving the error "RPC Server in Unavailable" ? Manually do i need to add or what ?