For a while, I’ve used Microsoft’s DHCP server in my home lab. Within the last year though, I’ve started having issues where my Unifi WAP’s would have trouble getting an IP address. If I rebooted the WAP’s, everything was fine. Because of this, I don’t really think its a problem with my DHCP server or configuration, but I wanted to be absolutely sure and rule it out.
I had a slight problem. Although I use DHCP, I use DHCP reservations for almost everything on my network. The only things I don’t reserve are guest devices. I have an excel spreadsheet that I maintain that has all my devices and their reservations. I needed a way to easily and automatically import these reservations into pfSense for when I make additions and/or changes, just like I do for Microsoft’s DHCP server currently.
I already have a powershell script to do this for Microsoft DHCP, so I used that to get started. It wasn’t as difficult as I thought it would be. A few lines of powershell later, and I have a working powershell script that takes my excel file and dumps it into pfSense’s DHCP configuration, then restarts the service.
# 1) Install-Module ImportExcel -Scope CurrentUser
# 2) Save this script in the same directory as your spreadsheet, or adjust the relevant lines
Clear
Set-Location $PSScriptRoot
$pfSenseUrl = "https://x.x.x.x"
$excelFile = "Home Network.xlsx"
# Prompt for credentials to login to pfsense
# The only permission this user needs is "WebCfg - Diagnostics: Backup & Restore"
# If you want the script to be able to restart the dhcp service, then you
# also need:
# Allow access to the 'Services: DHCP Server' page.
# and
# Allow access to the 'Status: Services' page.
if ( $cred -eq $null ) {
$cred = (Get-Credential)
}
# get csrf token for request
$lastRequest = (Invoke-WebRequest $pfSenseUrl/diag_backup.php -SessionVariable pfSenseSession)
$csrf = $lastRequest.Forms.Fields["__csrf_magic"]
# login
$bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($cred.Password)
$postParams = @{login='Login';usernamefld=$cred.UserName;passwordfld=([System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr));__csrf_magic=$csrf}
$lastRequest = (Invoke-WebRequest $pfSenseUrl/diag_backup.php -WebSession $pfSenseSession -Method Post -Body $postParams)
# get csrf token for next request
$lastRequest = (Invoke-WebRequest $pfSenseUrl/diag_backup.php -WebSession $pfSenseSession)
$csrf = $lastRequest.Forms.Fields["__csrf_magic"]
# download current dhcp server configuration
$postParams = @{download='download';donotbackuprrd='yes';__csrf_magic=$csrf;backuparea='dhcpd';}
$lastRequest = (Invoke-WebRequest $pfSenseUrl/diag_backup.php -WebSession $pfSenseSession -Method Post -Body $postParams)
#remove all current static mappings
[System.Xml.XmlDocument]$xml = [System.Xml.XmlDocument]::new()
$xml.LoadXml([System.Text.ASCIIEncoding]::UTF8.GetString($lastRequest.Content))
$xml.dhcpd.lan.SelectNodes("staticmap") | ForEach-Object {
[void]$_.ParentNode.RemoveChild($_)
}
$lastRequest = $null
# convert the excel file to csv for easy manipulation in powershell
Import-Excel $excelFile | Export-Csv -Path "./$($excelFile).csv" -NoTypeInformation
# parse the generated csv file and add mappings to the xml document in memory
Import-Csv "$($excelFile).csv" | ForEach-Object {
$mac = $_.UniqueID
if ( $mac.Length -gt 0 ) {
$desc = $_.HostName
if ($_.Description.Length -gt 0) {
$desc = $_.Description
}
if ($r -eq $null) {
Write-Host Adding reservation for $_
$reservation = $xml.CreateElement("staticmap")
$macEle = $xml.CreateElement("mac")
$macEle.InnerText = $mac
$descEle = $xml.CreateElement("descr")
$descEle.InnerText = $desc
$ipEle = $xml.CreateElement("ipaddr")
$ipEle.InnerText = $_.IP
$hostEle = $xml.CreateElement("hostname")
$hostEle.InnerText = $_.HostName
[void]$reservation.AppendChild($macEle)
[void]$reservation.AppendChild($ipEle)
[void]$reservation.AppendChild($hostEle)
[void]$reservation.AppendChild($descEle)
[void]$xml.dhcpd.lan.AppendChild($reservation)
}
}
}
$LF = "`r`n"
$boundary = [System.Guid]::NewGuid().ToString()
$contentType = "multipart/form-data; boundary=`"$boundary`""
# generate the multipart/form-data body with required form variables and the config "file"
$bodyLines = (
"--$boundary",
"Content-Disposition: form-data; name=`"__csrf_magic`"$LF",
$csrf,
"--$boundary",
"Content-Disposition: form-data; name=`"restorearea`"$LF",
"dhcpd",
"--$boundary",
"Content-Disposition: form-data; name=`"conffile`"; filename=`"dhcpd.xml`"",
"Content-Type: text/xml$LF",
$xml.OuterXml,
"--$boundary",
"Content-Disposition: form-data; name=`"restore`"$LF",
"Restore Configuration"
) -join $LF
try {
# restore the dhcpd configuration to pfSense
$lastRequest = ( Invoke-WebRequest $pfSenseUrl/diag_backup.php -ContentType $contentType -WebSession $pfSenseSession -Method Post -Body $bodyLines -ErrorAction SilentlyContinue)
} catch {}
if ( $lastRequest.StatusCode -eq 200 ) {
Write-Host
Write-Host Success! -ForegroundColor Green
Write-Host
Write-Host Restarting DHCP service
# get csrf token for request
$csrf = $lastRequest.Forms.Fields["__csrf_magic"]
#restart dhcp service
$postParams = @{ajax='ajax';mode='restartservice';__csrf_magic=$csrf;service='dhcpd';}
$lastRequest = (Invoke-WebRequest $pfSenseUrl/status_services.php -Method Post -WebSession $pfSenseSession -Body $postParams)
}
else {
Write-Host Failure! -ForegroundColor Red
}
$bstr = $null
$postParams = $null
$lastRequest = $null
11 comments on “Scripting pfSense DHCP static assignments”
fgfgfghey, I happened to find this script and I wanna use it to populate a bunch of DHCP static mappings on my newly configured PfSense install but before I run your script, can you post a screenshot (or the column headers) for your csv file? I assume it’s mac, descr, ipaddr, hostname but wanna be sure.
You wouldn’t believe how many people would find this useful.
thanks for writing it!
The columns that matter are:
HostName
Description
UniqueID
IP
They’re referenced in the script as properties on the automatic variable $_.
For example, $_.HostName, $.Description, $_.UniqueID, and $_.IP
The other columns are just columns I’m using for my own personal organization.
Glad it is helpful.
Sorry to bug you on this but I am getting an error. I am stepping through this with powershell ISE and
line 41 is giving me trouble.. producing the following error:
$lastRequest = $null
At line:1 char:14
+ $xml = ::new()
+ ~
An expression was expected after ‘(‘.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ExpectedExpression
It looks like your $xml declaration might be incorrect.
Try “[xml]$xml = [xml]::new()” instead of “$xml = ::new()”
That worked!
Thanks for your help!
I posted a link to your site and your blog post on the pfSense reddit.
https://www.reddit.com/r/PFSENSE/comments/j2s0ih/static_mapping_dhcp_reservation_in_pfsense_with/
Thanks for the reddit post.
After reading it, I went and checked my original post, and the syntax is correct in the post, but it appears that the syntax highlighter doesn’t like it and it’s getting removed there :/
Edit: Changed it from the shortcut [xml] to [System.Xml.XmlDocument] and it seems to have fixed the formatting.
Hello
to skip the ssl error i included
add-type @”
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
“@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
so it looks like this
$pfSenseUrl = “https://x.x.x.x”
$excelFile = “Home Network.xlsx”
add-type @”
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
“@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
what this dose it tells the script to trust all certs so the selfsigned cert will not complain ๐
Hello
can you also make this work if you have multiple dns servers in pfsense for vlans? ๐
like a sheet per vlan with just the vlan id or the name of the dhcp server ๐
My pfSense setup is pretty basic and don’t have multiple vlans/dns servers. It still should be relatively easy since I’m just using a backup/restore process to do it. Can you send me a sample backup that contains just your dns area?
Hello
no idea how to do that..
can you tell me how to do it and il gladly do it ๐
update: with the release of 22.01 it seems like this scrip is no longer working.
i get this error
:37 char:17
+ … tRequest = (Invoke-WebRequest $pfSenseUrl/diag_backup.php -SessionVar …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
Cannot index into a null array.
At C:\Users\TS\Dokument\pfsense\pfsensenetworkimport.ps1:38 char:1
+ $csrf = $lastRequest.Forms.Fields[“__csrf_magic”]
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray