Skip to content

Instantly share code, notes, and snippets.

@laymanstake
Last active July 25, 2023 07:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save laymanstake/99992a246c2294ff0eea000a19bd545d to your computer and use it in GitHub Desktop.
Save laymanstake/99992a246c2294ff0eea000a19bd545d to your computer and use it in GitHub Desktop.
The function to get DHCP Inventory along with reservations. It uses PS jobs so even if you have many DHCP servers in org or many scopes, it should be done within mins
$logpath = "c:\temp\logfile.txt"
function Write-Log {
[CmdletBinding()]
Param(
[Parameter(ValueFromPipeline = $true, mandatory = $true)]$logtext,
[Parameter(ValueFromPipeline = $true, mandatory = $true)]$logpath
)
$Stamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss")
$LogMessage = "$Stamp : $logtext"
$isWritten = $false
do {
try {
Add-content $logpath -value $LogMessage -Force -ErrorAction SilentlyContinue
$isWritten = $true
}
catch {
}
} until ( $isWritten )
}
function New-BaloonNotification {
Param(
[Parameter(ValueFromPipeline = $true, mandatory = $true)][String]$title,
[Parameter(ValueFromPipeline = $true, mandatory = $true)][String]$message,
[Parameter(ValueFromPipeline = $true, mandatory = $false)][ValidateSet('None', 'Info', 'Warning', 'Error')][String]$icon = "Info",
[Parameter(ValueFromPipeline = $true, mandatory = $false)][scriptblock]$Script
)
Add-Type -AssemblyName System.Windows.Forms
if ($null -eq $script:balloonToolTip) { $script:balloonToolTip = New-Object System.Windows.Forms.NotifyIcon }
$tip = New-Object System.Windows.Forms.NotifyIcon
$path = Get-Process -id $pid | Select-Object -ExpandProperty Path
$tip.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path)
$tip.BalloonTipIcon = $Icon
$tip.BalloonTipText = $message
$tip.BalloonTipTitle = $title
$tip.Visible = $true
try {
register-objectevent $tip BalloonTipClicked BalloonClicked_event -Action { $script.Invoke() } | Out-Null
}
catch {}
$tip.ShowBalloonTip(10000) # Even if we set it for 1000 milliseconds, it usually follows OS minimum 10 seconds
Start-Sleep -seconds 1
$tip.Dispose() # Important to dispose otherwise the icon stays in notifications till reboot
Get-EventSubscriber -SourceIdentifier "BalloonClicked_event" -ErrorAction SilentlyContinue | Unregister-Event # In case if the Event Subscription is not disposed
}
Function Get-DHCPInventory {
# Variable declaration
$jobs = @()
$maxParallelJobs = 50
# Get all Authorized DCs from AD configuration
$DHCPs = Get-DhcpServerInDC
$scriptBlock1 = ${function:Write-log}
$scriptBlock2 = ${function:New-BaloonNotification}
$initScript = [scriptblock]::Create(@"
function Write-log {$scriptBlock1}
function New-BaloonNotification {$scriptBlock2}
"@)
foreach ($dhcp in $DHCPs) {
while ((Get-Job -State Running).Count -ge $maxParallelJobs) {
Start-Sleep -Milliseconds 50 # Wait for 0.05 seconds before checking again
}
$ScriptBlock = {
param($dhcp, $logpath)
$Report = @()
$Reservations = @()
$Summary = @()
if ((Test-Connection -ComputerName $dhcp.DNSName -count 2 -Quiet ) -AND (Get-Service -Name DHCPServer -ComputerName $dhcp.DNSName -ErrorAction SilentlyContinue).Status -eq "Running") {
$scopes = $null
$scopes = (Get-DhcpServerv4Scope -ComputerName $dhcp.DNSName -ErrorAction SilentlyContinue)
try {
$OS = (Get-WmiObject win32_operatingSystem -ComputerName $dhcp.DNSName -Property Caption -ErrorAction SilentlyContinue).Caption
}
catch {
$OS = "Access denied"
Write-Log -logtext "Could not get operating system details for DHCP Server $($dhcp.DNSName) : $($_.Exception.Message)" -logpath $logpath
}
If ($scopes) {
try {
$GlobalOptions = Get-DhcpServerv4OptionValue -OptionId 6, 15 -ComputerName $dhcp.DNSName -ErrorAction SilentlyContinue
$Option015 = [string]($Globaloptions | Where-Object { $_.optionID -eq 15 } ).Value
$GlobalDNSList = ($Globaloptions | Where-Object { $_.optionID -eq 6 } ).Value
if ($GlobalDNSList) {
$GlobalDNS1 = $GlobalDNSList[0]
$GlobalDNS2 = $GlobalDNSList[1]
$GlobalDNS3 = $GlobalDNSList[2]
}
}
catch {
Write-Log -logtext "Could not get option values (6 or 15) from DHCP Server $($dhcp.DNSName) : $($_.Exception.Message)" -logpath $logpath
}
$NoLeaseScopes = @()
foreach ($Scope in $scopes) {
$Leases = Get-DhcpServerv4Lease -ComputerName $dhcp.DNSName -ScopeId $Scope.ScopeId -ErrorAction SilentlyContinue
if ($Leases.Count -eq 0) {
$NoLeaseScopes += $Scope.ScopeID
}
}
$scopes | ForEach-Object {
try {
$ScopeOptions = Get-DhcpServerv4OptionValue -OptionId 3, 6, 15, 160, 234 -ScopeID $_.ScopeId -ComputerName $dhcp.DNSName -ErrorAction SilentlyContinue
$gateways = ($ScopeOptions | Where-Object { $_.optionID -eq 3 } ).Value
if ($gateways) {
$gateway = $gateways[0]
}
$ScopeOption015 = [string]($ScopeOptions | Where-Object { $_.optionID -eq 15 } ).Value
$ScopeOption160 = [string]($ScopeOptions | Where-Object { $_.optionID -eq 160 } ).Value
$DoGroupId = [string]($ScopeOptions | Where-Object { $_.optionID -eq 234 } ).Value
$ScopeDNSList = ($ScopeOptions | Where-Object { $_.optionID -eq 6 } ).Value
if ($ScopeDNSList) {
$ScopeDNS1 = $ScopeDNSList[0]
$ScopeDNS2 = $ScopeDNSList[1]
$ScopeDNS3 = $ScopeDNSList[2]
}
}
catch {
Write-Log -logtext "Could not get option values (3,15,160,234) for Scope $($_.Name) on DHCP Server $($dhcp.DNSName) : $($_.Exception.Message)" -logpath $logpath
}
Try {
$ScopeExclusions = Get-DhcpServerv4ExclusionRange -ComputerName $dhcp.DNSName -ScopeID $_.ScopeId -ErrorAction SilentlyContinue | Select-Object @{l = "Exclusions"; e = { "$($_.StartRange.IPAddressToString) - $($_.EndRange.IPAddressToString)" } }
}
Catch {
Write-Log -logtext "Issue in getting exclusio details for scope $($_.Name) from DHCP Server $($dhcp.DNSName) : $($_.Exception.Message)" -logpath $logpath
}
try {
$ResValues = Get-DhcpServerv4Reservation -ComputerName $dhcp.DNSName -ScopeID $_.ScopeID -ErrorAction SilentlyContinue | Select-Object ScopeId, IPAddress, Name, Description, ClientID, AddressState
$ResValues | ForEach-Object {
$Reservation = [PSCustomObject]@{
ServerName = $dhcp.DNSName
ScopeID = $_.ScopeId
IPAddress = $_.IPAddress
HostName = $_.Name
Description = $_.Description
ClientID = $_.ClientID
AddressState = $_.AddressState
}
$Reservations += $Reservation
}
}
catch {
Write-Log -logtext "Could not get reservation details for scope $($_.ScopeID) on DHCP Server $($dhcp.DNSName) : $($_.Exception.Message)" -logpath $logpath
}
$ScopeDetail = [PSCustomObject]@{
DHCPName = $dhcp.DNSName
DHCPAddress = $dhcp.IPAddress
ScopeID = $_.ScopeID
SubnetMask = $_.SubnetMask
Gateway = $gateway
ScopeName = $_.Name
State = $_.State
StartRange = $_.StartRange
Endrange = $_.EndRange
LeaseDuration = $_.LeaseDuration
Description = $_.Description
ScopeOption15 = $ScopeOption015
Exclusions = $ScopeExclusions.Exclusions -join "`n"
GlobalOption15 = $Option015
DOGroupID = $DoGroupID
Option160 = $ScopeOption160
ScopeDNS1 = $ScopeDNS1
ScopeDNS2 = $ScopeDNS2
ScopeDNS3 = $ScopeDNS3
GlobalDNS1 = $GlobalDNS1
GlobalDNS2 = $GlobalDNS2
GlobalDNS3 = $GlobalDNS3
}
$Report += $ScopeDetail
}
$Summary += [PSCustomObject]@{
DHCPName = $dhcp.DNSName
DHCPAddress = $dhcp.IPAddress
OperatingSystem = $OS
ScopeCount = $scopes.count
InactiveScopeCount = @($scopes | Where-Object { $_.State -eq 'Inactive' }).count
ScopeWithNoLease = $NoLeaseScopes -join "`n"
NoLeaseScopeCount = $NoLeaseScopes.count
}
}
else {
$message = "No scopes found on the DHCP Server $($dhcp.DNSName)."
New-BaloonNotification -title "Information" -message $message
Write-Log -logtext $message -logpath $logpath
$Summary += [PSCustomObject]@{
DHCPName = $dhcp.DNSName
DHCPAddress = $dhcp.IPAddress
OperatingSystem = $OS
ScopeCount = 0
InactiveScopeCount = 0
ScopeWithNoLease = ""
NoLeaseScopeCount = 0
}
$ScopeDetail = [PSCustomObject]@{
DHCPName = $dhcp.DNSName
DHCPAddress = $dhcp.IPAddress
ScopeID = "No scopes"
SubnetMask = "No scopes"
Gateway = "No scopes"
ScopeName = "No scopes"
State = "No scopes"
StartRange = "No scopes"
Endrange = "No scopes"
LeaseDuration = "No scopes"
Description = "No scopes"
ScopeDNS1 = "No scopes"
ScopeDNS2 = "No scopes"
ScopeDNS3 = "No scopes"
ScopeOption15 = "No scopes"
Exclusions = "No scopes"
GlobalDNS1 = "No scopes"
GlobalDNS2 = "No scopes"
GlobalDNS3 = "No scopes"
GlobalOption15 = "No scopes"
DOGroupID = "No scopes"
Option160 = "No scopes"
}
$Report += $ScopeDetail
}
}
else {
$message = "The DHCP Server $($dhcp.DNSName) not reachable or the service is stopped, would be skipped."
New-BaloonNotification -title "Information" -message $message
Write-Log -logtext $message -logpath $logpath
$Summary += [PSCustomObject]@{
DHCPName = $dhcp.DNSName
DHCPAddress = $dhcp.IPAddress
OperatingSystem = ""
ScopeCount = 0
InactiveScopeCount = 0
ScopeWithNoLease = ""
NoLeaseScopeCount = 0
}
$ScopeDetail = [PSCustomObject]@{
DHCPName = $dhcp.DNSName
DHCPAddress = $dhcp.IPAddress
ScopeID = "Not reachable"
SubnetMask = "Not reachable"
Gateway = "Not reachable"
ScopeName = "Not reachable"
State = "Not reachable"
StartRange = "Not reachable"
Endrange = "Not reachable"
LeaseDuration = "Not reachable"
Description = "Not reachable"
ScopeDNS1 = "Not reachable"
ScopeDNS2 = "Not reachable"
ScopeDNS3 = "Not reachable"
ScopeOption15 = "Not reachable"
Exclusions = "Not reachable"
GlobalDNS1 = "Not reachable"
GlobalDNS2 = "Not reachable"
GlobalDNS3 = "Not reachable"
GlobalOption15 = "Not reachable"
DOGroupID = "Not reachable"
Option160 = "Not reachable"
}
$Report += $ScopeDetail | Select-Object DHCPName, DHCPAddress, ScopeName, Description, ScopeID, SubnetMask, StartRange, Endrange, LeaseDuration, State, Gateway, ScopeOption15, Exclusions, DOGroupID, Option160, GlobalOption15, ScopeDNS1, ScopeDNS2, ScopeDNS3, GlobalDNS1, GlobalDNS2, GlobalDNS3
}
$message = "Working over DHCP Server $($dhcp.DNSName) related details."
New-BaloonNotification -title "Information" -message $message
Write-Log -logtext $message -logpath $logpath
$Output = [PSCustomObject]@{
Report = $Report
Reservation = $Reservations
Summary = $Summary
}
return $Output
}
$jobs += Start-Job -ScriptBlock $scriptBlock -ArgumentList $dhcp, $logpath -InitializationScript $initscript
}
Write-Log -logtext "Powershell jobs submitted for looking into $($DHCPs.count) DHCP Server details" -logpath $logpath
$null = $jobs | Wait-Job
$result = @()
foreach ($job in $jobs) {
$result += Receive-Job -Job $job
}
$null = Get-Job | remove-Job
Write-Log -logtext "Powershell jobs completed for $($DHCPs.count) DHCP Server details" -logpath $logpath
$Details = [pscustomobject] @{
Inventory = $result.Report
Reservation = $result.Reservation
Summary = $result.Summary
}
Return $Details
}
$s = Get-DHCPInventory
$s.Summary | Format-Table *
$s.Inventory | Format-Table *
$s.Reservation | Format-Table *
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment