In this post, I will explain how you can easily change the IP configuration for a server's NIC from manual (or static or fixed) to a DHCP reservation. If you have to scale the procedure with many servers involved, you can automate the task with PowerShell.

Normally, your network addressing scheme is stable, and the switches and routers hum along steadily, undisturbed for years. You have 192-168-something-something / 24 subnets for clients, perhaps different subnets for servers and management, and even some external addresses for webservers, ADFS, or cloud interconnection. Some of those addresses may still be public IPv4 addresses.

And this leads us back to our problem: due to acquisitions, mergers, or more technical changes in infrastructure and so on, your customer (or boss or colleagues) may decide to change something major in the network addressing scheme. One of my customers decided to do just that recently, due to a new interconnection with a new institution.

For clients, that's usually easy because they get their IP Addresses from DHCP. Change the settings only once on the DHCP server, make sure their cluster partners replicate, and you're done. Servers, on the other hand, are a different story. Especially when you have many of them. This customer does. Many of them.

Solution: Automate

I thought this would be a good opportunity to try something from Azure that inspired me. When you work with VMs in Azure, you never set a fixed IP address on a VM's NIC. What you have instead is something like a permanent DHCP reservation.

Back to the solution: my idea is to create reservations for the servers' IP addresses on a DHCP server. This way, with every migration, I can quickly do all the configurations in a single place: the DHCP server(s). Also, all the common settings for each server (like, for example, DNS servers or default gateway) only need to be configured once and will propagate to all the DHCP clients.

If you don't trust DHCP servers (although you should, they've been stable and easy to cluster since Windows Server 2012), you can always fall back to manual configuration using the Alternate Configuration settings in the NIC's properties.

Alternate configuration for a NIC

Alternate configuration for a NIC

Get-IPAddressPretty (optional but recommended)

Throughout this post, IP addresses will be changed and checked frequently. Whenever you do this, if you prefer to see only the relevant information, you'd run a command like this:

Get-NetIPAddress -AddressFamily IPv4 -CimSession Mgmt,DC2 `
| select PSComputerName, InterfaceAlias, IPAddress, PrefixLength, PrefixOrigin `
| Where-Object InterfaceAlias -NotLike "*Loopback*" `
| Format-Table -Wrap -AutoSize

The result is something like in the figure below:

Retrieving IP address information with Get NetIPAddress

Retrieving IP address information with Get NetIPAddress

This provides all the information you need for our purposes (computer name, NIC name, IP address, subnet mask, and whether it's manual or assigned via DHCP). The only problem is that it's a long command to type each time.

To avoid all the typing, I created a function called Get-IPAddressPretty, which returns the same information without the need for all the things we want and the things we don't.

function Get-IPAddressPretty {
<# 
.SYNOPSIS
Outputs a nicer, more concise version of the command Get-NetIpAddress. If an address family (IPv4 or IPv6) is not specified, it outputs the IPv4Addresses. Also, it skips the Loopback address(es)
.EXAMPLE
Get-IPAddressPretty -CimSession ServerB
Displays the IPv4 Addresses of ServerB
.EXAMPLE
Get-IPAddressPretty -CimSession DC1,DC2 -AddressFamily IPv6
Displays the IPv6 Addresses of DC1 and DC2

.NOTES
Last revision on 24 Nov 2020
#>
Param (
    # Computer(s) for which the IP address info will be retrieved
    [Parameter(Mandatory=$false,Position=0)][array]$CimSession = $env:COMPUTERNAME,
    # Toggle between IPv4 and IPv6
    [Parameter(Mandatory=$false)][ValidateSet("IPv4","IPv6")][string]$AddressFamily = "IPv4",
    # A wildcard for a NIC alias. If none is provided, information for all the relevant NICs is displayed.
    [Parameter(Mandatory=$false)][string]$InterfaceAlias = "*"
)

# Only IPv4 is the default option.
if ($AddressFamily -eq "IPv4") {
    Get-NetIPAddress -CimSession $CimSession -AddressFamily $AddressFamily | 
        Where-Object { $_.InterfaceAlias -like "*$InterfaceAlias*" } | 
        Where-Object { $_.PrefixOrigin -eq "Manual" -or $_.PrefixOrigin -eq "Dhcp" } | 
        Select-Object PSComputerName,InterfaceAlias,IPAddress,PrefixLength,PrefixOrigin | 
        Sort-Object PSComputerName,InterfaceAlias |
        Format-Table -AutoSize -Wrap
    }
# Otherwise, retrieve the IPv6 information.
else {
    Get-NetIPAddress -CimSession $CimSession -AddressFamily $AddressFamily | 
        Where-Object { $_.InterfaceAlias -like "*$InterfaceAlias*" } | 
        select PSComputerName,InterfaceAlias,IPAddress,PrefixLength,PrefixOrigin | 
        Sort-Object PSComputerName,InterfaceAlias |
        Format-Table -AutoSize -Wrap
    }
} 
My Get IPAddressPretty function in action

My Get IPAddressPretty function in action

As you see, the result is the same as above, but the command is much shorter. By default, it displays the IPv4 addresses of all the NICs on the computer(s). You may get more granular if you want to.

Create a DHCP reservation

Note: This will work with Microsoft DHCP servers. It won't work on a Cisco switch that's configured to also be a DHCP server for a small subnet or other flavors of DHCP servers. However, the function/ commands may be adapted if those other flavors support PowerShell.

With this small disclaimer out of the way, let's look first at the logic of the function that creates the reservation.

The function

  • You provide a computer name and a network interface for which you want to create a reservation. You can also provide a new IP address if you don't want to create a reservation for the current IP address.
  • You also need to provide a DHCP server on which to create the reservation. The function is kind enough to provide auto-completion for the server name (use Tab or Ctrl+Space).
  • The function validates the name of the NIC on the specific server. If a valid name is not provided, the function will throw a warning to ask you for a valid name and will stop.
  • Once the NIC is validated, the function retrieves the MAC address for the NIC. This will be used later when the reservation is created.
  • If a specific IP was not provided, the function determines the existing IPv4 address of the NIC. The DHCP reservation will be created using the existing IP.
  • The function retrieves the subnets on the DHCP server you specified and will check in which subnet the IP Address can fit. The suitable scope is determined using a small function that checks whether your desired IP address for the reservation is found between the start and end IP address of each scope of the DHCP server. If no suitable scope is found for this IP reservation, the function will throw a warning and stop.
  • Once the function "has" the validated interface alias, the IP and MAC addresses, and the suitable DHCP scope, it will check to see whether there isn't already an existing reservation for this MAC address. If there is, you receive a warning and a suggestion to remove the existing reservation if you want to create a new one.
  • Finally, if there is no reservation already, one will be created using the information provided. After the reservation is created, the NIC will be configured for DHCP, and the DHCP lease will be renewed (using the good old ipconfig tool).

The code

This is the code that makes it all happen:

function New-Ipv4ToDhcpReservation {

[cmdletbinding()]

Param (
    # Computer(s) for which the IP address info will be retrieved
    [Parameter(Mandatory=$false,Position=0)][string]$ComputerName = $env:COMPUTERNAME,
    # List of authorized DHCP servers in AD
    [ArgumentCompleter({ (Get-DhcpServerInDC).DnsName })][string]$DhcpServer,
    # Name of the NIC for which the reservation will be created
    [Parameter(Mandatory)][string]$InterfaceAlias,
    # Desired IP address for the reservation. If none is provided, the current IP address of the NIC will be used.
    [Parameter(Mandatory=$false)][ipaddress]$IpAddress
)

#region additional functions

# This small function converts an IP address to an Int64 value based on the provided IP 
Function Convert-IpToInt64 () { 
    param ([String]$Ip) 
    process { 
        $Octets = $Ip.split(".") 
        return [int64]([int64]$Octets[0]*16777216 +[int64]$Octets[1]*65536 +[int64]$Octets[2]*256 +[int64]$Octets[3]) 
    }
} 

#endregion

#region Validate and retrieve the NIC info

# Check if the interface alias provided for the specific computer is valid, and stop if it isn't
[string]$Nic = (Get-NetAdapter -CimSession $ComputerName | Where-Object InterfaceAlias -Like "$InterfaceAlias").InterfaceAlias
if (!($Nic)) {
    Write-Warning "The Nic name you provided is not valid for the computer name $ComputerName. Please provide a valid Interface Alias"
    break
    }

Write-Verbose "The NIC `"$Nic`" for the computer $ComputerName is valid, continuing."

# Get the MAC address of the NIC
$MacAddress = (Get-NetAdapter -CimSession $ComputerName -InterfaceAlias $InterfaceAlias).MacAddress
Write-Verbose "MAC address for the reservation: $MacAddress"

#endregion

#region Select the IP address for the reservation
# Use the current IP address of the NIC if no IP address is provided
if (!($IpAddress)) {
    [string]$Ip = (Get-NetIPAddress -CimSession $ComputerName -AddressFamily IPv4 -InterfaceAlias $Interfacealias).IPAddress
    [int]$CurrentIp = Convert-IpToInt64 -Ip $Ip
    Write-Host -ForegroundColor Magenta $Ip
    Write-Verbose "IP address for the reservation: $Ip"
    }
else {
    [string]$Ip = $IPAddress.IPAddressToString
    $CurrentIp = Convert-IpToInt64 -Ip $Ip
    Write-Verbose "IP address for the reservation: $Ip"
    }

#endregion

#region Select the target scope for the reservation
# Retrieve the list of scopes on the DHCP server
$DhcpScopes = (Get-DhcpServerv4Scope -ComputerName $DhcpServer)

# Go through each to determine in which scope the DHCP reservation will be created
foreach ($s in $DhcpScopes) {
    $netMaskIP=$s.Subnetmask.Address
    $HostBytes = 32 - ([convert]::ToString($netMaskIP,2)).length

    [int]$StartIp = Convert-IpToInt64 -ip (($s).ScopeId.IPAddressToString)
    [int]$EndIp = $StartIp + [math]::Pow(2,$HostBytes) - 1
    
    if ($CurrentIp -in ($StartIp .. $EndIp)) {
        $Scope = $s.ScopeId
        Write-Verbose "The selected IP address ($Ip) falls within the target scope $Scope"
        }
    }
if (!($Scope)) {
    Write-Warning "No suitable Scope can accomodate the IP Address $IpAddress! Please select an IP address to fit one the DHCP Scopes on `'$DhcpServer`' or select a different DHCP server"
    break
    }
#endregion

#region Create the DHCP reservation and configure the NIC for DHCP
# Create the reservation on the DHCP server if one already doesn't already exist for that specific MAC address
if (!(Get-DhcpServerv4Reservation -ComputerName $DhcpServer -ScopeId $Scope | Where-Object ClientId -EQ $MacAddress)) {
    Write-Verbose "Create the reservation on the DHCP server `'$DhcpServer`' for Computer `'$ComputerName`' (for interface `'$InterfaceAlias`')"
    # Create the reservation
    Add-DhcpServerv4Reservation -ComputerName $DhcpServer -ScopeId $Scope -IPAddress $Ip -ClientId $MacAddress -Name "Computer: $ComputerName on Interface: $InterfaceAlias" -Description "Created on $(Get-Date -Format 'dd MMM yyyy') using New-Ipv4ToDhcpReservation" -ErrorAction Stop
    # Configure the target NIC on the target computer for DHCP
    Write-Verbose "Configure the NIC $InterfaceAlias on Computer $ComputerName for DHCP"
    Set-NetIPInterface -CimSession $ComputerName -InterfaceAlias $InterfaceAlias -DHCP Enabled
    Write-Verbose "Renew the DHCP lease for the computer $ComputerName"
    Invoke-Command -ComputerName $ComputerName -ScriptBlock {ipconfig /renew} | Out-Null
    }

else {Write-Warning "A reservation already exists in the scope $Scope for $MacAddress. Remove the existing reservation before continuing"}
#endregion
}

See it in action

Now that you know what New-Ipv4ToDhcpReservation does, let's see it in action.

Example 1

New IpToDhcpv4Reservation Example 1

New IpToDhcpv4Reservation Example 1

# Get the current IP configuration
Get-IPAddressPretty -CimSession MGMT
# INCORRECT NIC NAME: Try to create a DHCP reservation using the wrong name of the NIC
New-IpToDhcpv4Reservation -ComputerName MGMT -InterfaceAlias Eth0 -DhcpServer dc2
# CORRECT: Create a DHCP reservation using the correct name of the NIC
New-Ipv4ToDhcpReservation -DhcpServer dc2 -InterfaceAlias Ethernet -Verbose
# Get the updated IP configuration
Get-IPAddressPretty -CimSession MGMT

In this first example, first you see the current configuration. Then, the function is used to create a DHCP reservation. First, it fails, because an incorrect NIC name was provided. Then, it succeeds when the NIC name is correct. You can also see the before and after configurations. You may also notice that the new IP address comes from a DHCP server (see the column Prefix Origin, which is now DHCP instead of manual).

DHCP Reservation Example 1 – GUI

DHCP Reservation Example 1 – GUI

This is the reservation in the DHCP server console. The reservation displays the name of the computer for which it is created, as well as the name of the NIC. The description displays the date when the reservation was created (and that it was created using New-Ipv4ToDhcpReservation).

Example 2

In this second example, we change to create a reservation for the same server (this environment is not too rich in VMs), this time using a different IP address. As a bonus, we will use the Verbose switch to display more information about what the function New-Ipv4ToDhcpReservation is doing.

New IpToDhcpv4Reservation Example 2 Verbose

New IpToDhcpv4Reservation Example 2 Verbose

New-IpToDhcpv4Reservation -ComputerName MGMT -InterfaceAlias Ethernet -DhcpServer dc2 -IpAddress 10.0.2.88 -Verbose 

Using the Verbose switch displays more information about what's happening "under the hood."

DHCP Reservation Example 2 – GUI

DHCP Reservation Example 2 – GUI

As you may notice, this time the reservation was created in a different scope, based on the desired IP address.

Conclusion

This little function can make your life easier in case you need to make a lot of IP address changes for a lot of servers. Just run it for all the relevant computers, then make your network changes, and the DHCP-managed computers will acquire those automatically.

Subscribe to 4sysops newsletter!

If you find it useful, a future post may include a symmetrical function that converts DHCP reservations to static IP addresses. Until then, more PowerShell and less clicking.

avataravatar
11 Comments
  1. I need to convert about a 100 static IP workstations to DHCP with a reservation: However we have a Linux DHCP server.

    • Author

      Hello, Robert.

      This will not work with your Linux DHCP server, as the function is using commands from the DHCP Server PowerShell module (I mentioned in the article it only works with Microsoft's own DHCP server implementation). Most likely these commands will not work on other flavours of DHCP servers.

      If your Linux DHCP server provides a PowerShell module for administration, you could tweak the script in this post to match those commands.

      Another option would be to build a temporary Windows Server 201x DHCP server, create the reservations and then migrate the whole configuration (including reservations) to your Linux server, if such a migration tool is available.

      Cheers. Emanuel

      avatar
  2. David Stevens 2 years ago

    Can I set this up so that it gets a DHCP address, and if the dhcp server is down it will revert to the alternate configuration?

    I can't find anything in the powershell set-ipaddress that does the alternative one.

    Does it work like that? With the alternate taking over if the dhcp isn't there? or would both IP's be active if I changed the reservation.

    • Author

      Hi, David.

      I couldn't find a way to set an alternate IP Address (that would take over in case a DHCP server cannot be reached) via PowerShell. 

      I've seen a lot of VBScripts that can do it, which I would not call ideal. At the end of the day -as almost anything that's configurable in Windows- there's a registry key for that, and you could use PowerShell to populate that registry key with an alternate configuration. The link below is a nice starting point for that, since it mentions the registry keys.

      Is it possible to configure a static IP alternative for all adapters using the registry? – Super User

      I hope it helps.

      Cheers! Emanuel

      • David Stevens 2 years ago

        Not a lot unfortunately.

        Can you confirm the behavior of the alternate ip? Will I still get a dhcp address and then fail-over to that if it doesn't get a DHCP?

  3. David Stevens 2 years ago

    Oh, I see it doesn't change the DNS settings to be DHCP.
    Can you tell me how to blip the code to do that?

  4. Olivier 2 years ago

    hello Emmanuel
    Your function is very interesting and could be used in the following scenario:
    I have in an OU, a set of machines in fixed IP (for application reasons) and I would like to transform them into reserved DHCP IPs.
    In principle, script using your function. This script is executed by GPO (LogonScript), therefore without human interaction.

    Several questions, however, deserve attention:

    1. – The active network interface must be determined without human interaction. If we take a laptop PC, it still has 2 network interfaces (wired and WIFI) but only one is active at any given time. We will say that it is the wired interface only that must be switched to IP reservation. This point is the most delicate, because there can also be workstations with a Hyper-V role, and therefore Virtual NetAdapter (virtual switch).
    2. – I have several DHCP servers (in DHCP failover): in this case, must I hard-code in the function, a specific DHCP server to use. The Get-DHCPinDC cmdlet returns me the 2 authorized DHCP servers of the domain.
    3. – Finally, switching an IP configuration to DHCP is very good thing (and in particular for management), however, do not forget to blank the DNS entries otherwise the local DNS entries take precedence over the same parameters provided by DHCP. This point is an important oversight of your function, easy to correct however (Clear-DnsClientCache).

    I would like to have your advice on the first 2 points. The 3rd being, I think, an improvement to bring to your function.

    Regards
    Olivier

    • Author

      Hi, Oliver.

      1. Maybe it helps to use Get-NetAdapter -Physical and start from there? The switch -Physical filters the non-physical NICs.

      2. If you have a load-balancing cluster for DHCP, just do it on one server, it will replicate to the other(s). You can add at the beginning of your script something like $DhcpServer = (Get-DhcpServerInDC)[0], which picks only one DHCP server from the list. If you have an active-passive configuration, make sure you change your script to pick the active DHCP server.

      3. Clear-DNSClientCache is the equivalent of ipconfig /flushdns, if I remember correctly. After a reboot it doesn't really matter if I didn't flush the cache, but yes, you are correct. I use Clear-DNSClientCache a lot in other scripts that involve operations with DNS, for example when I change the IP Address of a remote computer (in order to connect to the proper server after its IP Address has changed).

      I hope 1) and 2) help. And thank you for the suggestion for 3).

      Cheers!

  5. Olivier 2 years ago

    Hi again Emmanuel,

    Hmm another point of improvement.

    Your function is a modification function (usinh the verb New), so it should support "SupportsShouldProcess" (and therefore have the common parameter -Whatif).

    Regards
    Olivier

  6. Olivier 2 years ago

    Hi Again Emmanuel,

    Hmm another point of improvement.

    Your function is a modification function (using the verb New), so it should support "SupportsShouldProcess" (and therefore have the common parameter -Whatif).

    Regards
    Olivier

    • Author

      Hello (again), Olivier.

      Well, I'm quite sure there is a lot of room for improvement in my scripts in general. 🙂

      -WhatIf would be great, but I didn't think of it at the time. If at some point in the future I decide to improve this funtion, I will keep it in mind (as well as your other suggestion). 

      Cheers! Emanuel

Leave a reply

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2023

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account