Skip to content
ChartForgeX

PowerBGInfo

PowerBGInfo is an Evotec PowerShell module that puts machine details, charts, tables and diagrams on Windows desktop and lock-screen wallpapers. You bring the data; ChartForgeX draws the whole image, from the generated backdrop to the last chart label.

Scenarios
12
Theme presets
6
Cmdlets
32
Install-Module PowerBGInfo
server-status-dark.png1920 x 1080
Server status wallpaper rendered by PowerBGInfo: the host name SQL-PROD-01 with its role and owner, four tiles for uptime, CPU, memory and network, and panels for volumes, network throughput and critical services.

Twelve wallpapers, each with one job

Each script answers one question for the person in front of the screen, and each renders in light and dark at any resolution. A theme preset gives each its look and regions place the panels. Every script starts with a data block of fixed samples, so anyone can reproduce the renders, and a comment beside each value names the command that supplies it on a real machine. Three are shown in full here, with the New-BGInfo call that draws each one and the data block it reads.

Hyper-V host

Room for another VM, and which VMs use the host.

  • Corporate theme preset
  • Treemap sized by memory and colored by CPU status bands
  • Metric cards with status bars and a sparkline
  • Progress bars with threshold colors, a table and a list
hyperv-host-light.pnghyperv-host-dark.png PNG 1920 x 1080, 236 KB PNG 1920 x 1080, 241 KB Script on GitHub
Hyper-V host wallpaper for HV-AMS-07: room for 72 GB of VMs before the failover reserve, tiles for CPU, memory, CSV space and running VMs, a treemap of VMs sized by memory and coloured by CPU, cluster memory bars, live migrations and old checkpoints. Hyper-V host wallpaper for HV-AMS-07: room for 72 GB of VMs before the failover reserve, tiles for CPU, memory, CSV space and running VMs, a treemap of VMs sized by memory and coloured by CPU, cluster memory bars, live migrations and old checkpoints. Dark theme.
PowerShellRun.BGInfo.HyperVHost.ps1
New-BGInfo -Theme $look -Target File {
    New-BGInfoBackground -Width $Width -Height $Height -Pattern Grid `
        -PatternSpacing ([int](56 * $scale))
    # A wider right column for the tables; the treemap may fill the center.
    New-BGInfoLayout -ColumnWidth ([int](520 * $scale)) `
        -MaximumPanelHeight ([int](640 * $scale))

    New-BGInfoLabel -Name 'HYPER-V HOST  ·  PRODUCTION CLUSTER' -Accent `
        -FontSize ([int](15 * $scale)) -FontWeight 700
    New-BGInfoLabel -Name $hv.Name -FontSize ([int](72 * $scale)) `
        -FontWeight 600
    New-BGInfoLabel -Name "Room for $free GB of VMs before failover reserve" `
        -FontSize ([int](19 * $scale)) -FontWeight 600
    New-BGInfoValue -Name 'Cluster' -Value $hv.Cluster
    New-BGInfoValue -Name 'Hardware' -Value $hv.Hardware
    New-BGInfoValue -Name 'System' -Value $hv.Version

    # Top band: four host metrics beside the text. Above 80 % memory or
    # 75 % storage the card turns amber; above 90 % it turns red.
    $card = @{ Region = 'Top'; Height = [int](170 * $scale) }
    New-BGInfoMetric @card -Label 'CPU' -Value $hv.Cpu[-1] -Unit '%' `
        -Caption '64 logical cores' -History $hv.Cpu -HistoryMaximum 100 `
        -WarningThreshold 70 -CriticalThreshold 85
    New-BGInfoMetric @card -Label 'Memory' -Value $memory -Unit '%' `
        -Caption "$($hv.MemoryGB) of $($hv.TotalGB) GB assigned" `
        -WarningThreshold 80 -CriticalThreshold 90
    New-BGInfoMetric @card -Label $hv.Storage.Name -Unit '%' `
        -Value $hv.Storage.UsedPercent `
        -Caption "$($hv.Storage.FreeTB) TB free" `
        -WarningThreshold 75 -CriticalThreshold 90
    New-BGInfoMetric @card -Label 'VMs running' -Value $running `
        -Caption "$vcpu vCPU on 64 cores"

    # Center: where the memory goes, grouped by workload. The color is the
    # VM's CPU: amber from 60 %, red from 85 %.
    $hv.Vms | New-BGInfoTreemap -Region Center -Title 'Memory by VM' `
        -Note 'size: memory · color: CPU' -ValueProperty MemoryGB `
        -GroupProperty Workload -ValueSuffix ' GB' -ColorProperty Cpu `
        -WarningThreshold 60 -CriticalThreshold 85

    # Right column: where else VMs could go, today's live migrations and
    # the checkpoints left behind.
    New-BGInfoChart -Region Right -Id 'nodes' -Title 'Cluster memory' `
        -Kind ProgressBar -Values @($hv.Nodes.Values) `
        -Labels @($hv.Nodes.Keys) -Maximum 100 -ValueSuffix ' %' `
        -NoProgressHandles -ShowLatestValue:$false -NoHistory `
        -WarningThreshold 80 -CriticalThreshold 90 `
        -Height ([int](250 * $scale))
    $hv.Moves | New-BGInfoTable -Region Right -Title 'Live migrations' `
        -Note 'today' -Property Time, VM, Result -StatusColumn Result
    New-BGInfoList -Region Right -Title 'Old checkpoints' `
        -Note 'older than 7 days' -Item $hv.Checkpoints
} -ConfigurationDirectory $OutputDirectory `
    -OutputFileName "hyperv-host-$($Theme.ToLower()).png"
#endregion
Full script 170 lines
PowerShellExamples/Run.BGInfo.HyperVHost.ps1
<#
    Hyper-V host. The question for the administrator at this console: does
    this host have room for another VM, and which VMs are using it?

    The data block returns plain values; the presentation block only reads
    them. Comments name the command or source that supplies each value.
#>
param(
    [ValidateSet('Light', 'Dark')]
    [string] $Theme = 'Dark',
    [int] $Width = 1920,
    [int] $Height = 1080,
    [string] $OutputDirectory = (Join-Path $PSScriptRoot 'Output')
)

Import-Module (Join-Path $PSScriptRoot '..\PowerBGInfo.psd1') -Force

#region Data: your logic goes here
function Get-HyperVHostData {
    # Get-VM returns these properties (MemoryAssigned, ProcessorCount and
    # CPUUsage); the workload tag comes from the VM notes or your CMDB.
    # The sample keeps them as CSV to stay short.
    $vms = @'
Name,Workload,vCPU,MemoryGB,Cpu
SQL-PROD-01,Databases,16,128,71
SQL-PROD-02,Databases,16,96,34
SQL-RPT-01,Databases,8,48,12
ERP-APP-01,ERP,12,64,38
ERP-APP-02,ERP,12,64,29
ERP-BATCH-01,ERP,8,32,55
VDI-POOL-A,Desktops,48,96,41
VDI-POOL-B,Desktops,24,48,18
WEB-01,Web,4,16,22
WEB-02,Web,4,16,19
WEB-03,Web,4,16,9
FS-01,Infrastructure,4,32,6
MON-01,Infrastructure,4,24,14
DC02,Infrastructure,2,8,3
'@ | ConvertFrom-Csv | ForEach-Object {
        [pscustomobject] @{
            Name = $_.Name; Workload = $_.Workload; vCPU = [int] $_.vCPU
            MemoryGB = [int] $_.MemoryGB; Cpu = [int] $_.Cpu; State = 'Running'
        }
    }

    [pscustomobject] @{
        Name     = 'HV-AMS-07'
        # Get-Cluster and Get-ClusterNode
        Cluster  = 'HVC-AMS-01 · node 3 of 6'
        Hardware = '2 × Xeon Gold 6448Y · 64 cores'
        Version  = 'Windows Server 2025 Datacenter'
        # Get-Counter '\Hyper-V Hypervisor Logical Processor(_Total)\
        #     % Total Run Time' sampled every 5 minutes
        Cpu      = @(38, 41, 52, 47, 44, 49, 46)
        # (Get-VMHost).MemoryCapacity and the sum of MemoryAssigned
        MemoryGB = [int] ($vms | Measure-Object -Property MemoryGB -Sum).Sum
        # The cluster keeps one node's worth of memory free for failover.
        ReserveGB = 64
        TotalGB  = 768
        # Get-ClusterSharedVolume | Select-Object -ExpandProperty
        #     SharedVolumeInfo | Select-Object -ExpandProperty Partition
        Storage  = @{ Name = 'CSV-02'; UsedPercent = 68; FreeTB = 6.1 }
        Vms      = $vms
        # Get-ClusterGroup | Where-Object OwnerNode -NE $env:COMPUTERNAME,
        # or the Failover Clustering event log, event 5143 and 1641
        Moves    = @(
            [pscustomobject] @{ Time = '09:12'; VM = 'ERP-BATCH-01'
                Move = 'from HV-AMS-04'; Result = 'OK' }
            [pscustomobject] @{ Time = '08:47'; VM = 'WEB-03'
                Move = 'from HV-AMS-02'; Result = 'OK' }
            [pscustomobject] @{ Time = '02:05'; VM = 'VDI-POOL-B'
                Move = 'to HV-AMS-05'; Result = 'Failed' }
        )
        # Memory assigned on every node: Get-ClusterNode | ForEach-Object {
        #     Get-VM -ComputerName $_.Name | Measure-Object MemoryAssigned }
        Nodes       = [ordered] @{
            'HV-AMS-04' = 71; 'HV-AMS-05' = 64; 'HV-AMS-07' = 82
            'HV-AMS-08' = 91; 'HV-AMS-09' = 58; 'HV-AMS-10' = 47
        }
        # Get-VM | Get-VMSnapshot | Where-Object CreationTime -LT
        #     (Get-Date).AddDays(-7)
        Checkpoints = @(
            @{ Text = 'ERP-APP-02 · before CU 14'; Status = 'Warning'
                Value = '12 days' }
            @{ Text = 'WEB-02 · config test'; Status = 'Warning'
                Value = '9 days' }
        )
    }
}

$fallback = [pscustomobject] @{
    Name = $env:COMPUTERNAME; Cluster = 'unknown'; Hardware = 'unknown'
    Version = 'unknown'; Cpu = @(0); MemoryGB = 0; TotalGB = 1; ReserveGB = 0
    Storage = @{ Name = 'CSV'; UsedPercent = 0; FreeTB = 0 }
    Vms = @([pscustomobject] @{ Name = 'unknown'; Workload = ''
            vCPU = 0; MemoryGB = 1; Cpu = 0; State = 'Unknown' })
    Moves = @(); Checkpoints = @(); Nodes = [ordered] @{ 'unknown' = 0 }
}
$source = @{
    Name           = 'Hyper-V host'
    TimeoutSeconds = 20
    ScriptBlock    = ${function:Get-HyperVHostData}
    Default        = $fallback
}
$hv = Invoke-BGInfoDataSource @source
#endregion

#region Presentation: reads only $hv
$scale = [Math]::Round($Height / 1080, 2)
$look = New-BGInfoTheme Corporate -Mode $Theme -Scale $scale
$memory = [Math]::Round(100 * $hv.MemoryGB / $hv.TotalGB)
$running = @($hv.Vms | Where-Object State -EQ 'Running').Count
$vcpu = ($hv.Vms | Measure-Object vCPU -Sum).Sum
$free = $hv.TotalGB - $hv.MemoryGB - $hv.ReserveGB

New-BGInfo -Theme $look -Target File {
    New-BGInfoBackground -Width $Width -Height $Height -Pattern Grid `
        -PatternSpacing ([int](56 * $scale))
    # A wider right column for the tables; the treemap may fill the center.
    New-BGInfoLayout -ColumnWidth ([int](520 * $scale)) `
        -MaximumPanelHeight ([int](640 * $scale))

    New-BGInfoLabel -Name 'HYPER-V HOST  ·  PRODUCTION CLUSTER' -Accent `
        -FontSize ([int](15 * $scale)) -FontWeight 700
    New-BGInfoLabel -Name $hv.Name -FontSize ([int](72 * $scale)) `
        -FontWeight 600
    New-BGInfoLabel -Name "Room for $free GB of VMs before failover reserve" `
        -FontSize ([int](19 * $scale)) -FontWeight 600
    New-BGInfoValue -Name 'Cluster' -Value $hv.Cluster
    New-BGInfoValue -Name 'Hardware' -Value $hv.Hardware
    New-BGInfoValue -Name 'System' -Value $hv.Version

    # Top band: four host metrics beside the text. Above 80 % memory or
    # 75 % storage the card turns amber; above 90 % it turns red.
    $card = @{ Region = 'Top'; Height = [int](170 * $scale) }
    New-BGInfoMetric @card -Label 'CPU' -Value $hv.Cpu[-1] -Unit '%' `
        -Caption '64 logical cores' -History $hv.Cpu -HistoryMaximum 100 `
        -WarningThreshold 70 -CriticalThreshold 85
    New-BGInfoMetric @card -Label 'Memory' -Value $memory -Unit '%' `
        -Caption "$($hv.MemoryGB) of $($hv.TotalGB) GB assigned" `
        -WarningThreshold 80 -CriticalThreshold 90
    New-BGInfoMetric @card -Label $hv.Storage.Name -Unit '%' `
        -Value $hv.Storage.UsedPercent `
        -Caption "$($hv.Storage.FreeTB) TB free" `
        -WarningThreshold 75 -CriticalThreshold 90
    New-BGInfoMetric @card -Label 'VMs running' -Value $running `
        -Caption "$vcpu vCPU on 64 cores"

    # Center: where the memory goes, grouped by workload. The color is the
    # VM's CPU: amber from 60 %, red from 85 %.
    $hv.Vms | New-BGInfoTreemap -Region Center -Title 'Memory by VM' `
        -Note 'size: memory · color: CPU' -ValueProperty MemoryGB `
        -GroupProperty Workload -ValueSuffix ' GB' -ColorProperty Cpu `
        -WarningThreshold 60 -CriticalThreshold 85

    # Right column: where else VMs could go, today's live migrations and
    # the checkpoints left behind.
    New-BGInfoChart -Region Right -Id 'nodes' -Title 'Cluster memory' `
        -Kind ProgressBar -Values @($hv.Nodes.Values) `
        -Labels @($hv.Nodes.Keys) -Maximum 100 -ValueSuffix ' %' `
        -NoProgressHandles -ShowLatestValue:$false -NoHistory `
        -WarningThreshold 80 -CriticalThreshold 90 `
        -Height ([int](250 * $scale))
    $hv.Moves | New-BGInfoTable -Region Right -Title 'Live migrations' `
        -Note 'today' -Property Time, VM, Result -StatusColumn Result
    New-BGInfoList -Region Right -Title 'Old checkpoints' `
        -Note 'older than 7 days' -Item $hv.Checkpoints
} -ConfigurationDirectory $OutputDirectory `
    -OutputFileName "hyperv-host-$($Theme.ToLower()).png"
#endregion

Lines 116 to 170 on GitHub

Help desk

How to get help, plus the details support asks for.

  • Orchid theme preset
  • Generated gradient background with glows and dots
  • Visual canvas info tiles with built-in icons
  • Shared text styles for headings, values and status
Help desk wallpaper: how to reach the service desk, its portal, hours and current service status, with tiles for asset tag, computer name, IP address, signed-in user and Windows version. Help desk wallpaper: how to reach the service desk, its portal, hours and current service status, with tiles for asset tag, computer name, IP address, signed-in user and Windows version. Dark theme.
PowerShellRun.BGInfo.HelpDesk.ps1
New-BGInfo -Theme $look -TextPosition MiddleLeft -Target File {
    New-BGInfoBackground -Width $Width -Height $Height
    # The tile column sits vertically centered beside the centered text.
    New-BGInfoLayout -ColumnAlignment Center

    New-BGInfoLabel -Name 'IT SERVICE DESK' -Accent `
        -FontSize ([int](16 * $scale)) -FontWeight 700
    New-BGInfoLabel -Name 'Need help?' -FontSize ([int](88 * $scale)) `
        -FontWeight 600
    New-BGInfoLabel -Name 'Call 555-0100, extension 4357' -Accent `
        -FontSize ([int](40 * $scale)) -FontWeight 600
    New-BGInfoLabel -Name 'Have the asset tag on the right ready.' -Muted `
        -FontSize ([int](22 * $scale))
    New-BGInfoValue -Name 'Portal' -Value 'help.contoso.com'
    New-BGInfoValue -Name 'Teams' -Value '#it-help'
    New-BGInfoValue -Name 'Walk-in' -Value 'Building B, room 012'
    New-BGInfoValue -Name 'Hours' -Value 'Monday–Friday, 07:00–19:00'

    New-BGInfoLabel -Name ' ' -FontSize ([int](12 * $scale))
    New-BGInfoLabel -Name 'SERVICE STATUS' -Accent `
        -FontSize ([int](16 * $scale)) -FontWeight 700
    foreach ($service in $facts.Services) {
        New-BGInfoLabel -Name "●  $($service.Text)" -Status $service.Status `
            -FontSize ([int](19 * $scale)) -FontWeight 600
    }

    New-BGInfoVisualCanvas -Region Right -Tile $tiles
} -ConfigurationDirectory $OutputDirectory `
    -OutputFileName "helpdesk-$($Theme.ToLower()).png"
#endregion
Full script 107 lines
PowerShellExamples/Run.BGInfo.HelpDesk.ps1
<#
    Help-desk wallpaper for shared or front-desk PCs. It tells people how
    to get help and shows the details the service desk asks for first.

    The data block returns plain values; the presentation block only reads
    them. Sample values keep the render reproducible, and each one notes the
    command that supplies it on a real machine.
#>
param(
    [ValidateSet('Light', 'Dark')]
    [string] $Theme = 'Light',
    [int] $Width = 1920,
    [int] $Height = 1080,
    [string] $OutputDirectory = (Join-Path $PSScriptRoot 'Output')
)

Import-Module (Join-Path $PSScriptRoot '..\PowerBGInfo.psd1') -Force

#region Data: your logic goes here
function Get-HelpDeskFacts {
    [pscustomobject] @{
        # (Get-CimInstance Win32_SystemEnclosure).SMBIOSAssetTag
        AssetTag     = 'CT-04217'
        ComputerName = 'FRONTDESK-07'
        Location     = 'Reception, B1'
        # (Get-NetIPConfiguration |
        #     Where-Object IPv4DefaultGateway).IPv4Address.IPAddress
        IPAddress    = '10.20.4.37'
        Network      = 'Wired · VLAN 120'
        SignedInAs   = 'Reception (shared)'
        # (Get-CimInstance Win32_OperatingSystem).Caption and .BuildNumber
        Windows      = '11 Enterprise 24H2'
        Build        = '26100'
        # Your monitoring or status-page API
        Services     = @(
            @{ Text = 'Email, Teams, Wi-Fi and VPN are working'; Status = 'Ok' }
            @{
                Text   = 'Floor 2 printers are offline until 14:00'
                Status = 'Warning'
            }
        )
    }
}

$fallback = [pscustomobject] @{
    AssetTag = 'unknown'; ComputerName = $env:COMPUTERNAME
    Location = 'unknown'; IPAddress = 'unknown'; Network = ''
    SignedInAs = 'unknown'; Windows = 'unknown'; Build = ''
    Services = @(@{ Text = 'Service status is unavailable'; Status = 'Info' })
}
$source = @{
    Name           = 'Help-desk facts'
    TimeoutSeconds = 10
    ScriptBlock    = ${function:Get-HelpDeskFacts}
    Default        = $fallback
}
$facts = Invoke-BGInfoDataSource @source
#endregion

#region Presentation: reads only $facts
$scale = [Math]::Round($Height / 1080, 2)
$tile = @{ SurfaceStyle = 'Glass' }
$tiles = @(
    New-BGInfoVisualCanvasTile @tile -IconKind Shield -Label 'ASSET TAG' `
        -Value $facts.AssetTag -Detail 'Sticker under the keyboard'
    New-BGInfoVisualCanvasTile @tile -IconKind Computer `
        -Label 'COMPUTER NAME' -Value $facts.ComputerName `
        -Detail $facts.Location
    New-BGInfoVisualCanvasTile @tile -IconKind Network -Label 'IP ADDRESS' `
        -Value $facts.IPAddress -Detail $facts.Network
    New-BGInfoVisualCanvasTile @tile -IconKind User -Label 'SIGNED IN AS' `
        -Value $facts.SignedInAs -Detail 'Resets at sign-out'
    New-BGInfoVisualCanvasTile @tile -IconKind OperatingSystem `
        -Label 'WINDOWS' -Value $facts.Windows -Detail "Build $($facts.Build)"
)

$look = New-BGInfoTheme Orchid -Mode $Theme -Scale $scale
New-BGInfo -Theme $look -TextPosition MiddleLeft -Target File {
    New-BGInfoBackground -Width $Width -Height $Height
    # The tile column sits vertically centered beside the centered text.
    New-BGInfoLayout -ColumnAlignment Center

    New-BGInfoLabel -Name 'IT SERVICE DESK' -Accent `
        -FontSize ([int](16 * $scale)) -FontWeight 700
    New-BGInfoLabel -Name 'Need help?' -FontSize ([int](88 * $scale)) `
        -FontWeight 600
    New-BGInfoLabel -Name 'Call 555-0100, extension 4357' -Accent `
        -FontSize ([int](40 * $scale)) -FontWeight 600
    New-BGInfoLabel -Name 'Have the asset tag on the right ready.' -Muted `
        -FontSize ([int](22 * $scale))
    New-BGInfoValue -Name 'Portal' -Value 'help.contoso.com'
    New-BGInfoValue -Name 'Teams' -Value '#it-help'
    New-BGInfoValue -Name 'Walk-in' -Value 'Building B, room 012'
    New-BGInfoValue -Name 'Hours' -Value 'Monday–Friday, 07:00–19:00'

    New-BGInfoLabel -Name ' ' -FontSize ([int](12 * $scale))
    New-BGInfoLabel -Name 'SERVICE STATUS' -Accent `
        -FontSize ([int](16 * $scale)) -FontWeight 700
    foreach ($service in $facts.Services) {
        New-BGInfoLabel -Name "●  $($service.Text)" -Status $service.Status `
            -FontSize ([int](19 * $scale)) -FontWeight 600
    }

    New-BGInfoVisualCanvas -Region Right -Tile $tiles
} -ConfigurationDirectory $OutputDirectory `
    -OutputFileName "helpdesk-$($Theme.ToLower()).png"
#endregion

Lines 78 to 107 on GitHub

Meeting room

Which room, is it free, and how to share a screen.

  • Orchid theme preset
  • Single-day Gantt lanes for this room and the rooms next door
  • Wi-Fi QR code
  • Numbered list
meeting-room-light.pngmeeting-room-dark.png PNG 1920 x 1080, 194 KB PNG 1920 x 1080, 241 KB Script on GitHub
Meeting room wallpaper for Oslo: free until 13:00, the next booking, a guest Wi-Fi QR code, numbered steps to share a screen and today's bookings beside the neighbouring rooms. Meeting room wallpaper for Oslo: free until 13:00, the next booking, a guest Wi-Fi QR code, numbered steps to share a screen and today's bookings beside the neighbouring rooms. Dark theme.
PowerShellRun.BGInfo.MeetingRoom.ps1
New-BGInfo -Theme $look -Target File {
    New-BGInfoBackground -Width $Width -Height $Height -Pattern None

    New-BGInfoLabel -Name $room.Where -Accent -FontSize ([int](15 * $scale)) `
        -FontWeight 700
    New-BGInfoLabel -Name $room.Room -FontSize ([int](150 * $scale)) `
        -FontWeight 700
    New-BGInfoLabel -Name "●  $($status.Text)" -Status $status.Status `
        -FontSize ([int](34 * $scale)) -FontWeight 600
    New-BGInfoLabel -Name ' ' -FontSize ([int](8 * $scale))
    New-BGInfoValue -Name 'Next booking' -Value $after
    New-BGInfoValue -Name 'Room' -Value $room.Seats
    New-BGInfoValue -Name 'Book it' -Value $room.Book
    New-BGInfoValue -Name 'Help' -Value 'ext. 4357, or help.contoso.com'

    # Right column: guest Wi-Fi on the phone, then how to share a screen.
    New-BGInfoQRCode -Region Right -Text $room.Wifi.Text -Title 'Guest Wi-Fi' `
        -Caption $room.Wifi.Ssid -Note 'scan to join' `
        -Height ([int](330 * $scale))
    New-BGInfoList -Region Right -Title 'Share your screen' -Marker Number `
        -Item $room.Steps

    # Bottom band: today in this room and the two next to it.
    New-BGInfoTimeline -Region Bottom -Title 'Today' -NoLegend `
        -Note 'Bergen and Tromsø are next door' `
        -Item $day -Now $room.Now -Height ([int](280 * $scale))
} -ConfigurationDirectory $OutputDirectory `
    -OutputFileName "meeting-room-$($Theme.ToLower()).png"
#endregion
Full script 138 lines
PowerShellExamples/Run.BGInfo.MeetingRoom.ps1
<#
    Meeting room PC. The question for someone walking in: which room is
    this, is it free, and how do I get my laptop on the screen?

    The data block returns plain values; the presentation block only reads
    them. Comments name the command or source that supplies each value.
#>
param(
    [ValidateSet('Light', 'Dark')]
    [string] $Theme = 'Dark',
    [int] $Width = 1920,
    [int] $Height = 1080,
    [string] $OutputDirectory = (Join-Path $PSScriptRoot 'Output')
)

Import-Module (Join-Path $PSScriptRoot '..\PowerBGInfo.psd1') -Force

#region Data: your logic goes here
function Get-MeetingRoomData {
    $today = Get-Date '2026-10-16'
    # Microsoft Graph: GET /users/{room}/calendarView for today, for this
    # room and its neighbours. One row per booking.
    function Booking($room, $from, $to) {
        $kind = if ($room -eq 'Oslo') { 'This room' } else { 'Nearby rooms' }
        [pscustomobject] @{
            Lane = $room; Category = $kind
            Start = $today.Add([timespan] $from)
            End = $today.Add([timespan] $to)
        }
    }
    [pscustomobject] @{
        Available = $true
        Room     = 'Oslo'
        Where    = 'BUILDING A  ·  FLOOR 4  ·  ROOM 4.12'
        Seats    = '10 seats · video bar · whiteboard'
        Book     = 'Outlook or Teams, room "Oslo 4.12"'
        Now      = $today.AddHours(11).AddMinutes(50)
        Bookings = @(
            Booking 'Oslo' '08:30' '09:30'
            Booking 'Oslo' '10:00' '11:30'
            Booking 'Oslo' '13:00' '14:00'
            Booking 'Oslo' '15:00' '16:30'
            Booking 'Bergen' '09:00' '12:30'
            Booking 'Bergen' '14:00' '15:00'
            Booking 'Tromsø' '08:00' '10:00'
            Booking 'Tromsø' '11:00' '17:00'
        )
        # The guest network: WIFI:T:<WPA|nopass>;S:<SSID>;P:<key>;;
        Wifi     = @{
            Ssid = 'CONTOSO-GUEST'
            Text = 'WIFI:T:nopass;S:CONTOSO-GUEST;;'
        }
        Steps    = @(
            'Plug the USB-C cable on the table into your laptop'
            'Press Share on the touch panel'
            'No cable? In Teams choose Cast, then Oslo'
            'The screen turns off 5 minutes after you leave'
        )
    }
}

$fallback = [pscustomobject] @{
    Available = $false
    Room = $env:COMPUTERNAME; Where = 'MEETING ROOM'; Seats = ''
    Book = 'Outlook or Teams'; Now = Get-Date; Bookings = @()
    Wifi = @{ Ssid = 'CONTOSO-GUEST'; Text = 'WIFI:T:nopass;S:CONTOSO-GUEST;;' }
    Steps = @('Call the service desk on ext. 4357')
}
$source = @{
    Name           = 'Meeting room data'
    TimeoutSeconds = 10
    ScriptBlock    = ${function:Get-MeetingRoomData}
    Default        = $fallback
}
$room = Invoke-BGInfoDataSource @source
#endregion

#region Presentation: reads only $room
$scale = [Math]::Round($Height / 1080, 2)
$look = New-BGInfoTheme Orchid -Mode $Theme -Scale $scale
$here = @($room.Bookings | Where-Object Lane -EQ $room.Room)
$current = $here |
    Where-Object { $_.Start -le $room.Now -and $_.End -gt $room.Now }
$next = $here | Where-Object Start -GT $room.Now | Sort-Object Start |
    Select-Object -First 1
$status = if (-not $room.Available) {
    @{ Text = 'Availability unavailable'; Status = 'Info' }
} elseif ($current) {
    @{
        Text   = "In use until $($current.End.ToString('HH:mm'))"
        Status = 'Warning'
    }
} elseif ($next) {
    @{ Text = "Free until $($next.Start.ToString('HH:mm'))"; Status = 'Ok' }
} else {
    @{ Text = 'Free for the rest of the day'; Status = 'Ok' }
}
$after = if (-not $room.Available) { 'unavailable' } elseif ($next) {
    "$($next.Start.ToString('HH:mm'))–$($next.End.ToString('HH:mm'))"
} else { 'none today' }
# This room takes the accent; the rooms next door stay quiet.
$quiet = $look.Muted.WithAlpha(150).ToHexRgba()
$day = $room.Bookings | Select-Object *, @{
    Name = 'Color'
    Expression = {
        if ($_.Lane -eq $room.Room) { $look.Accent.ToHex() } else { $quiet }
    }
}

New-BGInfo -Theme $look -Target File {
    New-BGInfoBackground -Width $Width -Height $Height -Pattern None

    New-BGInfoLabel -Name $room.Where -Accent -FontSize ([int](15 * $scale)) `
        -FontWeight 700
    New-BGInfoLabel -Name $room.Room -FontSize ([int](150 * $scale)) `
        -FontWeight 700
    New-BGInfoLabel -Name "●  $($status.Text)" -Status $status.Status `
        -FontSize ([int](34 * $scale)) -FontWeight 600
    New-BGInfoLabel -Name ' ' -FontSize ([int](8 * $scale))
    New-BGInfoValue -Name 'Next booking' -Value $after
    New-BGInfoValue -Name 'Room' -Value $room.Seats
    New-BGInfoValue -Name 'Book it' -Value $room.Book
    New-BGInfoValue -Name 'Help' -Value 'ext. 4357, or help.contoso.com'

    # Right column: guest Wi-Fi on the phone, then how to share a screen.
    New-BGInfoQRCode -Region Right -Text $room.Wifi.Text -Title 'Guest Wi-Fi' `
        -Caption $room.Wifi.Ssid -Note 'scan to join' `
        -Height ([int](330 * $scale))
    New-BGInfoList -Region Right -Title 'Share your screen' -Marker Number `
        -Item $room.Steps

    # Bottom band: today in this room and the two next to it.
    New-BGInfoTimeline -Region Bottom -Title 'Today' -NoLegend `
        -Note 'Bergen and Tromsø are next door' `
        -Item $day -Now $room.Now -Height ([int](280 * $scale))
} -ConfigurationDirectory $OutputDirectory `
    -OutputFileName "meeting-room-$($Theme.ToLower()).png"
#endregion

Lines 110 to 138 on GitHub

More wallpapers

Filter by who the screen is for. Each card opens the wallpaper in the page theme with the same Presentation and Data tabs, and links to the full-size PNG and the script on GitHub.

Any screen

A script lays out for the screen it renders on. The on-call board spreads across a 3440 x 1440 ultrawide in the operations room, and the signage script turns a 1080 x 1920 portrait panel into a poster. Each file is drawn at its size, not scaled from 1920 x 1080.

Ultrawide3440 x 1440
The NOC and on-call wallpaper at 3440 x 1440: the on-call engineer and contacts, a runbook QR code and open incidents across the top, and the escalation path and the week's rota spread across the ultrawide. The NOC and on-call wallpaper at 3440 x 1440: the on-call engineer and contacts, a runbook QR code and open incidents across the top, and the escalation path and the week's rota spread across the ultrawide. Dark theme.
Portrait1080 x 1920
The signage wallpaper at 1080 x 1920: Open day in large type with its date, and the QR code and the highlights in two cards at the foot of the portrait screen. The signage wallpaper at 1080 x 1920: Open day in large type with its date, and the QR code and the highlights in two cards at the foot of the portrait screen. Dark theme.

Rendered by Examples/Run.BGInfo.Showcase.ps1 from PowerBGInfo 2f6f22d, every scenario at 1920 x 1080 and these two also at their own screen sizes. Every wallpaper on this page is a script's output, unedited.

PowerShell Download
Loading source…

Build one yourself

One wallpaper in eight steps. Each step shows only the lines it changes, next to the wallpaper the whole script renders at that point. Every background is generated: there is no stock image underneath, only a gradient, glows and a pattern that ChartForgeX paints at the screen's size.

  1. Start with an empty canvas

    New-BGInfoBackground sets the size and paints one colour. Without it, PowerBGInfo draws on -FilePath or on the wallpaper that is already set, and never on its own earlier output.

    PowerShell01-canvas.ps111 lines
    param([string] $OutputDirectory = "$env:TEMP\PowerBGInfo")
    
    $backdrop = @{
        Width = 1920
        Height = 1080
        Color = '#0B1222'
    }
    
    New-BGInfo -Target File {
        New-BGInfoBackground @backdrop
    } -ConfigurationDirectory $OutputDirectory -OutputFileName 'wallpaper.png'
    
    01-canvas.png1920 x 1080
    Wallpaper for WS-FIN-0042 after the step: start with an empty canvas.
  2. Generate the background in layers

    Three keys on the same hashtable add one layer each. The backdrop is painted at the size you ask for, so it is never a stretched photo.

    Gradient. Two colours blend across the canvas.

    PowerShell02-gradient.ps1+1 −1
    Gradient = '#0B3B46', '#050A14'
    
    02-gradient.png1920 x 1080
    Wallpaper for WS-FIN-0042 after the step: gradient.
    Whole script after this step 11 lines
    PowerShell02-gradient.ps1
    param([string] $OutputDirectory = "$env:TEMP\PowerBGInfo")
    
    $backdrop = @{
        Width = 1920
        Height = 1080
        Gradient = '#0B3B46', '#050A14'
    }
    
    New-BGInfo -Target File {
        New-BGInfoBackground @backdrop
    } -ConfigurationDirectory $OutputDirectory -OutputFileName 'wallpaper.png'
    

    Glows. Soft radial light at relative positions, so it lands in the same place at every resolution.

    PowerShell03-glows.ps1+4 −0
    Glow = @(
        New-BGInfoBackgroundGlow -X 0.85 -Y 0.1 -Radius 0.6 -Color '#2DD4BF40'
        New-BGInfoBackgroundGlow -X 0.05 -Y 0.95 -Radius 0.5 -Color '#3B82F633'
    )
    
    03-glows.png1920 x 1080
    Wallpaper for WS-FIN-0042 after the step: glows.
    Whole script after this step 15 lines
    PowerShell03-glows.ps1
    param([string] $OutputDirectory = "$env:TEMP\PowerBGInfo")
    
    $backdrop = @{
        Width = 1920
        Height = 1080
        Gradient = '#0B3B46', '#050A14'
        Glow = @(
            New-BGInfoBackgroundGlow -X 0.85 -Y 0.1 -Radius 0.6 -Color '#2DD4BF40'
            New-BGInfoBackgroundGlow -X 0.05 -Y 0.95 -Radius 0.5 -Color '#3B82F633'
        )
    }
    
    New-BGInfo -Target File {
        New-BGInfoBackground @backdrop
    } -ConfigurationDirectory $OutputDirectory -OutputFileName 'wallpaper.png'
    

    Pattern. A grid on top. Dots work the same way.

    PowerShell04-pattern.ps1+2 −0
    Pattern = 'Grid'
    PatternSpacing = 64
    
    04-pattern.png1920 x 1080
    Wallpaper for WS-FIN-0042 after the step: pattern.
    Whole script after this step 17 lines
    PowerShell04-pattern.ps1
    param([string] $OutputDirectory = "$env:TEMP\PowerBGInfo")
    
    $backdrop = @{
        Width = 1920
        Height = 1080
        Gradient = '#0B3B46', '#050A14'
        Glow = @(
            New-BGInfoBackgroundGlow -X 0.85 -Y 0.1 -Radius 0.6 -Color '#2DD4BF40'
            New-BGInfoBackgroundGlow -X 0.05 -Y 0.95 -Radius 0.5 -Color '#3B82F633'
        )
        Pattern = 'Grid'
        PatternSpacing = 64
    }
    
    New-BGInfo -Target File {
        New-BGInfoBackground @backdrop
    } -ConfigurationDirectory $OutputDirectory -OutputFileName 'wallpaper.png'
    
  3. Name the machine

    Labels and name and value pairs form the text block in the top-left corner. -BuiltinValue HostName, OSName or IPv4Address read the machine the script runs on; this page uses fixed values so anyone can reproduce the render.

    PowerShell05-details.ps1+8 −1
        New-BGInfoLabel -Name 'FINANCE · FLOOR 3' -Color '#2DD4BF' -FontWeight 700
        New-BGInfoLabel -Name 'WS-FIN-0042' -Color White -FontSize 64 -Bold
        New-BGInfoValue -Name 'User' -Value 'CONTOSO\anowak'
        New-BGInfoValue -Name 'Windows' -Value 'Windows 11 Enterprise'
        New-BGInfoValue -Name 'IP address' -Value '10.20.4.37'
        New-BGInfoValue -Name 'Support' -Value 'ext. 4357'
    } -Color '#94A3B8' -ValueColor White -FontSize 18 -SpaceX 64 -SpaceY 56 `
        -ConfigurationDirectory $OutputDirectory -OutputFileName 'wallpaper.png'
    
    05-details.png1920 x 1080
    Wallpaper for WS-FIN-0042 after the step: name the machine.
    Whole script after this step 25 lines
    PowerShell05-details.ps1
    param([string] $OutputDirectory = "$env:TEMP\PowerBGInfo")
    
    $backdrop = @{
        Width = 1920
        Height = 1080
        Gradient = '#0B3B46', '#050A14'
        Glow = @(
            New-BGInfoBackgroundGlow -X 0.85 -Y 0.1 -Radius 0.6 -Color '#2DD4BF40'
            New-BGInfoBackgroundGlow -X 0.05 -Y 0.95 -Radius 0.5 -Color '#3B82F633'
        )
        Pattern = 'Grid'
        PatternSpacing = 64
    }
    
    New-BGInfo -Target File {
        New-BGInfoBackground @backdrop
    
        New-BGInfoLabel -Name 'FINANCE · FLOOR 3' -Color '#2DD4BF' -FontWeight 700
        New-BGInfoLabel -Name 'WS-FIN-0042' -Color White -FontSize 64 -Bold
        New-BGInfoValue -Name 'User' -Value 'CONTOSO\anowak'
        New-BGInfoValue -Name 'Windows' -Value 'Windows 11 Enterprise'
        New-BGInfoValue -Name 'IP address' -Value '10.20.4.37'
        New-BGInfoValue -Name 'Support' -Value 'ext. 4357'
    } -Color '#94A3B8' -ValueColor White -FontSize 18 -SpaceX 64 -SpaceY 56 `
        -ConfigurationDirectory $OutputDirectory -OutputFileName 'wallpaper.png'
    
  4. Pick a theme preset

    -Theme Aurora gives the text, the panels and the backdrop one look, so the hand-picked colours and the background layers go. Parameters you set still win, and -ThemeMode Light gives the light pair.

    PowerShell06-theme.ps1+4 −12
    New-BGInfo -Theme Aurora -ThemeMode Dark -Target File {
    
        New-BGInfoLabel -Name 'FINANCE · FLOOR 3' -Accent -FontWeight 700
        New-BGInfoLabel -Name 'WS-FIN-0042' -FontSize 64 -Bold
    
    } -ConfigurationDirectory $OutputDirectory -OutputFileName 'wallpaper.png'
    
    06-theme.png1920 x 1080
    Wallpaper for WS-FIN-0042 after the step: pick a theme preset.
    Whole script after this step 17 lines
    PowerShell06-theme.ps1
    param([string] $OutputDirectory = "$env:TEMP\PowerBGInfo")
    
    $backdrop = @{
        Width = 1920
        Height = 1080
    }
    
    New-BGInfo -Theme Aurora -ThemeMode Dark -Target File {
        New-BGInfoBackground @backdrop
    
        New-BGInfoLabel -Name 'FINANCE · FLOOR 3' -Accent -FontWeight 700
        New-BGInfoLabel -Name 'WS-FIN-0042' -FontSize 64 -Bold
        New-BGInfoValue -Name 'User' -Value 'CONTOSO\anowak'
        New-BGInfoValue -Name 'Windows' -Value 'Windows 11 Enterprise'
        New-BGInfoValue -Name 'IP address' -Value '10.20.4.37'
        New-BGInfoValue -Name 'Support' -Value 'ext. 4357'
    } -ConfigurationDirectory $OutputDirectory -OutputFileName 'wallpaper.png'
    
  5. Add the first chart

    A chart panel placed by anchor and offsets, the way PowerBGInfo has always placed them. It picks up the theme's panel, type and series colour without a single colour parameter.

    PowerShell07-chart.ps1+3 −0
    New-BGInfoChart -Title 'CPU' -Kind Area -Values 12, 18, 25, 22, 30, 27 `
        -ValueSuffix ' %' -NoHistory `
        -Anchor TopRight -OffsetX 64 -OffsetY 56 -Width 460 -Height 240
    
    07-chart.png1920 x 1080
    Wallpaper for WS-FIN-0042 after the step: add the first chart.
    Whole script after this step 21 lines
    PowerShell07-chart.ps1
    param([string] $OutputDirectory = "$env:TEMP\PowerBGInfo")
    
    $backdrop = @{
        Width = 1920
        Height = 1080
    }
    
    New-BGInfo -Theme Aurora -ThemeMode Dark -Target File {
        New-BGInfoBackground @backdrop
    
        New-BGInfoLabel -Name 'FINANCE · FLOOR 3' -Accent -FontWeight 700
        New-BGInfoLabel -Name 'WS-FIN-0042' -FontSize 64 -Bold
        New-BGInfoValue -Name 'User' -Value 'CONTOSO\anowak'
        New-BGInfoValue -Name 'Windows' -Value 'Windows 11 Enterprise'
        New-BGInfoValue -Name 'IP address' -Value '10.20.4.37'
        New-BGInfoValue -Name 'Support' -Value 'ext. 4357'
    
        New-BGInfoChart -Title 'CPU' -Kind Area -Values 12, 18, 25, 22, 30, 27 `
            -ValueSuffix ' %' -NoHistory `
            -Anchor TopRight -OffsetX 64 -OffsetY 56 -Width 460 -Height 240
    } -ConfigurationDirectory $OutputDirectory -OutputFileName 'wallpaper.png'
    
  6. Let regions do the layout

    -Region Right stacks panels in a column and -Region Bottom shares a band. Nobody computes widths, the taskbar stays clear, and the layout follows the screen size; the region renders further down show it at ultrawide and portrait sizes.

    PowerShell08-regions.ps1+13 −1
        -Region Right
    New-BGInfoMetric -Region Right -Label 'Memory' -Value 86 -Unit '%' `
        -Caption 'of 32 GB' -Height 150
    
    New-BGInfoChart -Region Bottom -Title 'Volumes used' -Kind ProgressBar `
        -Values 63, 93 -Labels 'C: System', 'D: Data' -Maximum 100 `
        -ValueSuffix ' %' -NoProgressHandles -ShowLatestValue:$false `
        -NoHistory
    New-BGInfoChart -Region Bottom -Title 'Network' -Kind Line `
        -Values 120, 180, 160, 220, 260, 240 -ValueSuffix ' Mbps' -NoHistory
    New-BGInfoList -Region Bottom -Title 'This week' -Item @(
        @{ Text = 'Month-end close'; Checked = $false; Value = 'Fri' }
        @{ Text = 'Backups verified'; Checked = $true; Value = '02:10' }
    )
    
    08-regions.png1920 x 1080
    Wallpaper for WS-FIN-0042 after the step: let regions do the layout.
    Whole script after this step 34 lines
    PowerShell08-regions.ps1
    param([string] $OutputDirectory = "$env:TEMP\PowerBGInfo")
    
    $backdrop = @{
        Width = 1920
        Height = 1080
    }
    
    New-BGInfo -Theme Aurora -ThemeMode Dark -Target File {
        New-BGInfoBackground @backdrop
    
        New-BGInfoLabel -Name 'FINANCE · FLOOR 3' -Accent -FontWeight 700
        New-BGInfoLabel -Name 'WS-FIN-0042' -FontSize 64 -Bold
        New-BGInfoValue -Name 'User' -Value 'CONTOSO\anowak'
        New-BGInfoValue -Name 'Windows' -Value 'Windows 11 Enterprise'
        New-BGInfoValue -Name 'IP address' -Value '10.20.4.37'
        New-BGInfoValue -Name 'Support' -Value 'ext. 4357'
    
        New-BGInfoChart -Title 'CPU' -Kind Area -Values 12, 18, 25, 22, 30, 27 `
            -ValueSuffix ' %' -NoHistory `
            -Region Right
        New-BGInfoMetric -Region Right -Label 'Memory' -Value 86 -Unit '%' `
            -Caption 'of 32 GB' -Height 150
    
        New-BGInfoChart -Region Bottom -Title 'Volumes used' -Kind ProgressBar `
            -Values 63, 93 -Labels 'C: System', 'D: Data' -Maximum 100 `
            -ValueSuffix ' %' -NoProgressHandles -ShowLatestValue:$false `
            -NoHistory
        New-BGInfoChart -Region Bottom -Title 'Network' -Kind Line `
            -Values 120, 180, 160, 220, 260, 240 -ValueSuffix ' Mbps' -NoHistory
        New-BGInfoList -Region Bottom -Title 'This week' -Item @(
            @{ Text = 'Month-end close'; Checked = $false; Value = 'Fri' }
            @{ Text = 'Backups verified'; Checked = $true; Value = '02:10' }
        )
    } -ConfigurationDirectory $OutputDirectory -OutputFileName 'wallpaper.png'
    
  7. Add thresholds

    Warning and critical thresholds turn the memory card amber and the D: bar red by themselves, using the theme's status colours.

    PowerShell09-thresholds.ps1+3 −2
    -Caption 'of 32 GB' -Height 150 `
    -WarningThreshold 80 -CriticalThreshold 90
    
    -NoHistory -WarningThreshold 80 -CriticalThreshold 90
    
    09-thresholds.png1920 x 1080
    Wallpaper for WS-FIN-0042 after the step: add thresholds.
    Whole script after this step 35 lines
    PowerShell09-thresholds.ps1
    param([string] $OutputDirectory = "$env:TEMP\PowerBGInfo")
    
    $backdrop = @{
        Width = 1920
        Height = 1080
    }
    
    New-BGInfo -Theme Aurora -ThemeMode Dark -Target File {
        New-BGInfoBackground @backdrop
    
        New-BGInfoLabel -Name 'FINANCE · FLOOR 3' -Accent -FontWeight 700
        New-BGInfoLabel -Name 'WS-FIN-0042' -FontSize 64 -Bold
        New-BGInfoValue -Name 'User' -Value 'CONTOSO\anowak'
        New-BGInfoValue -Name 'Windows' -Value 'Windows 11 Enterprise'
        New-BGInfoValue -Name 'IP address' -Value '10.20.4.37'
        New-BGInfoValue -Name 'Support' -Value 'ext. 4357'
    
        New-BGInfoChart -Title 'CPU' -Kind Area -Values 12, 18, 25, 22, 30, 27 `
            -ValueSuffix ' %' -NoHistory `
            -Region Right
        New-BGInfoMetric -Region Right -Label 'Memory' -Value 86 -Unit '%' `
            -Caption 'of 32 GB' -Height 150 `
            -WarningThreshold 80 -CriticalThreshold 90
    
        New-BGInfoChart -Region Bottom -Title 'Volumes used' -Kind ProgressBar `
            -Values 63, 93 -Labels 'C: System', 'D: Data' -Maximum 100 `
            -ValueSuffix ' %' -NoProgressHandles -ShowLatestValue:$false `
            -NoHistory -WarningThreshold 80 -CriticalThreshold 90
        New-BGInfoChart -Region Bottom -Title 'Network' -Kind Line `
            -Values 120, 180, 160, 220, 260, 240 -ValueSuffix ' Mbps' -NoHistory
        New-BGInfoList -Region Bottom -Title 'This week' -Item @(
            @{ Text = 'Month-end close'; Checked = $false; Value = 'Fri' }
            @{ Text = 'Backups verified'; Checked = $true; Value = '02:10' }
        )
    } -ConfigurationDirectory $OutputDirectory -OutputFileName 'wallpaper.png'
    
  8. Bring your own data

    The values move into one function, Invoke-BGInfoDataSource runs it with a time limit and a fallback, and the drawing code only reads $data. The wallpaper looks the same; now it is yours.

    PowerShell10-data.ps1+49 −14
    #region Data: your logic goes here
    function Get-MyWallpaperData {
        # Sample values. Each comment names a live source for the same value.
        [pscustomobject] @{
            Team = 'FINANCE · FLOOR 3'
            Name = 'WS-FIN-0042'                # $env:COMPUTERNAME
            User = 'CONTOSO\anowak'             # Win32_ComputerSystem.UserName
            Windows = 'Windows 11 Enterprise'   # Win32_OperatingSystem.Caption
            IPv4 = '10.20.4.37'                 # Get-NetIPAddress
            Cpu = 12, 18, 25, 22, 30, 27        # Get-Counter, or your monitoring
            Memory = 86                         # Win32_OperatingSystem, % in use
            # Get-Volume | Where-Object DriveLetter, % used per volume
            Volumes = [ordered] @{ 'C: System' = 63; 'D: Data' = 93 }
            Network = 120, 180, 160, 220, 260, 240
            Tasks = @(
                @{ Text = 'Month-end close'; Checked = $false; Value = 'Fri' }
                @{ Text = 'Backups verified'; Checked = $true; Value = '02:10' }
            )
        }
    }
    
    # What the wallpaper shows when the source fails or runs out of time.
    $fallback = [pscustomobject] @{
        Team = 'STATUS UNAVAILABLE'; Name = $env:COMPUTERNAME
        User = 'unknown'; Windows = 'unknown'; IPv4 = 'unknown'
        Cpu = @(0); Memory = 0; Volumes = @{ 'C:' = 0 }; Network = @(0)
        Tasks = @(@{ Text = 'Status unknown'; Checked = $false })
    }
    $source = @{
        Name = 'Wallpaper data'
        TimeoutSeconds = 10
        ScriptBlock = ${function:Get-MyWallpaperData}
        Default = $fallback
    }
    $data = Invoke-BGInfoDataSource @source
    #endregion
    
    #region Presentation: reads only $data
    
        New-BGInfoLabel -Name $data.Team -Accent -FontWeight 700
        New-BGInfoLabel -Name $data.Name -FontSize 64 -Bold
        New-BGInfoValue -Name 'User' -Value $data.User
        New-BGInfoValue -Name 'Windows' -Value $data.Windows
        New-BGInfoValue -Name 'IP address' -Value $data.IPv4
    
        New-BGInfoChart -Title 'CPU' -Kind Area -Values $data.Cpu `
    
        New-BGInfoMetric -Region Right -Label 'Memory' -Unit '%' `
            -Value $data.Memory -Caption 'of 32 GB' -Height 150 `
    
            -Values @($data.Volumes.Values) -Labels @($data.Volumes.Keys) `
            -Maximum 100 `
    
            -Values $data.Network -ValueSuffix ' Mbps' -NoHistory
        New-BGInfoList -Region Bottom -Title 'This week' -Item $data.Tasks
    
    #endregion
    
    10-data.png1920 x 1080
    Wallpaper for WS-FIN-0042 after the step: bring your own data.
    Whole script after this step 72 lines
    PowerShell10-data.ps1
    param([string] $OutputDirectory = "$env:TEMP\PowerBGInfo")
    
    #region Data: your logic goes here
    function Get-MyWallpaperData {
        # Sample values. Each comment names a live source for the same value.
        [pscustomobject] @{
            Team = 'FINANCE · FLOOR 3'
            Name = 'WS-FIN-0042'                # $env:COMPUTERNAME
            User = 'CONTOSO\anowak'             # Win32_ComputerSystem.UserName
            Windows = 'Windows 11 Enterprise'   # Win32_OperatingSystem.Caption
            IPv4 = '10.20.4.37'                 # Get-NetIPAddress
            Cpu = 12, 18, 25, 22, 30, 27        # Get-Counter, or your monitoring
            Memory = 86                         # Win32_OperatingSystem, % in use
            # Get-Volume | Where-Object DriveLetter, % used per volume
            Volumes = [ordered] @{ 'C: System' = 63; 'D: Data' = 93 }
            Network = 120, 180, 160, 220, 260, 240
            Tasks = @(
                @{ Text = 'Month-end close'; Checked = $false; Value = 'Fri' }
                @{ Text = 'Backups verified'; Checked = $true; Value = '02:10' }
            )
        }
    }
    
    # What the wallpaper shows when the source fails or runs out of time.
    $fallback = [pscustomobject] @{
        Team = 'STATUS UNAVAILABLE'; Name = $env:COMPUTERNAME
        User = 'unknown'; Windows = 'unknown'; IPv4 = 'unknown'
        Cpu = @(0); Memory = 0; Volumes = @{ 'C:' = 0 }; Network = @(0)
        Tasks = @(@{ Text = 'Status unknown'; Checked = $false })
    }
    $source = @{
        Name = 'Wallpaper data'
        TimeoutSeconds = 10
        ScriptBlock = ${function:Get-MyWallpaperData}
        Default = $fallback
    }
    $data = Invoke-BGInfoDataSource @source
    #endregion
    
    #region Presentation: reads only $data
    $backdrop = @{
        Width = 1920
        Height = 1080
    }
    
    New-BGInfo -Theme Aurora -ThemeMode Dark -Target File {
        New-BGInfoBackground @backdrop
    
        New-BGInfoLabel -Name $data.Team -Accent -FontWeight 700
        New-BGInfoLabel -Name $data.Name -FontSize 64 -Bold
        New-BGInfoValue -Name 'User' -Value $data.User
        New-BGInfoValue -Name 'Windows' -Value $data.Windows
        New-BGInfoValue -Name 'IP address' -Value $data.IPv4
        New-BGInfoValue -Name 'Support' -Value 'ext. 4357'
    
        New-BGInfoChart -Title 'CPU' -Kind Area -Values $data.Cpu `
            -ValueSuffix ' %' -NoHistory `
            -Region Right
        New-BGInfoMetric -Region Right -Label 'Memory' -Unit '%' `
            -Value $data.Memory -Caption 'of 32 GB' -Height 150 `
            -WarningThreshold 80 -CriticalThreshold 90
    
        New-BGInfoChart -Region Bottom -Title 'Volumes used' -Kind ProgressBar `
            -Values @($data.Volumes.Values) -Labels @($data.Volumes.Keys) `
            -Maximum 100 `
            -ValueSuffix ' %' -NoProgressHandles -ShowLatestValue:$false `
            -NoHistory -WarningThreshold 80 -CriticalThreshold 90
        New-BGInfoChart -Region Bottom -Title 'Network' -Kind Line `
            -Values $data.Network -ValueSuffix ' Mbps' -NoHistory
        New-BGInfoList -Region Bottom -Title 'This week' -Item $data.Tasks
    } -ConfigurationDirectory $OutputDirectory -OutputFileName 'wallpaper.png'
    #endregion
    

The steps are scripts in this site's repository, rendered with -Target File by PowerBGInfo built from source commit 2f6f22d.

Your logic, your data

Collecting the values is the part only you can write, so PowerBGInfo keeps it apart from the drawing. Every example has the same two regions: a data block that returns plain values, and a presentation block that only reads them.

The data block

One function returns one object. This is where your sources go: replace the sample values, keep the property names, and the layout keeps working. A comment beside each value names a live source for it.

Copy the template: Run.BGInfo.Template.ps1

PowerShell10-data.ps1
#region Data: your logic goes here
function Get-MyWallpaperData {
    # Sample values. Each comment names a live source for the same value.
    [pscustomobject] @{
        Team = 'FINANCE · FLOOR 3'
        Name = 'WS-FIN-0042'                # $env:COMPUTERNAME
        User = 'CONTOSO\anowak'             # Win32_ComputerSystem.UserName
        Windows = 'Windows 11 Enterprise'   # Win32_OperatingSystem.Caption
        IPv4 = '10.20.4.37'                 # Get-NetIPAddress
        Cpu = 12, 18, 25, 22, 30, 27        # Get-Counter, or your monitoring
        Memory = 86                         # Win32_OperatingSystem, % in use
        # Get-Volume | Where-Object DriveLetter, % used per volume
        Volumes = [ordered] @{ 'C: System' = 63; 'D: Data' = 93 }
        Network = 120, 180, 160, 220, 260, 240
        Tasks = @(
            @{ Text = 'Month-end close'; Checked = $false; Value = 'Fri' }
            @{ Text = 'Backups verified'; Checked = $true; Value = '02:10' }
        )
    }
}

# What the wallpaper shows when the source fails or runs out of time.
$fallback = [pscustomobject] @{
    Team = 'STATUS UNAVAILABLE'; Name = $env:COMPUTERNAME
    User = 'unknown'; Windows = 'unknown'; IPv4 = 'unknown'
    Cpu = @(0); Memory = 0; Volumes = @{ 'C:' = 0 }; Network = @(0)
    Tasks = @(@{ Text = 'Status unknown'; Checked = $false })
}
$source = @{
    Name = 'Wallpaper data'
    TimeoutSeconds = 10
    ScriptBlock = ${function:Get-MyWallpaperData}
    Default = $fallback
}
$data = Invoke-BGInfoDataSource @source
#endregion

Typical sources

Each snippet is a body for that function. Combine them as you need; the wallpaper only sees the object they return.

Operating system, memory, uptime and fixed disks from Get-CimInstance. Read-only, and no module to install.

PowerShellcim.ps1
function Get-MyWallpaperData {
    $os = Get-CimInstance -ClassName Win32_OperatingSystem
    $disks = Get-CimInstance -ClassName Win32_LogicalDisk `
        -Filter 'DriveType = 3'
    $uptime = (Get-Date) - $os.LastBootUpTime
    $free = $os.FreePhysicalMemory / $os.TotalVisibleMemorySize

    $volumes = [ordered] @{}
    foreach ($disk in $disks) {
        $used = 1 - $disk.FreeSpace / $disk.Size
        $volumes[$disk.DeviceID] = [int] (100 * $used)
    }
    [pscustomobject] @{
        Windows = $os.Caption
        Uptime = '{0} days {1} h' -f $uptime.Days, $uptime.Hours
        Memory = [int] (100 * (1 - $free))
        Volumes = $volumes
    }
}

A slow source never blocks logon

Invoke-BGInfoDataSource runs your function in its own runspace with a time limit. When the function throws, returns nothing or runs out of time, it writes a warning and returns the fallback you gave it, so the wallpaper always renders and sign-in never waits on a broken CMDB.

  • Keep -TimeoutSeconds small, 5 to 15 seconds. A timed-out runspace is stopped in the background and the call returns at the limit.
  • The function cannot see your script's variables. Pass what it needs with -ArgumentList and read it with param().
  • Give each slow source its own call, so one failure only replaces its own values.

When the source does not answer. The same script with a source that hangs. Invoke-BGInfoDataSource stops waiting after ten seconds, writes a warning and returns the fallback, and the wallpaper still renders.

PowerShell11-timeout.ps1+3 −17
# A source that never answers: a CMDB that is down, a share that hangs.
Start-Sleep -Seconds 60
[pscustomobject] @{ Team = 'too late' }
Console11-timeout.ps1
WARNING: Wallpaper data did not finish within 10 s; using the default value.
11-timeout.png1920 x 1080
The same wallpaper drawn from the fallback values: the heading reads STATUS UNAVAILABLE, the details read unknown and the panels are empty.

Swap a built-in helper

Get-BGInfoSystemInfo returns uptime, OS and build, IPv4, fixed disks, the last update, Defender and BitLocker state and the console user as one plain object, and every probe in it is read-only. A function of your own that returns the same property names replaces it; the system facts example does exactly that with sample values.

The same facts are built-in tokens for New-BGInfoValue -BuiltinValue:

  • Uptime
  • LastPatch
  • DefenderStatus
  • BitLockerStatus
  • LoggedOnUser
  • Domain
  • DiskSummary
PowerShellsystem-info.ps1
# The built-in helper: read-only, each probe limited to five seconds.
$facts = Get-BGInfoSystemInfo

# Your own function with the same property names drops in for it.
function Get-MySystemInfo {
    $os = Get-CimInstance -ClassName Win32_OperatingSystem
    $uptime = (Get-Date) - $os.LastBootUpTime
    $volumes = Get-Volume | Where-Object { $_.DriveLetter -and $_.Size }

    [pscustomobject] @{
        ComputerName = $env:COMPUTERNAME
        OSName = $os.Caption
        UptimeText = '{0} days {1} h' -f $uptime.Days, $uptime.Hours
        Disks = foreach ($volume in $volumes) {
            [pscustomobject] @{
                Drive = "$($volume.DriveLetter):"
                FreeGB = [Math]::Round($volume.SizeRemaining / 1GB, 1)
                UsedPercent = [Math]::Round(
                    100 * (1 - $volume.SizeRemaining / $volume.Size), 1)
            }
        }
    }
}
$facts = Get-MySystemInfo

# The presentation code reads the same names either way.
$facts.Disks | New-BGInfoTable -Region Right -Title 'Disks' `
    -Property Drive, FreeGB, UsedPercent
Feature.SystemInfo.ps11920 x 1080
System facts wallpaper for WS-ANALYST-11 rendered from sample values with the same property names as Get-BGInfoSystemInfo: signed-in user, Windows build, uptime, IP address and last update, a protection list and a disks table. System facts wallpaper for WS-ANALYST-11 rendered from sample values with the same property names as Get-BGInfoSystemInfo: signed-in user, Windows build, uptime, IP address and last update, a protection list and a disks table. Dark theme.

Themes, regions and widgets

Six theme presets give a wallpaper one look in one parameter, five regions lay it out on any screen, and eight widget cmdlets draw what charts alone cannot.

Six theme presets

Each preset has a light and a dark pair; the page theme picks which one you see. New-BGInfoTheme adjusts a preset's accent, scale or font, and parameters you set always win. Feature.Themes.ps1

PowerShell
New-BGInfo -Theme Midnight -ThemeMode Light { ... }
  • Aurora theme preset: the theme example wallpaper for APP-WEB-02 with a requests card and a CPU chart. Aurora theme preset: the theme example wallpaper for APP-WEB-02 with a requests card and a CPU chart. Dark theme.
    Aurora
  • Graphite theme preset: the theme example wallpaper for APP-WEB-02 with a requests card and a CPU chart. Graphite theme preset: the theme example wallpaper for APP-WEB-02 with a requests card and a CPU chart. Dark theme.
    Graphite
  • Midnight theme preset: the theme example wallpaper for APP-WEB-02 with a requests card and a CPU chart. Midnight theme preset: the theme example wallpaper for APP-WEB-02 with a requests card and a CPU chart. Dark theme.
    Midnight
  • Ember theme preset: the theme example wallpaper for APP-WEB-02 with a requests card and a CPU chart. Ember theme preset: the theme example wallpaper for APP-WEB-02 with a requests card and a CPU chart. Dark theme.
    Ember
  • Orchid theme preset: the theme example wallpaper for APP-WEB-02 with a requests card and a CPU chart. Orchid theme preset: the theme example wallpaper for APP-WEB-02 with a requests card and a CPU chart. Dark theme.
    Orchid
  • Corporate theme preset: the theme example wallpaper for APP-WEB-02 with a requests card and a CPU chart. Corporate theme preset: the theme example wallpaper for APP-WEB-02 with a requests card and a CPU chart. Dark theme.
    Corporate

Regions on every screen

One script rendered at three sizes. Left and Right are columns, Top and Bottom are bands and Center sits between them; a panel without a size shares what is left. New-BGInfoLayout keeps the taskbar and the desktop icons clear. Feature.Regions.ps1

PowerShell
New-BGInfoLayout -IconAreaWidth 96 -Gap 24
1920 x 1080
The regions example for FILE-SRV-07 at 1920 x 1080: a left column with a memory card and a CPU chart, a right column with a shares table and volume bars, and a bottom band with network, a CPU gauge and a list. The regions example for FILE-SRV-07 at 1920 x 1080: a left column with a memory card and a CPU chart, a right column with a shares table and volume bars, and a bottom band with network, a CPU gauge and a list. Dark theme.
3440 x 1440
The regions example for FILE-SRV-07 at 3440 x 1440: a left column with a memory card and a CPU chart, a right column with a shares table and volume bars, and a bottom band with network, a CPU gauge and a list. The regions example for FILE-SRV-07 at 3440 x 1440: a left column with a memory card and a CPU chart, a right column with a shares table and volume bars, and a bottom band with network, a CPU gauge and a list. Dark theme.
1080 x 1920
The regions example for FILE-SRV-07 at 1080 x 1920: a left column with a memory card and a CPU chart, a right column with a shares table and volume bars, and a bottom band with network, a CPU gauge and a list. The regions example for FILE-SRV-07 at 1080 x 1920: a left column with a memory card and a CPU chart, a right column with a shares table and volume bars, and a bottom band with network, a CPU gauge and a list. Dark theme.

Widgets

Each widget is one cmdlet that takes plain values, hashtables or the pipeline, and flows into a region like any chart. The crops come from the feature examples; open one for the whole wallpaper. Examples/Features

  • Metric cards

    One value with its trend, a short history and a status from thresholds.

    Metric cards from Feature.Metrics.ps1, cropped to the widget. Metric cards from Feature.Metrics.ps1, cropped to the widget. Dark theme.
    PowerShellFeature.Metrics.ps1
    New-BGInfoMetric -Region Right -Label CPU -Value 38
  • Tables

    Objects in, rows out, with calculated properties and a status column.

    Tables from Feature.Tables.ps1, cropped to the widget. Tables from Feature.Tables.ps1, cropped to the widget. Dark theme.
    PowerShellFeature.Tables.ps1
    $procs | New-BGInfoTable -Region Right
  • Lists

    Numbered steps, checklists and status lists.

    Lists from Feature.Lists.ps1, cropped to the widget. Lists from Feature.Lists.ps1, cropped to the widget. Dark theme.
    PowerShellFeature.Lists.ps1
    New-BGInfoList -Region Right -Item $checks
  • Calendar heatmap

    Days coloured by value: sign-ins, patches, backups or incidents.

    Calendar heatmap from Feature.CalendarHeatmap.ps1, cropped to the widget. Calendar heatmap from Feature.CalendarHeatmap.ps1, cropped to the widget. Dark theme.
    PowerShellFeature.CalendarHeatmap.ps1
    $signIns | New-BGInfoCalendarHeatmap -Region Right
  • Treemap

    Where the space went, by folder, share or virtual machine.

    Treemap from Feature.Treemap.ps1, cropped to the widget. Treemap from Feature.Treemap.ps1, cropped to the widget. Dark theme.
    PowerShellFeature.Treemap.ps1
    $usage | New-BGInfoTreemap -Region Right
  • QR code

    A link a phone can open, encoded by CodeGlyphX and drawn by ChartForgeX.

    QR code from Feature.QRCode.ps1, cropped to the widget. QR code from Feature.QRCode.ps1, cropped to the widget. Dark theme.
    PowerShellFeature.QRCode.ps1
    New-BGInfoQRCode -Region Right -Url $link
  • Timeline

    Maintenance windows, change freezes and the on-call rota in lanes, with a marker for now.

    Timeline from Feature.Timeline.ps1, cropped to the widget. Timeline from Feature.Timeline.ps1, cropped to the widget. Dark theme.
    PowerShellFeature.Timeline.ps1
    New-BGInfoTimeline -Region Bottom -Title 'On call' -Item $schedule
  • Mermaid

    An escalation path or a network flow from a few lines of Mermaid text.

    Mermaid from Feature.Mermaid.ps1, cropped to the widget. Mermaid from Feature.Mermaid.ps1, cropped to the widget. Dark theme.
    PowerShellFeature.Mermaid.ps1
    New-BGInfoMermaid -Region Bottom -Title 'Escalation path' -Definition $diagram

What ChartForgeX draws

PowerBGInfo collects the values, lays out the wallpaper and applies it through Windows. Every pixel of the image comes from ChartForgeX; there is no System.Drawing or browser underneath. A theme preset resolves into the same ChartForgeX light or dark mode for every part, so a wallpaper reads as one design instead of a stack of overlays.

Get started

Install the module from the PowerShell Gallery and render to a file first, so you can check the layout before anything changes on a desktop.

Install the module

Install-Module PowerBGInfo

PowerBGInfo runs on Windows only. It is built for .NET Framework 4.7.2 and .NET 8 on Windows, so it works in Windows PowerShell 5.1 and in PowerShell 7.4 or later. It needs no separate helper modules; run the same command again to update.

Render a preview to a file

New-BGInfo -Theme Aurora -ThemeMode Dark -Target File {
    New-BGInfoValue -BuiltinValue HostName -Name 'Computer'
    New-BGInfoValue -BuiltinValue FullUserName -Name 'User'
    New-BGInfoValue -BuiltinValue OSName -Name 'Windows'
    New-BGInfoValue -BuiltinValue IPv4Address -Name 'IP address'
    New-BGInfoValue -BuiltinValue Uptime -Name 'Up for'
    New-BGInfoChart -Region Right -Kind Gauge -Metric MemoryPercent `
        -Title 'Memory used' -Maximum 100 -ValueSuffix ' %'
} -ConfigurationDirectory C:\Temp\PowerBGInfo -OutputFileName preview.png

-Target File writes preview.png and leaves the desktop alone. The built-in values read the machine the script runs on, and -Metric MemoryPercent reads the memory in use at that moment, so every machine gets its own wallpaper from the same script.

Set it as the wallpaper

New-BGInfo -Theme Aurora {
    New-BGInfoValue -BuiltinValue HostName -Name 'Computer'
    New-BGInfoValue -BuiltinValue OSName -Name 'Windows'
} -ConfigurationDirectory C:\ProgramData\PowerBGInfo -WallpaperFit Fill

Without -Target, New-BGInfo sets the wallpaper for the account that runs it. -AllUsers stages it for every profile, and -Target LogonScreen or -Target Both sets the lock screen too; those need an elevated session. One run applies one image; run it again to refresh the values.

Run it in production

PowerBGInfo has no refresh schedule of its own: each run renders the image, applies it once and exits. To keep the values current, run it again at sign-in, at unlock or on a timer, from the tool that already manages the machines.

RouteWhen to use itGuide
Task SchedulerStandalone machines and labs, or any fleet where another tool runs the registration script once. The most direct way to refresh at sign-in, at unlock and on a timer, in the user's own session.Task Scheduler guide
Group PolicyActive Directory domain-joined PCs and session hosts. A Group Policy Preferences task runs it for each user, and the same GPO can carry the execution policy and, on Enterprise and Education, the lock screen policy.Group Policy guide
IntuneMicrosoft Entra joined or hybrid joined devices. A Win32 app installs the module and the script and registers the per-user task; platform scripts, Remediations and the Personalization CSP cover the cases around it.Intune guide
Configuration ManagerDevices managed by Configuration Manager, including co-managed devices where the apps workload stays with it. An application installs the module and registers the same task.Configuration Manager guide
Remote Desktop, AVD, Windows 365Session hosts and AVD multi-session run the task in every session, with a per-user output folder and a fresh render on reconnect; LoggedOnUser names the user of the session being rendered. AVD personal desktops and Cloud PCs work like a physical PC.Remote Desktop, AVD, Windows 365 guide