Skip to content

Adding Charts to a Report Module

AsBuiltReport.Chart is an optional PowerShell module that adds chart generation capabilities to AsBuiltReport report modules. It provides five chart types — pie, bar, stacked bar, single stacked bar, and signal — and integrates directly with the PScribo document framework used by AsBuiltReport.

Charts are generated in memory as base64-encoded images and embedded directly into the report output using PScribo's Image cmdlet. No intermediate files are written to disk.

Prerequisite Reading

This guide assumes you are already familiar with creating AsBuiltReport report modules. If not, read the Creating a Report Module guide first.

Installation

Install AsBuiltReport.Chart from the PowerShell Gallery:

Install AsBuiltReport.Chart
Install-Module -Name AsBuiltReport.Chart -Scope CurrentUser

Declaring the Dependency

Add AsBuiltReport.Chart to the RequiredModules array in your module manifest (.psd1). This ensures the module is automatically installed when your report module is installed.

Module manifest (.psd1) — RequiredModules
RequiredModules = @(
    @{
        ModuleName    = 'AsBuiltReport.Core'
        ModuleVersion = '1.6.1'
    }
    @{
        ModuleName    = 'AsBuiltReport.Chart'
        ModuleVersion = '0.3.2'
    }
)

How Charts Are Embedded

All chart cmdlets accept a -Format base64 parameter that returns the chart as a base64-encoded string rather than writing a file to disk. This string is then passed directly to PScribo's Image cmdlet.

Core integration pattern
# Generate chart as base64 string
$chartFileItem = New-PieChart -Title 'VM Power States' `
    -Values $chartValues `
    -Labels $chartLabels `
    -Format base64 `
    -Width 600 -Height 400

# Embed the chart in the PScribo report
if ($chartFileItem) {
    Image -Text 'VM Power State Distribution' -Align 'Center' -Percent 100 -Base64 $chartFileItem
}

The Image cmdlet is provided by PScribo and is available inside any Section block in your report module.

Data Preparation

Chart cmdlets require their input arrays to be explicitly typed. Always cast labels to [string[]] and values to [double[]] before passing them to a chart cmdlet.

Casting arrays for chart input
$sampleData = [ordered]@{
    'Success' = 85
    'Warning' = 12
    'Failed'  = 3
}

$chartLabels = [string[]]$sampleData.Keys
$chartValues = [double[]]$sampleData.Values

Chart Types

Pie Chart

Use New-PieChart to show proportional distribution across categories — for example, VM power states, job results, or license usage by type.

Pie chart — VM power state distribution
$sampleData = [ordered]@{
    'Powered On'  = ($VMs | Where-Object { $_.PowerState -eq 'PoweredOn' } | Measure-Object).Count
    'Powered Off' = ($VMs | Where-Object { $_.PowerState -eq 'PoweredOff' } | Measure-Object).Count
    'Suspended'   = ($VMs | Where-Object { $_.PowerState -eq 'Suspended' } | Measure-Object).Count
}

$chartLabels = [string[]]$sampleData.Keys
$chartValues = [double[]]$sampleData.Values

$chartFileItem = New-PieChart `
    -Title ' ' `
    -Values $chartValues `
    -Labels $chartLabels `
    -EnableCustomColorPalette `
    -CustomColorPalette @('#DFF0D0', '#FFF3C4', '#FECDD1') `
    -Width 600 -Height 400 `
    -Format base64 `
    -EnableLegend `
    -LegendOrientation Vertical `
    -LegendAlignment UpperRight `
    -TitleFontBold `
    -TitleFontSize 16

Bar Chart

Use New-BarChart to compare values across discrete categories — for example, backup job results by status, or compliance check outcomes.

Bar chart — backup job results by status
$sampleData = [ordered]@{
    'Success' = ($Jobs | Where-Object { $_.LatestStatus -eq 'Success' } | Measure-Object).Count
    'Warning' = ($Jobs | Where-Object { $_.LatestStatus -eq 'Warning' } | Measure-Object).Count
    'Failed'  = ($Jobs | Where-Object { $_.LatestStatus -eq 'Failed' } | Measure-Object).Count
    'None'    = ($Jobs | Where-Object { $_.LatestStatus -eq 'None' } | Measure-Object).Count
}

$chartLabels = [string[]]$sampleData.Keys
$chartValues = [double[]]$sampleData.Values

$chartFileItem = New-BarChart `
    -Title 'Backup Job Results' `
    -Values $chartValues `
    -Labels $chartLabels `
    -LabelXAxis 'Status' `
    -LabelYAxis 'Job Count' `
    -EnableCustomColorPalette `
    -CustomColorPalette @('#DFF0D0', '#FFF3C4', '#FECDD1', '#ADACAF') `
    -Width 600 -Height 400 `
    -Format base64 `
    -EnableLegend `
    -LegendOrientation Horizontal `
    -LegendAlignment UpperCenter `
    -AxesMarginsTop 0.5 `
    -TitleFontBold `
    -TitleFontSize 16

Stacked Bar Chart

Use New-StackedBarChart when each bar is composed of multiple segments — for example, repository used space vs. free space, or resource allocation across multiple hosts.

The -Values parameter expects an array of arrays: one inner array per bar, with each element representing a segment value.

Stacked bar chart — repository space utilisation
$sampleData = $Repositories | Select-Object Name, UsedSpacePercent, FreeSpacePercent

$chartLabels    = [string[]]$sampleData.Name
$chartCategories = @('Used (%)', 'Free (%)')
$chartUsedValues = [double[]]$sampleData.UsedSpacePercent
$chartFreeValues = [double[]]$sampleData.FreeSpacePercent

# Build array-of-arrays: one inner array per bar (one per repository)
$chartValues = @()
foreach ($i in 0..($chartLabels.Count - 1)) {
    $chartValues += , @($chartUsedValues[$i], $chartFreeValues[$i])
}

$chartFileItem = New-StackedBarChart `
    -Title 'Repository Space Utilisation' `
    -Values $chartValues `
    -Labels $chartLabels `
    -LegendCategories $chartCategories `
    -EnableCustomColorPalette `
    -CustomColorPalette @('#FECDD1', '#DFF0D0') `
    -Width 600 -Height 600 `
    -Format base64 `
    -AreaOrientation Horizontal `
    -LabelXAxis 'Space (%)' `
    -LabelYAxis 'Repository' `
    -EnableLegend `
    -LegendOrientation Horizontal `
    -LegendAlignment UpperCenter `
    -AxesMarginsTop 0.2 `
    -TitleFontBold `
    -TitleFontSize 16

Single Stacked Bar Chart

Use New-SingleStackedBarChart to show a single bar divided into labelled segments — useful for illustrating licence consumption or a single capacity resource.

Single stacked bar chart — licence consumption by workload type
$chartLabels = [string[]]$LicenseUsage.WorkloadType
$chartValues = [double[]]$LicenseUsage.UsedInstances

$chartFileItem = New-SingleStackedBarChart `
    -Title ' ' `
    -Values $chartValues `
    -Label 'Workload Type' `
    -LegendCategories $chartLabels `
    -EnableCustomColorPalette `
    -CustomColorPalette @('#DFF0D0', '#FFF3C4', '#FECDD1', '#ADACAF', '#B3E5FC', '#F8BBD0') `
    -Width 600 -Height 200 `
    -Format base64 `
    -AreaOrientation Horizontal `
    -EnableLegend `
    -LegendOrientation Horizontal `
    -LegendAlignment UpperCenter `
    -TitleFontBold `
    -TitleFontSize 16

Signal Chart

Use New-SignalChart for time-series or trend data — for example, CPU utilisation over time or throughput metrics. The -Values parameter expects an array of arrays, where each inner array represents one line.

Signal chart — CPU utilisation trend (single line)
# Wrap a single line's values in a jagged array
$chartValues = @(,[double[]]@(42, 55, 61, 49, 78, 65, 53))

$chartFileItem = New-SignalChart `
    -Title 'CPU Utilisation (%)' `
    -Values $chartValues `
    -Format base64 `
    -Width 600 -Height 400 `
    -TitleFontBold `
    -TitleFontSize 16
Signal chart — multi-line throughput trend
$readThroughput  = [double[]]@(120, 145, 132, 160, 155)
$writeThroughput = [double[]]@(85,  92,  78,  101, 96)

$chartValues = @(,$readThroughput) + @(,$writeThroughput)

$chartFileItem = New-SignalChart `
    -Title 'Storage Throughput (MB/s)' `
    -Values $chartValues `
    -Labels @('Read', 'Write') `
    -Format base64 `
    -Width 600 -Height 400 `
    -EnableLegend `
    -LegendOrientation Horizontal `
    -LegendAlignment UpperCenter `
    -TitleFontBold `
    -TitleFontSize 16

Safety Guards

Chart generation can return $null if data is empty or an error occurs. Always validate the return value before calling Image, and verify that the underlying data contains non-zero values before rendering the chart.

Chart safety guard pattern
if ($chartFileItem -and ($chartValues | Measure-Object -Sum).Sum -ne 0) {
    Image -Text 'Chart alt text' -Align 'Center' -Percent 100 -Base64 $chartFileItem
}

Wrap the entire chart generation block in a try/catch when the data collection itself could fail:

Chart generation with error handling
try {
    $chartFileItem = New-BarChart -Title 'Job Results' -Values $chartValues -Labels $chartLabels `
        -Format base64 -Width 600 -Height 400

    if ($chartFileItem -and ($chartValues | Measure-Object -Sum).Sum -ne 0) {
        Image -Text 'Backup job result distribution' -Align 'Center' -Percent 100 -Base64 $chartFileItem
    }
} catch {
    Write-PScriboMessage -IsWarning "Unable to generate chart: $($_.Exception.Message)"
}

Color Palettes

AsBuiltReport.Chart supports 25 built-in colour palettes via the -ColorPalette parameter, or a custom palette via -EnableCustomColorPalette and -CustomColorPalette.

For consistency across report modules, use the following standard palette when representing status outcomes (Success / Warning / Failed / Unknown):

Standard status colour palette
$statusCustomPalette = @('#DFF0D0', '#FFF3C4', '#FECDD1', '#ADACAF')
# Green = Success, Yellow = Warning, Pink = Failed, Gray = Unknown/None

To use a built-in palette instead:

Using a built-in colour palette
New-PieChart -Title 'Distribution' -Values $chartValues -Labels $chartLabels `
    -ColorPalette Category20 -Format base64 -Width 600 -Height 400

Available palettes: Amber, Category10, Category20, Aurora, Building, ColorblindFriendly, ColorblindFriendlyDark, Dark, DarkPastel, Frost, LightOcean, LightSpectrum, Microcharts, Nero, Nord, Normal, OneHalf, OneHalfDark, PastelWheel, Penumbra, PolarNight, Redness, SnowStorm, SummerSplash, Tsitsulin

Placing Charts in a Report

Charts should appear before their related data table within the same section, providing a visual summary that the table then backs with detail. Place the Image call, then a BlankLine, then the Table call.

Chart followed by table — recommended layout
Section -Style NOTOCHeading4 'Job Status Summary' {
    if ($chartFileItem -and ($chartValues | Measure-Object -Sum).Sum -ne 0) {
        Image -Text 'Backup job result distribution' -Align 'Center' -Percent 100 -Base64 $chartFileItem
    }
    BlankLine
    $OutObj | Sort-Object -Property 'Name' | Table @TableParams
}

Complete Private Function Example

The following example shows a complete private function that collects backup job data, generates a bar chart of job results by status, and renders the chart above a summary table — the pattern used throughout the AsBuiltReport ecosystem.

Get-AbrVbrBackupJobSummary.ps1
function Get-AbrVbrBackupJobSummary {
    <#
    .SYNOPSIS
        Used by As Built Report to retrieve Veeam VBR backup job summary information.
    .DESCRIPTION
        Documents the configuration of Veeam VBR in Word/HTML/Text formats using PScribo.
    .NOTES
        Version:    0.1.0
        Author:     Your Name
    .LINK
        https://github.com/AsBuiltReport/AsBuiltReport.Veeam.VBR
    #>
    [CmdletBinding()]
    param ()

    begin {
        Write-PScriboMessage "Collecting backup job summary information."
    }

    process {
        try {
            $Jobs = Get-VBRJob -ErrorAction Stop

            if ($Jobs) {
                # Aggregate job results by status
                $sampleData = [ordered]@{
                    'Success' = ($Jobs | Where-Object { $_.Info.LatestStatus -eq 'Success' } | Measure-Object).Count
                    'Warning' = ($Jobs | Where-Object { $_.Info.LatestStatus -eq 'Warning' } | Measure-Object).Count
                    'Failed'  = ($Jobs | Where-Object { $_.Info.LatestStatus -eq 'Failed' } | Measure-Object).Count
                    'None'    = ($Jobs | Where-Object { $_.Info.LatestStatus -eq 'None' } | Measure-Object).Count
                }

                $chartLabels = [string[]]$sampleData.Keys
                $chartValues = [double[]]$sampleData.Values

                $statusCustomPalette = @('#DFF0D0', '#FFF3C4', '#FECDD1', '#ADACAF')

                $chartFileItem = New-BarChart `
                    -Title 'Backup Job Results' `
                    -Values $chartValues `
                    -Labels $chartLabels `
                    -LabelXAxis 'Status' `
                    -LabelYAxis 'Job Count' `
                    -EnableCustomColorPalette `
                    -CustomColorPalette $statusCustomPalette `
                    -Width 600 -Height 400 `
                    -Format base64 `
                    -EnableLegend `
                    -LegendOrientation Horizontal `
                    -LegendAlignment UpperCenter `
                    -AxesMarginsTop 0.5 `
                    -TitleFontBold `
                    -TitleFontSize 16

                Section -Style Heading3 'Backup Jobs' {
                    Paragraph "The following section provides a summary of all backup jobs and their current status."
                    BlankLine

                    Section -Style NOTOCHeading4 'Job Status Summary' {
                        if ($chartFileItem -and ($chartValues | Measure-Object -Sum).Sum -ne 0) {
                            Image -Text 'Backup job result distribution' -Align 'Center' -Percent 100 -Base64 $chartFileItem
                        }
                        BlankLine

                        $OutObj = foreach ($Job in ($Jobs | Sort-Object Name)) {
                            try {
                                [PSCustomObject]@{
                                    'Name'          = $Job.Name
                                    'Type'          = $Job.JobType
                                    'Last Result'   = $Job.Info.LatestStatus
                                    'Last Run'      = $Job.Info.EndTimeUtc
                                }
                            } catch {
                                Write-PScriboMessage -IsWarning "$($Job.Name): $($_.Exception.Message)"
                            }
                        }

                        $TableParams = @{
                            Name         = 'Backup Job Status'
                            List         = $false
                            ColumnWidths = 35, 25, 20, 20
                        }
                        if ($Report.ShowTableCaptions) {
                            $TableParams['Caption'] = "- $($TableParams.Name)"
                        }
                        $OutObj | Table @TableParams
                    }
                }
            }
        } catch {
            Write-PScriboMessage -IsWarning "Backup Job Summary: $($_.Exception.Message)"
        }
    }
    end {}
}

Localising Chart Text

When your module supports multiple languages, store chart titles and Image alt-text in your language file alongside other translated strings.

Language file entries for chart content (en-US)
GetAbrVbrBackupJobSummary = ConvertFrom-StringData @'
    Collecting     = Collecting backup job summary information.
    Heading        = Backup Jobs
    Paragraph      = The following section provides a summary of all backup jobs and their current status.
    StatusSection  = Job Status Summary
    ChartTitle     = Backup Job Results
    ChartAltText   = Backup job result distribution
    ChartXAxis     = Status
    ChartYAxis     = Job Count
    Success        = Success
    Warning        = Warning
    Failed         = Failed
    None           = None
'@
Using $LocalizedData for chart content
begin {
    $LocalizedData = $reportTranslate.GetAbrVbrBackupJobSummary
    Write-PScriboMessage $LocalizedData.Collecting
}

process {
    $sampleData = [ordered]@{
        ($LocalizedData.Success) = ($Jobs | Where-Object { $_.Info.LatestStatus -eq 'Success' } | Measure-Object).Count
        ($LocalizedData.Warning) = ($Jobs | Where-Object { $_.Info.LatestStatus -eq 'Warning' } | Measure-Object).Count
        ($LocalizedData.Failed)  = ($Jobs | Where-Object { $_.Info.LatestStatus -eq 'Failed' } | Measure-Object).Count
        ($LocalizedData.None)    = ($Jobs | Where-Object { $_.Info.LatestStatus -eq 'None' } | Measure-Object).Count
    }

    $chartLabels = [string[]]$sampleData.Keys
    $chartValues = [double[]]$sampleData.Values

    $chartFileItem = New-BarChart `
        -Title $LocalizedData.ChartTitle `
        -Values $chartValues `
        -Labels $chartLabels `
        -LabelXAxis $LocalizedData.ChartXAxis `
        -LabelYAxis $LocalizedData.ChartYAxis `
        -EnableCustomColorPalette `
        -CustomColorPalette @('#DFF0D0', '#FFF3C4', '#FECDD1', '#ADACAF') `
        -Width 600 -Height 400 `
        -Format base64 `
        -EnableLegend -LegendOrientation Horizontal -LegendAlignment UpperCenter `
        -AxesMarginsTop 0.5 -TitleFontBold -TitleFontSize 16

    if ($chartFileItem -and ($chartValues | Measure-Object -Sum).Sum -ne 0) {
        Image -Text $LocalizedData.ChartAltText -Align 'Center' -Percent 100 -Base64 $chartFileItem
    }
}

Common Parameters Reference

The following parameters are available on all five chart cmdlets:

Parameter Type Default Description
-Title String Chart title text
-Format Formats Output format: png, jpg, jpeg, bmp, svg, base64
-Width Int 400 Chart width in pixels
-Height Int 300 Chart height in pixels
-FontName String Arial Base font for all text elements
-TitleFontSize Int 14 Title text size in points
-TitleFontBold Switch Render title in bold
-TitleFontColor BasicColors Title text colour
-EnableLegend Switch Show the chart legend
-LegendOrientation Orientation Horizontal or Vertical
-LegendAlignment Alignments 9-position alignment (e.g. UpperRight, UpperCenter)
-ColorPalette ColorPalettes Built-in palette name
-EnableCustomColorPalette Switch Use -CustomColorPalette hex values
-CustomColorPalette String[] Array of hex colour codes
-EnableBorder Switch Draw a border around the chart
-BorderColor BasicColors Border colour
-BorderSize Int Border thickness
-EnableWatermark Switch Overlay a watermark on the chart
-WatermarkText String Confidential Watermark text
-WatermarkOpacity Double 0.3 Watermark opacity (0.0–1.0)
-OutputFolderPath DirectoryInfo Write chart to this folder (not needed with base64)
-Filename String Output filename (auto-generated if omitted)

Summary of Best Practices

  • Always use -Format base64 — embedding base64 keeps the chart in memory and avoids writing files to the output folder.
  • Guard every Image call — check $chartFileItem is not null and the values sum to a non-zero total before rendering.
  • Cast arrays explicitly[string[]] for labels, [double[]] for values. Passing untyped arrays causes parameter binding errors.
  • Use the standard status palette@('#DFF0D0', '#FFF3C4', '#FECDD1', '#ADACAF') (green, yellow, pink, grey) for consistent status visualisations across modules.
  • Place charts before tables — charts provide visual context; the table provides the underlying detail.
  • Keep chart sections out of the TOC — use NOTOCHeading4 or NOTOCHeading5 for the section containing the chart and table so individual chart sections do not clutter the Table of Contents.
  • Localise chart text — store chart titles, axis labels, and Image alt-text in your language file alongside other translatable strings.