Adding DHCP server support for PXE BIOS and UEFI Booting

Note: This script has been depreciated by the following.
Link: Adding DHCP Server Support for PXE BIOS and UEFI Booting – Version 2

ARCHIVED – ARCHIVED – ARCHIVED – ARCHIVED – ARCHIVED

There are numerous websites with really helpful articles on how to add PXE booting for both BIOS (Legacy) and UEFI devices.

In some cases you are directed to add (to enable UEFI, and thus disable BIOS) or remove (to disable UEFI and re-enable BIOS) an entry on your DHCP server.  Specifically the Option 060, with a value of “PXEClient”.

While this will allow you to manually turn on or of UEFI support, it is a manual process and disables legacy BIOS XE booting when it’s enabled. I’ve even seen some admins go so far as to create a new IP subnet just for UEFI booting.

Well, since I’m not one to let a manual process go too long without being automated in some way… I present to you the powershell script that will 99% configure your Windows DHCP server to allow for PXE booting BIOS (Legacy) and UEFI devices, all at the same time, on the same subnet!

Ain’t that neat, eh!

<# 
.Synopsis
	Receives user input, gets the list of DHCP options, classes and policies, then sets them if they don't exist.
.DESCRIPTION 
	Enter the option number, class name or policy name where appropriate.
.NOTES 
	Author: Derek Bannard
.EXAMPLE
	. .\DHCPPXEOptions.ps1
	Loads the script to perform the DHCP functions. Remember to "Dot Source" the script.
.EXAMPLE
	Get-DHCPOptions
	Lists missing options for use with Set-DHCPPXEOptions function.
.EXAMPLE
	Get-DHCPClasses
	Lists missing classes for use with Set-DHCPPXEOptions function.
.EXAMPLE
	Get-DHCPPolicies
	Lists missing policies for use with Set-DHCPPXEOptions function.
.EXAMPLE
	Set-DHCPPXEOptions
	Creates all missing options, classes and policies for BIOS/UEFI PXE co-existance.
#>

<#
=================================================
Check if this is a DHCP server and if not,
break out of the script.
=================================================
#>
if((Get-WindowsOptionalFeature -FeatureName "DHCPServer" -Online).State -eq "Enabled") {
	Import-Module DhcpServer
} else {
	Write-Host "`n ERROR: Please run this script on the DHCP server." -ForegroundColor Red
	Break
}

<#
=================================================
Error Preferences
Uncomment to make the screen output less "red"
=================================================
#>
#$errpref = $ErrorActionPreference
#$ErrorActionPreference = "SilentlyContinue"


<#
=================================================
Script Variables, comma separated
=================================================
#>
[string[]]$DHCPOptions = "060","066","067"
[string[]]$DHCPClasses = "PXEClient:Arch:00000","PXEClient:Arch:00006","PXEClient:Arch:00007"
[string[]]$DHCPPolicies = "PXEClient (BIOS x86 &amp; x64)","PXEClient (UEFI x86)","PXEClient (UEFI x64)"

[string[]]$rtnDHCPOptions = @()
[string[]]$rtnDHCPClasses = @()
[string[]]$rtnDHCPPolicies = @()


<#
=================================================
Script Get Functions
=================================================
#>
Function Get-DHCPOptions{
	$arrayOptions = New-Object System.Collections.ArrayList

	ForEach ($DHCPOption in $DHCPOptions){
		Try{
			If((Get-DhcpServerv4OptionDefinition -OptionId $DHCPOption) -eq $null){
				Write-Host "OptionId $DHCPOption is not present." -ForegroundColor Red
				$arrayOptions.Add($DHCPOption) | Out-Null
				}
			Else{
				Write-Host "OptionId $DHCPOption is already present." -ForegroundColor Green
				}
		}
		Catch{
		}
	}
	$global:rtnDHCPOptions = $arrayOptions
}


Function Get-DHCPClasses{
	$arrayClasses = New-Object System.Collections.ArrayList

	ForEach ($DHCPClass in $DHCPClasses){
		Try{
			If((Get-DhcpServerv4Class -Type Vendor | Where {$_.AsciiData -eq $DHCPClass}) -eq $null){
				Write-Host "Class $DHCPClass is not present." -ForegroundColor Red
				$arrayClasses.Add($DHCPClass) | Out-Null
				}
			Else{
				Write-Host "Class $DHCPClass is already present." -ForegroundColor Green
				}
		}
		Catch{
		}
	}
	$global:rtnDHCPClasses = $arrayClasses
}


Function Get-DHCPPolicies{
	$arrayPolicies = New-Object System.Collections.ArrayList

	ForEach ($DHCPPolicy in $DHCPPolicies){
		Try{
			If((Get-DhcpServerv4Policy -Name $DHCPPolicy) -eq $null){
				Write-Host "Policy $DHCPPolicy is not present." -ForegroundColor Red
				$arrayPolicies.Add($DHCPPolicy) | Out-Null
				}
			Else{
				Write-Host "Policy $DHCPPolicy is already present." -ForegroundColor Green
				}
		}
		Catch{
		}
	}
	$global:rtnDHCPPolicies = $arrayPolicies
}


<#
=================================================
Script Set Function
=================================================
#>
Function Set-DHCPPXEOptions{
	If($rtnDHCPOptions){
		ForEach ($rtnDHCPOption in $rtnDHCPOptions){
			If($rtnDHCPOption -eq "060"){
				Try{
					Write-Output "Adding OptionId 060..."
					Add-DhcpServerv4OptionDefinition -Name "PXEClient" -Description "PXE Support" -DefaultValue "PXEClient" -OptionId 060 -Type String
				}
				Catch{
				}
			}
			ElseIf($rtnDHCPOption -eq "066"){
				Try{
					Write-Output "Adding OptionId 066..."
					Add-DhcpServerv4OptionDefinition -Name "Boot Server Host Name" -Description "TFTP boot server host name" -OptionId 066 -Type String
				}
				Catch{
				}
			}
			ElseIf($rtnDHCPOption -eq "067"){
				Try{
					Write-Output "Adding OptionId 067..."
					Add-DhcpServerv4OptionDefinition -Name "Bootfile Name" -Description "Bootfile Name" -OptionId 067 -Type String
				}
				Catch{
				}
			}
			Else{
				Write-Output "Undefined options cannot be added!"
			}
		}
	}
	Else{
		Write-Output "No Options are required to be added."
	}


	If($rtnDHCPClasses){
		ForEach ($rtnDHCPClass in $rtnDHCPClasses){
			If($rtnDHCPClass -eq "PXEClient:Arch:00000"){
				Try{
					Write-Output "Adding PXEClient (BIOS x86 &amp; x64) Class..."
					Add-DhcpServerv4Class -Name "PXEClient (BIOS x86 &amp; x64)" -Type Vendor -Data "PXEClient:Arch:00000" -Description "PXEClient:Arch:00000"
				}
				Catch{
				}
			}
			ElseIf($rtnDHCPClass -eq "PXEClient:Arch:00006"){
				Try{
					Write-Output "Adding UEFI 32-Bit DHCP Class..."
					Add-DhcpServerv4Class -Name "PXEClient (UEFI x86)" -Type Vendor -Data "PXEClient:Arch:00006" -Description "PXEClient:Arch:00006"
				}
				Catch{
				}
			}
			ElseIf($rtnDHCPClass -eq "PXEClient:Arch:00007"){
				Try{
					Write-Output "Adding UEFI 64-Bit DHCP Class..."
					Add-DhcpServerv4Class -Name "PXEClient (UEFI x64)" -Type Vendor -Data "PXEClient:Arch:00007" -Description "PXEClient:Arch:00007"
				}
				Catch{
				}
			}
			Else{
				Write-Output "Undefined classes cannot be added!"
			}
		}
	}
	Else{
		Write-Output "No Classes are required to be added."
	}


	If($rtnDHCPPolicies){
		ForEach ($rtnDHCPPolicy in $rtnDHCPPolicies){
			If($rtnDHCPPolicy -eq "PXEClient (BIOS x86 &amp; x64)"){
				Try{
					Write-Output "Adding PXEClient (BIOS x86 &amp; x64) Policy..."
					Add-DhcpServerv4Policy -Name "PXEClient (BIOS x86 &amp; x64)" -Description "Delivers the correct bootfile for BIOS machines" -Condition OR -VendorClass EQ,"PXEClient (BIOS x86 &amp; x64)*" -Enabled $True -Confirm:$False
				}
				Catch{
				}
			}
			ElseIf($rtnDHCPPolicy -eq "PXEClient (UEFI x64)"){
				Try{
					Write-Output "Adding UEFI 64-Bit DHCP Policy..."
					Add-DhcpServerv4Policy -Name "PXEClient (UEFI x64)" -Description "Delivers the correct bootfile for (UEFI x64) machines" -Condition OR -VendorClass EQ,"PXEClient (UEFI x64)*" -Enabled $True -Confirm:$False
				}
				Catch{
				}
			}
			ElseIf($rtnDHCPPolicy -eq "PXEClient (UEFI x86)"){
				Try{
					Write-Output "Adding UEFI 32-Bit DHCP Policy..."
					Add-DhcpServerv4Policy -Name "PXEClient (UEFI x86)" -Description "Delivers the correct bootfile for (UEFI x86) machines" -Condition OR -VendorClass EQ,"PXEClient (UEFI x86)*" -Enabled $True -Confirm:$False
				}
				Catch{
				}
			}
			Else{
				Write-Output "Undefined policies cannot be added!"
			}
		}
	}
	Else{
		Write-Output "No Policies are required to be added."
	}

	
	If ($rtnDHCPOptions -or $rtnDHCPClasses -or $rtnDHCPPolicies){
		Restart-Service "DHCPServer"
	}
}


<#
=================================================
Runs the functions to populate the required information.
=================================================
#>
Get-DHCPOptions
Get-DHCPClasses
Get-DHCPPolicies

The Last 1%

I wasn’t able to find a way to set this in the script, but I believe most will want to set these options manually anyways.
You will be modifying the policies to make these changes.  Make sure the options you want added are selected (checked) so they are part of the policy.

Configure the options for each policy, based on your environment and needs.  I would suggest reviewing the recommendations below.

If using MDT:

Policy: PXEClient (BIOS x86 & x64)

  • 060Remove any entry, leave blank
  • 066IP Address of the WDS server
  • 067: boot\x86\wdsnbp.com

Policy: PXEClient UEFI (x86) & PXEClient UEFI (x64)

  • 060: PXEClient
  • 066IP Address of the WDS server
  • 067: boot\x86\wdsmgfw.efi

If Using SCCM:

Policy: PXEClient (BIOS x86 & x64)

  • 060Remove any entry, leave blank
  • 066IP Address of the SCCM server
  • 067: smsboot\x86\wdsnbp.com

Policy: PXEClient UEFI (x86)

  • 060: PXEClient
  • 066IP Address of the SCCM server
  • 067: smsboot\x86\wdsmgfw.efi

Policy: PXEClient UEFI (x64)

  • 060: PXEClient
  • 066IP Address of the SCCM server
  • 067: smsboot\x64\wdsmgfw.efi

References:

Using DHCP to Boot WDS to BIOS & UEFI with SCCM.
Link: https://www.experts-exchange.com/articles/29394/Using-DHCP-to-Boot-WDS-to-BIOS-UEFI-with-SCCM.html