Last active
July 25, 2023 07:54
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$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