Shore Up
A wall clock beside a printer feeding out a tidy roster of names and numbers, a hand filing one copy into a labelled folder and dropping another in an outbox tray.
Windows

Automate DHCP Lease Reporting on Windows Server

Ketan Aagja9 min read
No ratings yet

Before you run this

This guide builds a read-only PowerShell script that enumerates your DHCP scopes, pulls the current IPv4 leases from each, writes them to a timestamped CSV, and (optionally) emails the file. It does not change any DHCP configuration — no scopes, reservations, options, or leases are created, modified, or deleted. The worst it can do is fill a disk with CSVs if you never prune them.

Privileges: Reading DHCP data requires membership in the DHCP Administrators or DHCP Users group on the server, or local Administrator. When you later register this as a scheduled task, the task's run-as account needs that same read access. The DhcpServer PowerShell module is present automatically when the DHCP Server role is installed; to run it from an admin workstation instead you need the RSAT: DHCP Server Tools feature.

Test first: run the script interactively and read its output before you schedule anything. On a busy server Get-DhcpServerv4Lease across many large scopes can take a while and hit the DHCP database, so run it once by hand, confirm it completes and the CSV looks right, then automate. Nothing here is destructive, so there is no undo to worry about for the data — the only thing you'll want to reverse is the scheduled task itself, which I cover at the end.

Assumptions: Windows Server 2019 or 2022 with the DHCP role installed, run locally on the DHCP server, Windows PowerShell 5.1 (the version shipped in-box), domain-joined. IPv4 only. If your estate is IPv6, the parallel cmdlets use the v6 suffix (Get-DhcpServerv6Scope, Get-DhcpServerv6Lease) but I'm not walking through those here.

The reporting script

Save this as C:\Scripts\Export-DhcpLeases.ps1. Replace C:\Reports\DHCP with wherever you want the CSVs to land.

#requires -Modules DhcpServer

# --- Settings you edit ---
$ReportRoot = 'C:\Reports\DHCP'          # output folder - change to suit
$KeepDays   = 30                          # delete CSVs older than this
# -------------------------

# Make sure the output folder exists
if (-not (Test-Path $ReportRoot)) {
    New-Item -Path $ReportRoot -ItemType Directory -Force | Out-Null
}

$stamp    = Get-Date -Format 'yyyy-MM-dd_HHmm'
$csvPath  = Join-Path $ReportRoot "DHCP-Leases_$stamp.csv"

# Pull every active IPv4 scope, then every lease inside it.
$leases = foreach ($scope in Get-DhcpServerv4Scope) {
    Get-DhcpServerv4Lease -ScopeId $scope.ScopeId |
        Select-Object `
            @{Name='ScopeName'; Expression={ $scope.Name }},
            ScopeId,
            IPAddress,
            HostName,
            ClientId,          # the MAC / client identifier
            AddressState,      # Active, Declined, Expired, etc.
            LeaseExpiryTime,
            Description
}

# Write the report. UTF8 keeps hostnames with odd characters intact.
$leases | Sort-Object ScopeId, IPAddress |
    Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8

Write-Host "Wrote $($leases.Count) leases to $csvPath"

# Housekeeping: remove reports older than $KeepDays
Get-ChildItem -Path $ReportRoot -Filter 'DHCP-Leases_*.csv' |
    Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-$KeepDays) } |
    Remove-Item -Force

A few notes on the non-obvious parts:

  • #requires -Modules DhcpServer makes the script fail fast with a clear message if it's run somewhere the module isn't available, rather than erroring cmdlet-by-cmdlet.
  • ClientId is the client's hardware/identifier string as DHCP stores it. AddressState is the lease state — filter on Active later if you only care about live leases.
  • The property names above (ScopeId, IPAddress, HostName, ClientId, AddressState, LeaseExpiryTime, Description) are the ones the cmdlet returns. If you want to add a column, run Get-DhcpServerv4Lease -ScopeId <one scope> | Get-Member first to see exactly what's available on your server version rather than guessing a name.
  • The housekeeping block only ever matches files named DHCP-Leases_*.csv in that one folder, so it can't wander off and delete something else. Keep the $ReportRoot folder dedicated to these reports.

Run it once by hand:

powershell.exe -ExecutionPolicy Bypass -File C:\Scripts\Export-DhcpLeases.ps1

Open the CSV and confirm the scopes and lease counts match what you see in the DHCP console.

Optional: email the report

If you want the CSV mailed out, append this to the script and fill in your own relay and addresses. I'll be straight with you: Send-MailMessage is the built-in way and it works, but Microsoft has formally marked it obsolete and no longer maintains it — it's fine for an internal, unauthenticated relay on a trusted network, and if you need modern auth or TLS niceties you should reach for a maintained module instead (Microsoft's own docs on Send-MailMessage say as much and point you onward).

$mail = @{
    From       = 'dhcp-report@example.com'
    To         = 'netadmins@example.com'
    Subject    = "DHCP lease report $stamp"
    Body       = "DHCP lease report attached. $($leases.Count) leases."
    Attachments= $csvPath
    SmtpServer = 'smtp.example.com'   # your internal relay
}
Send-MailMessage @mail

Replace example.com, the addresses, and smtp.example.com with your real values.

Schedule it

The mainstream path is a scheduled task that runs daily. You can build one in the Task Scheduler GUI, or create it from PowerShell with the built-in cmdlets — I'll use the cmdlets because they're repeatable. Run this once, in an elevated PowerShell session, to register the task:

$action  = New-ScheduledTaskAction -Execute 'powershell.exe' `
    -Argument '-ExecutionPolicy Bypass -File C:\Scripts\Export-DhcpLeases.ps1'

$trigger = New-ScheduledTaskTrigger -Daily -At 6:00AM

# Run as SYSTEM. SYSTEM is a local account, so it needs read rights to
# DHCP - on the DHCP server itself SYSTEM can read the local service.
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' `
    -LogonType ServiceAccount -RunLevel Highest

Register-ScheduledTask -TaskName 'DHCP Lease Report' `
    -Action $action -Trigger $trigger -Principal $principal `
    -Description 'Daily IPv4 DHCP lease CSV export'

If you're emailing through a relay that only accepts a specific service account, register the task under that domain account instead of SYSTEM — use New-ScheduledTaskPrincipal -UserId 'EXAMPLE\svc-dhcpreport' and supply its password when prompted (or use -User/-Password on Register-ScheduledTask). Make sure that account is in DHCP Users or DHCP Administrators.

Verify it worked

Confirm the task exists and check its last result:

Get-ScheduledTask -TaskName 'DHCP Lease Report'

# Force a run now instead of waiting for 6 AM
Start-ScheduledTask -TaskName 'DHCP Lease Report'

# After it finishes, check the outcome (0x0 means success)
Get-ScheduledTaskInfo -TaskName 'DHCP Lease Report' |
    Select-Object LastRunTime, LastTaskResult

Then confirm a fresh CSV appeared:

Get-ChildItem 'C:\Reports\DHCP' -Filter 'DHCP-Leases_*.csv' |
    Sort-Object LastWriteTime -Descending | Select-Object -First 1

Open that file and cross-check a scope's lease count against DHCP console → IPv4 → the scope → Address Leases. If the numbers agree, you're done.

Undo

Nothing here touched DHCP, so there's nothing to roll back on the server side. To remove the automation and its output:

Unregister-ScheduledTask -TaskName 'DHCP Lease Report' -Confirm:$false

Delete C:\Scripts\Export-DhcpLeases.ps1 and the C:\Reports\DHCP folder if you no longer want the reports. For the exact behaviour and parameters of any cmdlet above, the DhcpServer module and the ScheduledTasks module are both documented on Microsoft Learn — search the cmdlet name there before customising further.

Written by
Ketan Aagja

Runs enterprise networks and security for a living, and writes Shore Up to turn two decades of hands-on Linux, Windows and mail-server work into guides you can actually use.

More about the author →

Was this article helpful?

Tap a star — no sign-in needed.

Be the first to rate this article.

Automate a Daily Failed-Logon (4625) Report with PowerShell

This guide builds a scheduled PowerShell job that reads Event ID 4625 (failed logon) from the Windows Security log for the last 24 hours and writes them to a dated HTML report. It is read-only — it queries the event log and creates a report file. It does not change auditing policy, delete events, or touch accounts.

9 min read

Monitor Free Disk Space and Email an Alert with PowerShell

This script reads the free space on the fixed local disks of the machine it runs on and emails you when any of them drops below a percentage you set. It is a lightweight stand-in for a monitoring platform — good for a handful of servers, not a replacement for one across a fleet.

8 min read

Automate a Nightly Robocopy Mirror to a NAS with Verification

This sets up a nightly one-way mirror of a local folder to a NAS SMB share using robocopy /MIR , followed by a second pass that lists any remaining differences as a verification step, all logged to a timestamped file and driven by a Scheduled Task.

9 min read