Network Adapter Teaming in MEMCM (aka 'ConfigMgr') OSD

NetAdapter Teaming in OSD has always challenged me, especially when building out our own servers.   I've always relied on working with just one network adapter throughout the imaging process when building our servers.  Then manually teaming the adapters post imaging.  I've never been able to figure out how to automate this process within imaging thru OSD itself.  And there's not a lot of articles/blogs out there around this area either.  Until here recently I had a 'light bulb' moment... So this how I went about making teaming to work...

Let's start off with how to deal with multiple adapters in OSD Task Sequence. 

By default with MDT, it is only configured to recognize a single adapter using OSDAdapter0xxx variable which is set in ZTIGather.xml.  So in order to work with multiple adapters, the xml file will need to be extended.  To accomplish that, navigate to the Toolkit\Scripts folder, and edit the ZTIGather.xml file (after taking a backup of this file, of course).  Copy the whole section as shown below, and paste it directly below it and edit all lines with "OSDAdapter0xxxx" to "OSDAdapter1xxxx", and repeat and number accordingly, depending on how many adapters you want to support.  Save the file So you would end up with a new ZTIGather.xml much like the screen shot below.

(Copy this section in ZTIGather.xml, paste it below this section to add variables for additional adapters.)

 <property id="OSDAdapter0Name" type="string" overwrite="false" description="If present, match all settings to the adapter with this name." />
 <property id="OSDAdapter0MacAddress" type="string" overwrite="false" description="If present, match all settings to the adapter with this MAC address." />
 <property id="OSDAdapter0EnableDHCP" type="string" overwrite="false" description="If false, will disable DHCP, otherwise True (true if blank)." />
 <property id="OSDAdapter0IPAddressList" type="string" overwrite="false" description="Comma delimited list of IPAddress Lists" />
 <property id="OSDAdapter0SubnetMask" type="string" overwrite="false" description="Comma delimited list of Subnet masks" />
 <property id="OSDAdapter0Gateways" type="string" overwrite="false" description="Comma delimited list of Gateway cost metrics" />
 <property id="OSDAdapter0GatewayCostMetric" type="string" overwrite="false" description="Comma delimited list of Gateway Cost MEtrics as either integers, or the string 'Automatic' (if empty, uses automatic)" />
 <property id="OSDAdapter0DNSServerList" type="string" overwrite="false" description="Comma delimited list of DNS Servers" />
 <property id="OSDAdapter0DNSSuffix" type="string" overwrite="false" description="DNS Suffix, example Frabrikam.com" />
 <property id="OSDAdapter0EnableDNSRegistration" type="string" overwrite="false" description="True/False to enable DNS registration." />
 <property id="OSDAdapter0EnableFullDNSRegistration" type="string" overwrite="false" description="True/False to enalbe Full DNS Registration" />
 <property id="OSDAdapter0EnableLMHOSTS" type="string" overwrite="false" description="True/False to enable LMHosts" />
 <property id="OSDAdapter0EnableWINS" type="string" overwrite="false" description="True/False to enable WINS" />
 <property id="OSDAdapter0TcpipNetbiosOptions" type="string" overwrite="false" description="NetBIOS Options 0/DHCP/False, 1/True, or 2/Disable" />
 <property id="OSDAdapter0WINSServerList" type="string" overwrite="false" description="Comma delimited list of WINS Servers" />
 <property id="OSDAdapter0EnableTCPIPFiltering" type="string" overwrite="false" description="True/False to enable TCP/IP Filtering." />
 <property id="OSDAdapter0TCPFilterPortList" type="string" overwrite="false" description="Comma delimited list of TCP Filters" />
 <property id="OSDAdapter0UDPFilterPortList" type="string" overwrite="false" description="Comma delimited list of IDP Filters" />
 <property id="OSDAdapter0IPProtocolFilterList" type="string" overwrite="false" description="Comma delimited list of IP Protocol FIlters" />

ZTIGather.xml end result.

 <!-- Action Properties (commonly defined within Management Console) -->
 <property id="OSDAdapterCount" type="string" overwrite="false" description="Number of Adapters defined here( either blank, 0 or 1)" />
 <property id="OSDAdapter0Name" type="string" overwrite="false" description="If present, match all settings to the adapter with this name." />
 <property id="OSDAdapter0MacAddress" type="string" overwrite="false" description="If present, match all settings to the adapter with this MAC address." />
 <property id="OSDAdapter0EnableDHCP" type="string" overwrite="false" description="If false, will disable DHCP, otherwise True (true if blank)." />
 <property id="OSDAdapter0IPAddressList" type="string" overwrite="false" description="Comma delimited list of IPAddress Lists" />
 <property id="OSDAdapter0SubnetMask" type="string" overwrite="false" description="Comma delimited list of Subnet masks" />
 <property id="OSDAdapter0Gateways" type="string" overwrite="false" description="Comma delimited list of Gateway cost metrics" />
 <property id="OSDAdapter0GatewayCostMetric" type="string" overwrite="false" description="Comma delimited list of Gateway Cost MEtrics as either integers, or the string 'Automatic' (if empty, uses automatic)" />
 <property id="OSDAdapter0DNSServerList" type="string" overwrite="false" description="Comma delimited list of DNS Servers" />
 <property id="OSDAdapter0DNSSuffix" type="string" overwrite="false" description="DNS Suffix, example Frabrikam.com" />
 <property id="OSDAdapter0EnableDNSRegistration" type="string" overwrite="false" description="True/False to enable DNS registration." />
 <property id="OSDAdapter0EnableFullDNSRegistration" type="string" overwrite="false" description="True/False to enalbe Full DNS Registration" />
 <property id="OSDAdapter0EnableLMHOSTS" type="string" overwrite="false" description="True/False to enable LMHosts" />
 <property id="OSDAdapter0EnableWINS" type="string" overwrite="false" description="True/False to enable WINS" />
 <property id="OSDAdapter0TcpipNetbiosOptions" type="string" overwrite="false" description="NetBIOS Options 0/DHCP/False, 1/True, or 2/Disable" />
 <property id="OSDAdapter0WINSServerList" type="string" overwrite="false" description="Comma delimited list of WINS Servers" />
 <property id="OSDAdapter0EnableTCPIPFiltering" type="string" overwrite="false" description="True/False to enable TCP/IP Filtering." />
 <property id="OSDAdapter0TCPFilterPortList" type="string" overwrite="false" description="Comma delimited list of TCP Filters" />
 <property id="OSDAdapter0UDPFilterPortList" type="string" overwrite="false" description="Comma delimited list of IDP Filters" />
 <property id="OSDAdapter0IPProtocolFilterList" type="string" overwrite="false" description="Comma delimited list of IP Protocol FIlters" />
 
 <property id="OSDAdapter1Name" type="string" overwrite="false" description="If present, match all settings to the adapter with this name." />
 <property id="OSDAdapter1MacAddress" type="string" overwrite="false" description="If present, match all settings to the adapter with this MAC address." />
 <property id="OSDAdapter1EnableDHCP" type="string" overwrite="false" description="If false, will disable DHCP, otherwise True (true if blank)." />
 <property id="OSDAdapter1IPAddressList" type="string" overwrite="false" description="Comma delimited list of IPAddress Lists" />
 <property id="OSDAdapter1SubnetMask" type="string" overwrite="false" description="Comma delimited list of Subnet masks" />
 <property id="OSDAdapter1Gateways" type="string" overwrite="false" description="Comma delimited list of Gateway cost metrics" />
 <property id="OSDAdapter1GatewayCostMetric" type="string" overwrite="false" description="Comma delimited list of Gateway Cost MEtrics as either integers, or the string 'Automatic' (if empty, uses automatic)" />
 <property id="OSDAdapter1DNSServerList" type="string" overwrite="false" description="Comma delimited list of DNS Servers" />
 <property id="OSDAdapter1DNSSuffix" type="string" overwrite="false" description="DNS Suffix, example Frabrikam.com" />
 <property id="OSDAdapter1EnableDNSRegistration" type="string" overwrite="false" description="True/False to enable DNS registration." />
 <property id="OSDAdapter1EnableFullDNSRegistration" type="string" overwrite="false" description="True/False to enalbe Full DNS Registration" />
 <property id="OSDAdapter1EnableLMHOSTS" type="string" overwrite="false" description="True/False to enable LMHosts" />
 <property id="OSDAdapter1EnableWINS" type="string" overwrite="false" description="True/False to enable WINS" />
 <property id="OSDAdapter1TcpipNetbiosOptions" type="string" overwrite="false" description="NetBIOS Options 0/DHCP/False, 1/True, or 2/Disable" />
 <property id="OSDAdapter1WINSServerList" type="string" overwrite="false" description="Comma delimited list of WINS Servers" />
 <property id="OSDAdapter1EnableTCPIPFiltering" type="string" overwrite="false" description="True/False to enable TCP/IP Filtering." />
 <property id="OSDAdapter1TCPFilterPortList" type="string" overwrite="false" description="Comma delimited list of TCP Filters" />
 <property id="OSDAdapter1UDPFilterPortList" type="string" overwrite="false" description="Comma delimited list of IDP Filters" />
 <property id="OSDAdapter1IPProtocolFilterList" type="string" overwrite="false" description="Comma delimited list of IP Protocol FIlters" />
 
 <property id="OSDAdapter2Name" type="string" overwrite="false" description="If present, match all settings to the adapter with this name." />
 <property id="OSDAdapter2MacAddress" type="string" overwrite="false" description="If present, match all settings to the adapter with this MAC address." />
 <property id="OSDAdapter2EnableDHCP" type="string" overwrite="false" description="If false, will disable DHCP, otherwise True (true if blank)." />
 <property id="OSDAdapter2IPAddressList" type="string" overwrite="false" description="Comma delimited list of IPAddress Lists" />
 <property id="OSDAdapter2SubnetMask" type="string" overwrite="false" description="Comma delimited list of Subnet masks" />
 <property id="OSDAdapter2Gateways" type="string" overwrite="false" description="Comma delimited list of Gateway cost metrics" />
 <property id="OSDAdapter2GatewayCostMetric" type="string" overwrite="false" description="Comma delimited list of Gateway Cost MEtrics as either integers, or the string 'Automatic' (if empty, uses automatic)" />
 <property id="OSDAdapter2DNSServerList" type="string" overwrite="false" description="Comma delimited list of DNS Servers" />
 <property id="OSDAdapter2DNSSuffix" type="string" overwrite="false" description="DNS Suffix, example Frabrikam.com" />
 <property id="OSDAdapter2EnableDNSRegistration" type="string" overwrite="false" description="True/False to enable DNS registration." />
 <property id="OSDAdapter2EnableFullDNSRegistration" type="string" overwrite="false" description="True/False to enalbe Full DNS Registration" />
 <property id="OSDAdapter2EnableLMHOSTS" type="string" overwrite="false" description="True/False to enable LMHosts" />
 <property id="OSDAdapter2EnableWINS" type="string" overwrite="false" description="True/False to enable WINS" />
 <property id="OSDAdapter2TcpipNetbiosOptions" type="string" overwrite="false" description="NetBIOS Options 0/DHCP/False, 1/True, or 2/Disable" />
 <property id="OSDAdapter2WINSServerList" type="string" overwrite="false" description="Comma delimited list of WINS Servers" />
 <property id="OSDAdapter2EnableTCPIPFiltering" type="string" overwrite="false" description="True/False to enable TCP/IP Filtering." />
 <property id="OSDAdapter2TCPFilterPortList" type="string" overwrite="false" description="Comma delimited list of TCP Filters" />
 <property id="OSDAdapter2UDPFilterPortList" type="string" overwrite="false" description="Comma delimited list of IDP Filters" />
 <property id="OSDAdapter2IPProtocolFilterList" type="string" overwrite="false" description="Comma delimited list of IP Protocol FIlters" />

Save the ZTIGather.xml file, then update the MDT toolkit package on all your DPs.   

We can now make use of those additional variables by defining them in the CM Console for your target servers (or workstations) objects.

So for this example, in my lab, I have setup a HyperV guest (CMTEST02), the server I am building with 3 network adapters with one valid IPv4 address that I need to use.  I have also imported its computer object in CM, along with the necessary OSDAdapter variables and settings accordingly for each adapter I have setup on my HyperV guest server.  Each of the 3 adapters (shown below) is named accordingly and defined via MacAddress.  I used the %MacAddress001% value for the first adapter, since I know it's going to grab that very first one during imaging.  Then I defined the rest with their respective MacAddresses and simply setting those to DHCP.  You could use %MacAddress002%, %MacAddress003% (and so on) values for additional adapters, but I wanted to be able to control which MacAddress goes to which adapter in this case.

IMPORTANT NOTE: OSDAdapterCount is the variable that defines the total # of Network Adapters you want to use.  Again, in this case, I want to define 3 since I'm using all 3 adapters in my Team.

Now that I have my HyperV guest built, and computer object imported in CM with all the variables I need to build this server.  It's time to make use of these in the Task Sequence...

Within my "Server 19 Deploy" Task Sequence, I'm calling a separate Task Sequence module that I put together to deal with network adapter settings, and teaming.  So throughout the entire OS provisioning process I'm only using the first network adapter defined (OSDAdapter0).  Then once that stage is done, I can now set my NIC Teaming and leverage it for the rest of the imaging process. This is great for adding more speed and redundancy during the installation of software updates and layered apps!

So let's take a deep dive on what this Task Sequence module does, shall we...   This is where the NIC Teaming magic happens, btw :).   (Oh and with the addition in CM for being able to easily add POSH codes in Task Sequences, it made this SO much more manageable.  Big thanks to my buddy Mike Terrill for submitting this as a Hackathon and for the @ConfigMgr team for making this happen!)

In this module, "Server 19 Module - NIC Settings", I simply perform these 3 PowerShell scripts...

Step 1: I clear the gateway then enable DHCP on the first or primary Network adapter. NOTE: Removing the gateway first is crucial to this process.  There's been instances where the gateway is left in the settings while in DHCP, then the adapter would not bind successfully to the Team.

$tsenv = New-Object -ComObject Microsoft.SMS.TSEnvironment
$Net1 = $tsenv.Value("OSDAdapter0Name")
$IPType = "IPv4"
$NIC1 = (Get-NetAdapter | ? {$_.Status -eq "up"} | ? {$_.Name -eq $Net1})
$interface = $NIC1 | Get-NetIPInterface -AddressFamily $IPType
If ($interface.Dhcp -eq "Disabled") {
    # Remove the existing gateway
    If (($interface | Get-NetIPConfiguration).Ipv4DefaultGateway) {
        $interface | Remove-NetRoute -Confirm:$false
    }
    # Enable DHCP here then set DNS
    $interface | Set-NetIPInterface -DHCP Enabled
    $interface | Set-DnsClientServerAddress -ResetServerAddresses
}

 

Step 2: I then set the NIC teaming, by grabbing the variables I'm using for the adapters, and using them to set the teaming.  I also added a sleep statement below to allow for the adapters to bind to the Team properly before I assign static address (Step 3).  Note that the different modes load balancing algorithms can also be used with 'New-NetLbfoTeam'.  Here's a link to the doc site for more options.

$tsenv = New-Object -ComObject Microsoft.SMS.TSEnvironment
$Net1 = $tsenv.Value("OSDAdapter0Name")
$Net2 = $tsenv.Value("OSDAdapter1Name")
$Net3 = $tsenv.Value("OSDAdapter2Name")
$Team = $tsenv.Value("TeamName")
 
New-NetLbfoTeam $Team $Net1,$Net2,$Net3 -Confirm:$false
start-sleep -seconds 45

 

Step 3: Set IPv4 and v6 (if available).  Here, I'm once again making use of the OSD variables I set up initially, then applying those IP settings accordingly.   I've also added the code below for assigning static IpV6 to the Teamed NIC!

$tsenv = New-Object -ComObject Microsoft.SMS.TSEnvironment
$Adap0IP = $tsenv.Value("OSDAdapter0IPAddressList")
$Adap0Mask = $tsenv.Value("OSDAdapter0SubnetMask")
$Adap0GW = $tsenv.Value("OSDAdapter0Gateways")
$Adap0DNSList = $tsenv.Value("OSDAdapter0DNSServerList")
$Adap0DNSDomain = $tsenv.Value("OSDAdapter0DNSDomain")
$Team = $tsenv.Value("TeamName")
#$IPv6Add = $tsenv.Value("IPv6Address")
#$IPv6Length = $tsenv.Value("IPv6Length")
#$IPv6Gate = $tsenv.Value("IPv6Gateway")
#$IPv6DNS1 = $tsenv.Value("IPv6DNS1")
#$IPv6DNS2 = $tsenv.Value("IPv6DNS2")
 
# Need to figure out the appropriate Maskbits based on OSDAdapter0Mask
if ($Adap0Mask -eq "255.255.255.252"){$MaskBits = 30}
Elseif ($Adap0Mask -eq "255.255.255.248"){$MaskBits = 29}
Elseif ($Adap0Mask -eq "255.255.255.240"){$MaskBits = 28}
Elseif ($Adap0Mask -eq "255.255.255.224"){$MaskBits = 27}
Elseif ($Adap0Mask -eq "255.255.255.192"){$MaskBits = 26}
Elseif ($Adap0Mask -eq "255.255.255.128"){$MaskBits = 25}
Elseif ($Adap0Mask -eq "255.255.255.0"){$MaskBits = 24}
Elseif ($Adap0Mask -eq "255.255.254.0"){$MaskBits = 23}
Elseif ($Adap0Mask -eq "255.255.252.0"){$MaskBits = 22}
Elseif ($Adap0Mask -eq "255.255.248.0"){$MaskBits = 21}
Elseif ($Adap0Mask -eq "255.255.240.0"){$MaskBits = 20}
Elseif ($Adap0Mask -eq "255.255.224.0"){$MaskBits = 19}
Elseif ($Adap0Mask -eq "255.255.192.0"){$MaskBits = 18}
Elseif ($Adap0Mask -eq "255.255.128.0"){$MaskBits = 17}
Elseif ($Adap0Mask -eq "255.255.0.0"){$MaskBits = 16}
 
$IPType = "IPv4"
# Retrieve the network adapter that you want to configure
$NICTeam = Get-NetAdapter | ? {$_.Name -eq $Team}
 # Configure the IP address and default gateway
$NICTeam | New-NetIPAddress `
 -AddressFamily $IPType `
 -IPAddress $Adap0IP `
 -PrefixLength $MaskBits `
 -DefaultGateway $Adap0GW
# Configure the DNS client server IP addresses
$NICTeam | Set-DnsClientServerAddress -ServerAddresses $Adap0DNSList
 
# IpV6 section
#$TeamIndex = (Get-NetAdapter | ? {$_.Name -eq $Team}).ifindex
#New-NetIPAddress -InterfaceIndex $TeamIndex -AddressFamily IPv6 -IPAddress $IPv6Add -PrefixLength #$IPv6Length -DefaultGateway $IPv6Gate
#Set-DnsClientServerAddress -InterfaceIndex $TeamIndex -ServerAddresses ("$IPv6DNS1","$IPv6DNS2")
 
Once the OSD build process is done, launch the "LBFOAdmin" from the prompt, and you should see the teamed network adapters called "Team".
 
 
Voila!  There's my NIC Teaming method!  Try it out!  It's worked for us with building our CM server infrastructure here at the #BigBank :)
 
 
Alternate Method ****
 
If you're only dealing with just two Network Adapters that are live and plugged in on the server(s) you're building, you could just import the computer object in CM with just using OSDAdapter0, set the OSDAdapterCount to 1, and let the OS discover the 2nd adapter post OS installation.  Then in your Task Sequence, find the 2nd adapter that is 'plugged in' and name it accordingly so you can pull it in your team.  
 
1. Find the 2nd adapter (plugged in and live) using the POSH below
 
$new = "NIC2"
# Find the 2nd adapter that's live and name <> 'NIC1'
$x = (Get-NetAdapter | ? {$_.Status -eq "up"} | ? {$_.Name -ne "NIC1"}).ifIndex
# Get the name of the 2nd adapter
$y = (get-netadapter |where { $_.ifIndex -eq $x}).Name
# Rename the 2nd adapter to NIC2
Rename-NetAdapter -Name $y -NewName $new
 
2. Set NIC1 (1st adapter) to DHCP (see POSH code above for setting to DHCP)
 
3. Team the NICs (sample POSH below)
 
New-NetLbfoTeam Team NIC1,NIC2 -Confirm:$false
start-sleep -seconds 45
 
4. Assign IP Address to the Teamed NICs (see POSH code above for setting IPs)

 

BONUS*****

Not sure if you've noticed above, I've also included a couple of lines of codes to add/configure static IPv6 addresses...  I just commented those sections above.

 1. I at first add them as OSD variables on my imported computer object(s), and pull the information via POSH using $tsenv, so i can use them during the build process.

$tsenv = New-Object -ComObject Microsoft.SMS.TSEnvironment
$IPv6Add = $tsenv.Value("IPv6Address")
$IPv6Length = $tsenv.Value("IPv6Length")
$IPv6Gate = $tsenv.Value("IPv6Gateway")
$IPv6DNS1 = $tsenv.Value("IPv6DNS1")
$IPv6DNS2 = $tsenv.Value("IPv6DNS2")

2. And reapply the IPv6 address using the code below, in your TS.

$TeamIndex = (Get-NetAdapter | ? {$_.Name -eq $Team}).ifindex
New-NetIPAddress -InterfaceIndex $TeamIndex -AddressFamily IPv6 -IPAddress $IPv6Add -PrefixLength #$IPv6Length -DefaultGateway $IPv6Gate
Set-DnsClientServerAddress -InterfaceIndex $TeamIndex -ServerAddresses ("$IPv6DNS1","$IPv6DNS2")

 

 

Copyright © 2020 - The Twin Cities Systems Management User Group