I'm pretty addicted to reading blog posts. I saw this new blog post the other day, where the author created the DHCP HTML report, and he did it by manually building headers, footers, table borders, and finally, adding some coloring to the percentage of DHCP being in use. It's the “standard” approach to build HTML in PowerShell, and I've seen a similar path before, but that got me thinking how much time it would take for me to replicate the very same functionality using PSWriteHTML module.
If you're interested in the other article, you can find it here. The essentials parts, however, that I wanted to focus on are visuals and part of the code to generate it. The next two images are from the other article and are here to illustrate what we are trying to accomplish (1st image) and what we are trying to avoid (2nd image).
The important part to generate it
As you see above, the visual part is a clear and readable table; however, the code to do so, except for PowerShell knowledge, also requires HTML building skills. You have to know what each tag does and how it impacts visuals. You could, of course, argue that it's just copy/paste and you're done, so in the long run, it doesn't matter, but I'm here to show you another way to do things.
Let's see how we can do it my way. My approach uses the same code to gather the DHCP servers from Active Directory and then going thru each scope, finally putting it in a single array called Output.
# Dynamically pulling the DHCP servers in a Active Directory domain $DHCP_Servers = Get-DhcpServerInDC | Sort-Object -Property DnsName $Output = Foreach ($DHCP_Server in $DHCP_Servers) { # Going through the DHCP servers that were returned one at a time to pull statistics try { $DHCP_Scopes = Get-DhcpServerv4Scope –ComputerName $DHCP_Server.DNSName -ErrorAction Stop } catch { Write-Warning "Couldn't reach server $($DHCP_Server.DNSName)" $DHCP_Scopes = $Null } Foreach ($DHCP_Scope in $DHCP_Scopes) { # Going through the scopes returned in a given server $DHCP_Scope_Stats = Get-DhcpServerv4ScopeStatistics -ComputerName $DHCP_Server.DNSName -ScopeId $DHCP_Scope.ScopeId [PSCustomObject] @{ 'DHCP Server' = $DHCP_Server.DNSName 'DHCP IP' = $DHCP_Server.IPAddress 'Scope ID' = $DHCP_Scope.ScopeId.IPAddressToString 'Scope Name' = $DHCP_Scope.Name 'Scope State' = $DHCP_Scope.State 'In Use' = $DHCP_Scope_Stats.InUse 'Free' = $DHCP_Scope_Stats.Free '% In Use' = ([math]::Round($DHCP_Scope_Stats.PercentageInUse, 0)) 'Reserved' = $DHCP_Scope_Stats.Reserved 'Subnet Mask' = $DHCP_Scope.SubnetMask 'Start Range' = $DHCP_Scope.StartRange 'End Range' = $DHCP_Scope.EndRange 'Lease Duration' = $DHCP_Scope.LeaseDuration } } }
When you run the above code, you should get everything in one table. The approach here is – we separate data preparation from reporting. This allows us to build our dataset and use it for anything – be it HTML report, export to Excel, or send via email.
$Output | Format-Table
Now that I've my data in a variable I can install my PSWriteHTML PowerShell module.
Install-Module PSWriteHTML -Force
That's it. The module is installed and with it, you're ready to build your HTML reports with almost zero effort on your side.
Ok, so we have our data saved in $Output variable, we have PowerShell module to create HTML installed now we can build our HTML.
$Output | Out-HtmlView
And that's it. When you call Out-HTMLView, it automatically generates HTML code for you and opens it up in the default browser.
As you see above, the table contains all information that we require, but it also was executed on a server where I have JavaScript disabled. In such a case, PSWriteHTML will give users the ability to see a minimal HTML version. If you have JavaScript enabled or/and using the modern browser the output would be much better, giving you sort, search, export to Excel, CSV, or even PDF.
And in case the screen is smaller, columns that wouldn't fit on the screen will simply hide away and will be accessible under the plus button.
Cool right? Out-HtmlView has a lot of parameters that one can use on a daily basis, for other types of data. This is just one of many examples and I encourage you to try and explore it a bit. Its use case is mostly limited to opening HTML right there, during our processing. Of course, you can save the file, filter things in it, disable or remove buttons as required.
$Output | Out-HtmlView -FilePath $Env:USERPROFILE\Desktop\MyReport.html -Title 'DHCP Servers' -HideFooter -PreventShowHTML
However, nothing is stopping you from using it for reporting if you wish to. As you can see above, I've told Out-HtmlView where to save the file, how to name it, to hide footer, and finally, to not automatically open it in a browser.
Of course, you may say now that I've merely displayed the data in HTML, but I've no way delivered the same visual effect that the author in the other article. So let's do that, shall we? Out-HTMLView isn't designed for advanced reporting, but it does support the same sets of options as New-HTMLTable, which is part of PSWriteHTML. Some months ago, I added a couple of features that allow you to quickly build conditional formatting or advanced headers without diving into HTML.
$Output | Out-HtmlView { New-TableCondition -Name '% In Use' -Operator ge -Value 95 -BackgroundColor Red -Color White -Inline -ComparisonType number New-TableCondition -Name '% In Use' -Operator ge -Value 80 -BackgroundColor Yellow -Color Black -Inline -ComparisonType number New-TableCondition -Name '% In Use' -Operator lt -Value 80 -BackgroundColor Green -Color White -Inline -ComparisonType number New-TableCondition -Name 'Scope State' -Operator eq -Value 'Inactive' -BackgroundColor Gray -Color White -Inline -ComparisonType number } -HideFooter -Title 'DHCP Servers'
Six lines of code, dynamically built, easily can be adjusted for any reporting, not only for DHCP scopes.
Of course, since I don't have a busy DHCP server at the moment, you only see green color, but trust me on that – it would be multicolor. You could add as many conditions as you want, for different column names, and even overwrite some values on demand if needed. But still, we've just talked about conditions, and our author managed to do more.
$Output | Out-HtmlView { New-TableCondition -Name '% In Use' -Operator ge -Value 95 -BackgroundColor Red -Color White -Inline -ComparisonType number New-TableCondition -Name '% In Use' -Operator ge -Value 80 -BackgroundColor Yellow -Color Black -Inline -ComparisonType number New-TableCondition -Name '% In Use' -Operator lt -Value 80 -BackgroundColor Green -Color White -Inline -ComparisonType number New-TableCondition -Name 'Scope State' -Operator eq -Value 'Inactive' -BackgroundColor Gray -Color White -Inline -ComparisonType string New-TableHeader -Title "DHCP Scope Statistics Report ($(Get-Date))" -Alignment center -BackGroundColor BuddhaGold -Color White -FontWeight bold New-TableHeader -Names 'DHCP Server', 'DHCP IP' -Title 'Server Information' -Color White -Alignment center -BackGroundColor Gray New-TableHeader -Names 'Subnet Mask','Start Range','End Range','Lease Duration' -Title 'Scope Configuration' -Color White -Alignment center -BackGroundColor Gray } -HideFooter -Title 'DHCP Servers'
No javascript enabled view
Or if you have JavaScript enabled, more advanced view
It's up to you to choose proper colors, alignment, background color, the color of text, or whether it's bold or not. You can add titles, modify headers, all by using basic PowerShell knowledge, and zero HTML.
So with just 40 lines of code, I can gather DHCP data scopes and output them in HTML form, and I can do so without knowing how to build HTML, what each tag means, and how everything in HTML is connected with CSS and JavaScript.
# Dynamically pulling the DHCP servers in a Active Directory domain $DHCP_Servers = Get-DhcpServerInDC | Sort-Object -Property DnsName $Output = Foreach ($DHCP_Server in $DHCP_Servers) { # Going through the DHCP servers that were returned one at a time to pull statistics try { $DHCP_Scopes = Get-DhcpServerv4Scope –ComputerName $DHCP_Server.DNSName -ErrorAction Stop } catch { Write-Warning "Couldn't reach server $($DHCP_Server.DNSName)" $DHCP_Scopes = $Null } Foreach ($DHCP_Scope in $DHCP_Scopes) { # Going through the scopes returned in a given server $DHCP_Scope_Stats = Get-DhcpServerv4ScopeStatistics -ComputerName $DHCP_Server.DNSName -ScopeId $DHCP_Scope.ScopeId [PSCustomObject] @{ 'DHCP Server' = $DHCP_Server.DNSName 'DHCP IP' = $DHCP_Server.IPAddress 'Scope ID' = $DHCP_Scope.ScopeId.IPAddressToString 'Scope Name' = $DHCP_Scope.Name 'Scope State' = $DHCP_Scope.State 'In Use' = $DHCP_Scope_Stats.InUse 'Free' = $DHCP_Scope_Stats.Free '% In Use' = ([math]::Round($DHCP_Scope_Stats.PercentageInUse, 0)) 'Reserved' = $DHCP_Scope_Stats.Reserved 'Subnet Mask' = $DHCP_Scope.SubnetMask 'Start Range' = $DHCP_Scope.StartRange 'End Range' = $DHCP_Scope.EndRange 'Lease Duration' = $DHCP_Scope.LeaseDuration } } } $Output | Out-HtmlView { New-TableCondition -Name '% In Use' -Operator ge -Value 95 -BackgroundColor Red -Color White -Inline -ComparisonType number New-TableCondition -Name '% In Use' -Operator ge -Value 80 -BackgroundColor Yellow -Color Black -Inline -ComparisonType number New-TableCondition -Name '% In Use' -Operator lt -Value 80 -BackgroundColor Green -Color White -Inline -ComparisonType number New-TableCondition -Name 'Scope State' -Operator eq -Value 'Inactive' -BackgroundColor Gray -Color White -Inline -ComparisonType string New-TableHeader -Title "DHCP Scope Statistics Report ($(Get-Date))" -Alignment center -BackGroundColor BuddhaGold -Color White -FontWeight bold New-TableHeader -Names 'DHCP Server', 'DHCP IP' -Title 'Server Information' -Color White -Alignment center -BackGroundColor Gray New-TableHeader -Names 'Subnet Mask','Start Range','End Range','Lease Duration' -Title 'Scope Configuration' -Color White -Alignment center -BackGroundColor Gray } -HideFooter -Title 'DHCP Servers' -FilePath $env:USERPROFILE\Desktop\MyDHCPReport.html
But Out-HTMLView is just one command of many available within PSWriteHTML. Ever since Emailimo was integrated you can send pretty emails using readable code.
Email -AttachSelf -AttachSelfName 'DHCP Report' { EmailHeader { EmailFrom -Address 'MyEmail@evotec.pl' EmailTo -Addresses "MyOtherEmail@evotec.pl" EmailServer -Server 'smtp.office365.com' -UserName 'login@evotec.pl' -Password "$ENV:UserProfile\Desktop\Password-Evotec.txt" -PasswordAsSecure -PasswordFromFile -Port 587 -SSL EmailOptions -Priority High -DeliveryNotifications Never EmailSubject -Subject 'DHCP Report - Scope Utilization' } EmailBody { EmailTextBox -FontFamily 'Calibri' -Size 17 -TextDecoration underline -Color DarkSalmon -Alignment center { 'Demonstration' } EmailText -LineBreak EmailTable -DataTable $Output { New-TableCondition -Name '% In Use' -Operator ge -Value 95 -BackGroundColor Red -Color White -Inline -ComparisonType number New-TableCondition -Name '% In Use' -Operator ge -Value 80 -BackGroundColor Yellow -Color Black -Inline -ComparisonType number New-TableCondition -Name '% In Use' -Operator lt -Value 80 -BackGroundColor Green -Color White -Inline -ComparisonType number New-TableCondition -Name 'Scope State' -Operator eq -Value 'Inactive' -BackGroundColor Gray -Color White -Inline -ComparisonType string New-TableHeader -Title "DHCP Scope Statistics Report ($(Get-Date))" -Alignment center -BackGroundColor BuddhaGold -Color White -FontWeight bold New-TableHeader -Names 'DHCP Server', 'DHCP IP' -Title 'Server Information' -Color White -Alignment center -BackGroundColor Gray New-TableHeader -Names 'Subnet Mask', 'Start Range', 'End Range', 'Lease Duration' -Title 'Scope Configuration' -Color White -Alignment center -BackGroundColor Gray } -HideFooter } } -Supress $false
Using the very same variable $Output, and using the same code that we used in Out-HtmlView to create a custom header with conditional formatting. Our EmailHeader contains information about server, recipients, and subject, while our body contains what we want to show in the email. We also used the AttachSelf parameter, which attaches the very same HTML that is displayed in email as an attachment. Why would I do this? Because if you do so, you get a modern version of the same report with JavaScript enabled.
In other words, what's in Email is quickly available, but with the attachment, you get advanced features such as filtering, export to Excel, CSV, or search.
Full code to generate DHCP Scopes information and send pretty email takes just about 60 lines of code.
Import-Module PSWriteHTML $DHCP_Servers = Get-DhcpServerInDC | Sort-Object -Property DnsName $Output = Foreach ($DHCP_Server in $DHCP_Servers) { # Going through the DHCP servers that were returned one at a time to pull statistics try { $DHCP_Scopes = Get-DhcpServerv4Scope –ComputerName $DHCP_Server.DNSName -ErrorAction Stop } catch { Write-Warning "Couldn't reach server $($DHCP_Server.DNSName)" $DHCP_Scopes = $Null } Foreach ($DHCP_Scope in $DHCP_Scopes) { # Going through the scopes returned in a given server $DHCP_Scope_Stats = Get-DhcpServerv4ScopeStatistics -ComputerName $DHCP_Server.DNSName -ScopeId $DHCP_Scope.ScopeId [PSCustomObject] @{ 'DHCP Server' = $DHCP_Server.DNSName 'DHCP IP' = $DHCP_Server.IPAddress 'Scope ID' = $DHCP_Scope.ScopeId.IPAddressToString 'Scope Name' = $DHCP_Scope.Name 'Scope State' = $DHCP_Scope.State 'In Use' = $DHCP_Scope_Stats.InUse 'Free' = $DHCP_Scope_Stats.Free '% In Use' = ([math]::Round($DHCP_Scope_Stats.PercentageInUse, 0)) 'Reserved' = $DHCP_Scope_Stats.Reserved 'Subnet Mask' = $DHCP_Scope.SubnetMask 'Start Range' = $DHCP_Scope.StartRange 'End Range' = $DHCP_Scope.EndRange 'Lease Duration' = $DHCP_Scope.LeaseDuration } } } Email -AttachSelf -AttachSelfName 'DHCP Report' { EmailHeader { EmailFrom -Address 'myemail@evotec.pl' EmailTo -Addresses "myemail1@evotec.pl" EmailServer -Server 'smtp.office365.com' -UserName 'myemail@evotec.pl' -Password "$ENV:UserProfile\Desktop\Password-Evotec.txt" -PasswordAsSecure -PasswordFromFile -Port 587 -SSL EmailOptions -Priority High -DeliveryNotifications Never EmailSubject -Subject 'DHCP Report - Scope Utilization' } EmailBody { EmailTextBox -FontFamily 'Calibri' -Size 17 -TextDecoration underline -Color DarkSalmon -Alignment center { 'Demonstration' } EmailText -LineBreak EmailTable -DataTable $Output { New-TableCondition -Name '% In Use' -Operator ge -Value 95 -BackGroundColor Red -Color White -Inline -ComparisonType number New-TableCondition -Name '% In Use' -Operator ge -Value 80 -BackGroundColor Yellow -Color Black -Inline -ComparisonType number New-TableCondition -Name '% In Use' -Operator lt -Value 80 -BackGroundColor Green -Color White -Inline -ComparisonType number New-TableCondition -Name 'Scope State' -Operator eq -Value 'Inactive' -BackGroundColor Gray -Color White -Inline -ComparisonType string New-TableHeader -Title "DHCP Scope Statistics Report ($(Get-Date))" -Alignment center -BackGroundColor BuddhaGold -Color White -FontWeight bold New-TableHeader -Names 'DHCP Server', 'DHCP IP' -Title 'Server Information' -Color White -Alignment center -BackGroundColor Gray New-TableHeader -Names 'Subnet Mask', 'Start Range', 'End Range', 'Lease Duration' -Title 'Scope Configuration' -Color White -Alignment center -BackGroundColor Gray } -HideFooter } } -Supress $false
Of course, thanks to having separate data gathering from data displaying, you could, for example, send an email only if certain conditions are met. Below is the same code just with renamed New-TableCondition to its alias EmailTableCondition and New-TableHeader to EmailTableHeader. It's a small change, but I do prefer to keep naming convention that way. It's just an alias, so only my preference.
Import-Module PSWriteHTML $DHCP_Servers = Get-DhcpServerInDC | Sort-Object -Property DnsName $Output = Foreach ($DHCP_Server in $DHCP_Servers) { # Going through the DHCP servers that were returned one at a time to pull statistics try { $DHCP_Scopes = Get-DhcpServerv4Scope –ComputerName $DHCP_Server.DNSName -ErrorAction Stop } catch { Write-Warning "Couldn't reach server $($DHCP_Server.DNSName)" $DHCP_Scopes = $Null } Foreach ($DHCP_Scope in $DHCP_Scopes) { # Going through the scopes returned in a given server $DHCP_Scope_Stats = Get-DhcpServerv4ScopeStatistics -ComputerName $DHCP_Server.DNSName -ScopeId $DHCP_Scope.ScopeId [PSCustomObject] @{ 'DHCP Server' = $DHCP_Server.DNSName 'DHCP IP' = $DHCP_Server.IPAddress 'Scope ID' = $DHCP_Scope.ScopeId 'Scope Name' = $DHCP_Scope.Name 'Scope State' = $DHCP_Scope.State 'In Use' = $DHCP_Scope_Stats.InUse 'Free' = $DHCP_Scope_Stats.Free '% In Use' = ([math]::Round($DHCP_Scope_Stats.PercentageInUse, 0)) 'Reserved' = $DHCP_Scope_Stats.Reserved 'Subnet Mask' = $DHCP_Scope.SubnetMask 'Start Range' = $DHCP_Scope.StartRange 'End Range' = $DHCP_Scope.EndRange 'Lease Duration' = $DHCP_Scope.LeaseDuration } } } Email -AttachSelf -AttachSelfName 'DHCP Report' { EmailHeader { EmailFrom -Address 'myemail@evotec.pl' EmailTo -Addresses "myemail2@evotec.pl" EmailServer -Server 'smtp.office365.com' -Username 'myemail@evotec.pl' -Password "$ENV:UserProfile\Desktop\Password-Evotec.txt" -PasswordAsSecure -PasswordFromFile -Port 587 -SSL EmailOptions -Priority High -DeliveryNotifications Never EmailSubject -Subject 'DHCP Report - Scope Utilization' } EmailBody { EmailTextBox -FontFamily 'Calibri' -Size 17 -TextDecoration underline -Color DarkSalmon -Alignment center { 'Demonstration' } EmailText -LineBreak EmailTable -DataTable $Output { EmailTableCondition -Name '% In Use' -Operator ge -Value 95 -BackgroundColor Red -Color White -Inline -ComparisonType number EmailTableCondition -Name '% In Use' -Operator ge -Value 80 -BackgroundColor Yellow -Color Black -Inline -ComparisonType number EmailTableCondition -Name '% In Use' -Operator lt -Value 80 -BackgroundColor Green -Color White -Inline -ComparisonType number EmailTableCondition -Name 'Scope State' -Operator eq -Value 'Inactive' -BackgroundColor Gray -Color White -Inline -ComparisonType string EmailTableHeader -Title "DHCP Scope Statistics Report ($(Get-Date))" -Alignment center -BackGroundColor BuddhaGold -Color White -FontWeight bold EmailTableHeader -Names 'DHCP Server', 'DHCP IP' -Title 'Server Information' -Color White -Alignment center -BackGroundColor Gray EmailTableHeader -Names 'Subnet Mask', 'Start Range', 'End Range', 'Lease Duration' -Title 'Scope Configuration' -Color White -Alignment center -BackGroundColor Gray } -HideFooter } } -Supress $false
Now you know how to display DHCP Scope Statistics with Out-HTMLView or how to send an email with Email. But that's just a small part of PSWriteHTML. With minimal effort on your side, you can have multiple dynamic tables next to each other, on different tabs, with as many complications as you want.
I've taken the same DHCP output and added a list of processes running on the system. This is to show you that you can display any data next to each other without much preparation. And the code to do it is less than 20 lines of code.
New-HTML { New-HTMLSection -Invisible { New-HTMLSection -HeaderText 'DHCP Report' { New-HTMLTable -DataTable $Output { New-TableCondition -Name '% In Use' -Operator ge -Value 95 -BackgroundColor Red -Color White -Inline -ComparisonType number New-TableCondition -Name '% In Use' -Operator ge -Value 80 -BackgroundColor Yellow -Color Black -Inline -ComparisonType number New-TableCondition -Name '% In Use' -Operator lt -Value 80 -BackgroundColor Green -Color White -Inline -ComparisonType number New-TableCondition -Name 'Scope State' -Operator eq -Value 'Inactive' -BackgroundColor Gray -Color White -Inline -ComparisonType string New-TableHeader -Title "DHCP Scope Statistics Report ($(Get-Date))" -Alignment center -BackGroundColor BuddhaGold -Color White -FontWeight bold New-TableHeader -Names 'DHCP Server', 'DHCP IP' -Title 'Server Information' -Color White -Alignment center -BackGroundColor Gray New-TableHeader -Names 'Subnet Mask', 'Start Range', 'End Range', 'Lease Duration' -Title 'Scope Configuration' -Color White -Alignment center -BackGroundColor Gray } } New-HTMLSection -HeaderText 'Some Processes' { New-HTMLTable -DataTable (Get-Process | Select-Object -First 15) { } } } } -FilePath $Env:UserProfile\Desktop\DHCPReport.html -Online #-ShowHTML
Here's the full code with two tables.
# Dynamically pulling the DHCP servers in a Active Directory domain $DHCP_Servers = Get-DhcpServerInDC | Sort-Object -Property DnsName $Output = Foreach ($DHCP_Server in $DHCP_Servers) { # Going through the DHCP servers that were returned one at a time to pull statistics try { $DHCP_Scopes = Get-DhcpServerv4Scope –ComputerName $DHCP_Server.DNSName -ErrorAction Stop } catch { Write-Warning "Couldn't reach server $($DHCP_Server.DNSName)" $DHCP_Scopes = $Null } Foreach ($DHCP_Scope in $DHCP_Scopes) { # Going through the scopes returned in a given server $DHCP_Scope_Stats = Get-DhcpServerv4ScopeStatistics -ComputerName $DHCP_Server.DNSName -ScopeId $DHCP_Scope.ScopeId [PSCustomObject] @{ 'DHCP Server' = $DHCP_Server.DNSName 'DHCP IP' = $DHCP_Server.IPAddress 'Scope ID' = $DHCP_Scope.ScopeId.IPAddressToString 'Scope Name' = $DHCP_Scope.Name 'Scope State' = $DHCP_Scope.State 'In Use' = $DHCP_Scope_Stats.InUse 'Free' = $DHCP_Scope_Stats.Free '% In Use' = ([math]::Round($DHCP_Scope_Stats.PercentageInUse, 0)) 'Reserved' = $DHCP_Scope_Stats.Reserved 'Subnet Mask' = $DHCP_Scope.SubnetMask 'Start Range' = $DHCP_Scope.StartRange 'End Range' = $DHCP_Scope.EndRange 'Lease Duration' = $DHCP_Scope.LeaseDuration } } } New-HTML { New-HTMLSection -Invisible { New-HTMLSection -HeaderText 'DHCP Report' { New-HTMLTable -DataTable $Output { New-TableCondition -Name '% In Use' -Operator ge -Value 95 -BackgroundColor Red -Color White -Inline -ComparisonType number New-TableCondition -Name '% In Use' -Operator ge -Value 80 -BackgroundColor Yellow -Color Black -Inline -ComparisonType number New-TableCondition -Name '% In Use' -Operator lt -Value 80 -BackgroundColor Green -Color White -Inline -ComparisonType number New-TableCondition -Name 'Scope State' -Operator eq -Value 'Inactive' -BackgroundColor Gray -Color White -Inline -ComparisonType string New-TableHeader -Title "DHCP Scope Statistics Report ($(Get-Date))" -Alignment center -BackGroundColor BuddhaGold -Color White -FontWeight bold New-TableHeader -Names 'DHCP Server', 'DHCP IP' -Title 'Server Information' -Color White -Alignment center -BackGroundColor Gray New-TableHeader -Names 'Subnet Mask', 'Start Range', 'End Range', 'Lease Duration' -Title 'Scope Configuration' -Color White -Alignment center -BackGroundColor Gray } } New-HTMLSection -HeaderText 'Some Processes' { New-HTMLTable -DataTable (Get-Process | Select-Object -First 15) { } } } } -FilePath $Env:UserProfile\Desktop\DHCPReport.html -Online -ShowHTML
Of course, you could do a lot more if you wanted to. Let's try to get other information from DHCP servers.
# Dynamically pulling the DHCP servers in a Active Directory domain $DHCP_Servers = Get-DhcpServerInDC | Sort-Object -Property DnsName $Output = Foreach ($DHCP_Server in $DHCP_Servers) { # Going through the DHCP servers that were returned one at a time to pull statistics try { $DHCP_Scopes = Get-DhcpServerv4Scope –ComputerName $DHCP_Server.DNSName -ErrorAction Stop } catch { Write-Warning "Couldn't reach server $($DHCP_Server.DNSName)" $DHCP_Scopes = $Null } Foreach ($DHCP_Scope in $DHCP_Scopes) { # Going through the scopes returned in a given server $DHCP_Scope_Stats = Get-DhcpServerv4ScopeStatistics -ComputerName $DHCP_Server.DNSName -ScopeId $DHCP_Scope.ScopeId [PSCustomObject] @{ 'DHCP Server' = $DHCP_Server.DNSName 'DHCP IP' = $DHCP_Server.IPAddress 'Scope ID' = $DHCP_Scope.ScopeId.IPAddressToString 'Scope Name' = $DHCP_Scope.Name 'Scope State' = $DHCP_Scope.State 'In Use' = $DHCP_Scope_Stats.InUse 'Free' = $DHCP_Scope_Stats.Free '% In Use' = ([math]::Round($DHCP_Scope_Stats.PercentageInUse, 0)) 'Reserved' = $DHCP_Scope_Stats.Reserved 'Subnet Mask' = $DHCP_Scope.SubnetMask 'Start Range' = $DHCP_Scope.StartRange 'End Range' = $DHCP_Scope.EndRange 'Lease Duration' = $DHCP_Scope.LeaseDuration } } } New-HTML { New-HTMLTab -Name 'Summary' { New-HTMLSection -HeaderText 'All servers' { New-HTMLTable -DataTable $DHCP_Servers } foreach ($Server in $DHCP_Servers) { New-HTMLSection -Invisible { try { $Database = Get-DhcpServerDatabase -ComputerName $Server.DnsName } catch { continue } New-HTMLSection -HeaderText "Server $($Server.DnsName) - Database Information" { New-HTMLTable -DataTable $Database } try { $AuditLog = Get-DhcpServerAuditLog -ComputerName $Server.DnsName } catch { continue } New-HTMLSection -HeaderText "Server $($Server.DnsName) - Audit Log" { New-HTMLTable -DataTable $AuditLog } } } } New-HTMLTab -Name 'All DHCP Scopes' { New-HTMLSection -HeaderText 'DHCP Report' { New-HTMLTable -DataTable $Output { New-TableCondition -Name '% In Use' -Operator ge -Value 95 -BackgroundColor Red -Color White -Inline -ComparisonType number New-TableCondition -Name '% In Use' -Operator ge -Value 80 -BackgroundColor Yellow -Color Black -Inline -ComparisonType number New-TableCondition -Name '% In Use' -Operator lt -Value 80 -BackgroundColor Green -Color White -Inline -ComparisonType number New-TableCondition -Name 'Scope State' -Operator eq -Value 'Inactive' -BackgroundColor Gray -Color White -Inline -ComparisonType string New-TableHeader -Title "DHCP Scope Statistics Report ($(Get-Date))" -Alignment center -BackgroundColor BuddhaGold -Color White -FontWeight bold New-TableHeader -Names 'DHCP Server', 'DHCP IP' -Title 'Server Information' -Color White -Alignment center -BackgroundColor Gray New-TableHeader -Names 'Subnet Mask', 'Start Range', 'End Range', 'Lease Duration' -Title 'Scope Configuration' -Color White -Alignment center -BackgroundColor Gray } } } } -FilePath $Env:UserProfile\Desktop\DHCPReport.html -Online -ShowHTML
What that 80 lines code get you? Custom HTML report with two tabs, multiple tables, all with advanced options.
Could it be cuter? Sure it could. I've spent very little time optimizing it looks. You could change colors, placement, or even add charts, gages, or other things.
If you liked what was presented here, feel free to read other related articles. Keep in mind that some are quite old, and a few things have changed. For example, there's no more Emailimo or Dashimo, as all those options are now part of PSWriteHTML. There's still separate Statusimo, but you don't need it to build something of your own.
As mentioned earlier to simply get it up and running, just install it from PowerShellGallery and you're good.
Install-Module PSWriteHTML -Force
If you want to explore other examples, I recommend running those available on GitHub. A lot of stuff that PSWriteHTML can do doesn't have documentation but is shown in Examples.