Easily change DNS servers for computers with static IP addresses using PowerShell. Run locally or remote.
Changing DNS servers
Recently I provisioned new domain controllers as part of a migration from a 2008 R2 Active Directory forest to a 2016 Active Directory forest. Like the existing 2008 R2 domain controllers, the new domain controllers are configured as AD integrated DNS servers and will be the primary and secondary DNS servers used on the internal network.
Changing DNS servers for clients using DHCP is a trivial matter; just update Option 6 for the DHCP scope with the new name server IP addresses and restart the client (or wait until they renew their lease).
However if you are like a lot of environments, you have Windows servers and maybe even workstations configured with static IP addresses and static DNS servers. How can you systematically update these configurations?
Change DNS servers with Win32_NetworkAdapterConfiguration
We can use PowerShell and Get-WmiInstance or Get-CimInstance (Win 8/2012 or later). For compatibility, we will be demonstrating using Get-WmiInstance.
In our scenario, our old DNS servers were 192.168.1.11 and 192.168.1.12. We will be replacing these with 192.168.1.13 and 192.168.1.14. We use the following snippet leveraging the class Win32_NetworkAdapterConfiguration to return a list of all non-DHCP adapters with DNS servers configured.
$Adapters = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.DHCPEnabled -ne 'True' -and $_.DNSServerSearchOrder -ne $null}
To see the current DNS servers, we run the following:
PS C:\Windows\system32> $Adapters | ForEach-Object {$_.DNSServerSearchOrder} 192.168.1.11 192.168.1.12
To update the DNS servers for adapters meeting this criteria, we run the following:
$NewDnsServerSearchOrder = "192.168.1.13","192.168.1.14" $Adapters | ForEach-Object {$_.SetDNSServerSearchOrder($NewDnsServerSearchOrder)} | Out-Null
To confirm the servers have been updated, re-run our original Get sequence:
$Adapters = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.DHCPEnabled -ne 'True' -and $_.DNSServerSearchOrder -ne $null} PS C:\Windows\system32> $Adapters | ForEach-Object {$_.DNSServerSearchOrder} 192.168.1.13 192.168.1.14
Remotely updating DNS servers for set of computers
The previous section covered the basic updating of the DNS servers locally on a single computer. However you may have the need to update multiple computers remotely.
One approach would be to query your Active Directory to get a list of all computers that have statically assigned IP addresses. For example, it may be that all of your computers running a Windows Server OS have static IPs.
Get all “Windows Server” AD computers
We can get all of the the computers in Active Directory that are running a “Windows Server” build using the following Get-ADComputer query:
$Computers = Get-ADComputer -Filter '(OperatingSystem -like "Windows Server*")' | Sort-Object Name
Use Invoke-Command to update DNS on remote computers
We now have a set of computers to process. We will use Invoke-Command to connect via WinRM to the remote computer and execute the previous commands locally on the computer.
ForEach ($Computer in $Computers) { Write-Host "$($Computer.Name): " -ForegroundColor Yellow Invoke-Command -ComputerName $Computer.Name -ScriptBlock { $NewDnsServerSearchOrder = "192.168.1.13","192.168.1.14" $Adapters = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.DHCPEnabled -ne 'True' -and $_.DNSServerSearchOrder -ne $null} # Show DNS servers before update Write-Host "Before: " -ForegroundColor Green $Adapters | ForEach-Object {$_.DNSServerSearchOrder} # Update DNS servers $Adapters | ForEach-Object {$_.SetDNSServerSearchOrder($NewDnsServerSearchOrder)} | Out-Null # Show DNS servers after update $Adapters = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.DHCPEnabled -ne 'True' -and $_.DNSServerSearchOrder -ne $null} Write-Host "After: " -ForegroundColor Green $Adapters | ForEach-Object {$_.DNSServerSearchOrder} } }
Why we used Invoke-Command
We could use the WMI cmdlets directly against the remote computer, but it would require all of the necessary network port access and permissions for using WMI remotely. Connecting using WinRM only requires a single network port to be opened and lets us run all other cmdlets without having to give a lot of thought to remote security and performance over the network.
Set-DnsClientServerAddress: Windows 8/2012 or newer
The method described above is intended for maximum compatibility: it will work with Windows Server 2003 all the way through Windows Server 2016. If however your environment is running Windows 8 / Windows Server 2012 and above, you do not need to use the WMI cmdlets, you can use the newer cmdlet Set-DnsClientServerAddress.
Set-DnsClientServerAddress -InterfaceIndex 2 -ServerAddresses ("192.168.1.13","192.168.1.14")
Note that we specified the InterfaceIndex. Other interface identification options are InterfaceAlias or you can pass a CimInstance for an interface to the cmdlet. You could use this cmdlet in hybrid with our previous Get-WmiObject query:
$Adapters = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.DHCPEnabled -ne 'True' -and $_.DNSServerSearchOrder -ne $null} $Adapters | Set-DnsClientServerAddress -ServerAddresses "192.168.1.13","192.168.1.14"
Conclusion
Using this approach as a starting point, you can modify as required fit your own environment’s requirements to confidently and efficiently update DNS servers for computers with statically assigned IPs.
Herb Martin says
For big companies this is likely to fail on older computers — and they are still likely to be around.
Fall back to NetSh, it’s on almost everything and PowerShell can drive it easily enough.
Aaron Rothstein says
This method will work for Windows XP / 2003 computers if they have WMF installed and configured. You are right though, anything older than that and you will have to resort to legacy tool sets.
Mike says
HI —
I am new to PowerShell and Windows DC’s but I need help simply changing the DC static IP to another Static IP. It seems you are using DHCP here and I don’t think that will work. In my use-case I am moving a DC from one environment to another. I need to be able to apply the changes remotely. Using Infrastructure as code (IaC) e.g. aws CloudFormation.
Thanks!
Mike
Aaron Rothstein says
Hey Mike,
I’m guessing you have already addressed your issue, but for changing the IP address of a host (DC or otherwise), you would use the cmdlet Set-NetIPAddress (https://docs.microsoft.com/en-us/powershell/module/nettcpip/set-netipaddress?view=win10-ps).
For a domain controller, here are additional recommended steps from a TechNet article that is no longer available:
“After you change the IP address of a domain controller, you should run the ipconfig /registerdns command to register the host record and dcdiag /fix command to ensure that service records are appropriately registered with DNS”
Greg says
Good afternoon,
I have been trying to turn part of this into a script that I can setup in Group Policy and push out network wide, but I am having trouble.
The part I am using is:
$NewDnsServerSearchOrder = “192.168.1.13”,”192.168.1.14″
$Adapters | ForEach-Object {$_.SetDNSServerSearchOrder($NewDnsServerSearchOrder)} | Out-Null
Whenever I try to run it in PS ISE I get the following error: “You cannot call a method on a null-valued expression”
I am not sure what I am doing wrong here because it runs perfectly fine in a PS window. Any Suggestions?
Aaron Rothstein says
Hi Greg,
Replying here as well, we can continue the conversation either on Facebook or here, whichever you prefer.
It appears $Adapters is null. Can you confirm nothing is being returned when running the following in the ISE:
Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.DHCPEnabled -ne ‘True’ -and $_.DNSServerSearchOrder -ne $null}
Peter says
I am not a PS expert – How would the above scripts work on servers that have multiple NICs in various VLANs if I only want to change DNS on a specific NIC? And are there arguments to provide credentials if I am remotely acting on servers requiring different administrative credentials for making these modifications?
Aaron Rothstein says
You would want to look at doing some additional filtering during the initial querying, under the ‘Where-Object’ call. For example, you could query by adapter name or other properties:
$Adapters = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.DHCPEnabled -ne ‘True’ -and $_.DNSServerSearchOrder -ne $null}