PowerShell

Active Directory DHCP Report to HTML or EMAIL with zero HTML knowledge

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.

DHCP Reporting - Data gathering

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.

DHCP Reporting - Export to HTML

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.

DHCP Reporting - Advanced Out-HTMLView

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.

DHCP Reporting - Advanced Out-HTMLView Full Code

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
DHCP Reporting - Sending reports to email

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.

DHCP Reporting - Full Code to Send Email

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
DHCP Reporting - PSWriteHTML Other Features

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.

Earlier resources for PSWriteHTML, Dashimo, Statusimo or Emailimo as PowerShell Modules

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.

This post was last modified on January 9, 2023 17:06

Przemyslaw Klys

System Architect with over 14 years of experience in the IT field. Skilled, among others, in Active Directory, Microsoft Exchange and Office 365. Profoundly interested in PowerShell. Software geek.

Share
Published by
Przemyslaw Klys

Recent Posts

Upgrade Azure Active Directory Connect fails with unexpected error

Today, I made the decision to upgrade my test environment and update the version of…

2 months ago

Mastering Active Directory Hygiene: Automating Stale Computer Cleanup with CleanupMonster

Have you ever looked at your Active Directory and wondered, "Why do I still have…

3 months ago

Active Directory Replication Summary to your Email or Microsoft Teams

Active Directory replication is a critical process that ensures the consistent and up-to-date state of…

7 months ago

Syncing Global Address List (GAL) to personal contacts and between Office 365 tenants with PowerShell

Hey there! Today, I wanted to introduce you to one of the small but excellent…

12 months ago

Active Directory Health Check using Microsoft Entra Connect Health Service

Active Directory (AD) is crucial in managing identities and resources within an organization. Ensuring its…

1 year ago

Seamless HTML Report Creation: Harness the Power of Markdown with PSWriteHTML PowerShell Module

In today's digital age, the ability to create compelling and informative HTML reports and documents…

1 year ago