Private/Initialize-NotificationSupport.ps1

function Initialize-NotificationSupport {
    # Prepares toast notifications, and reports whether they are usable and why
    # not. Toasts are a convenience: every failure path here reports rather than
    # throwing, because a missing notification module must never be the reason a
    # maintenance run does not happen.
    #
    # The reason is returned, not just logged, so the end-of-run summary can
    # repeat it. A warning printed at the moment of discovery has scrolled well
    # out of sight by the time a long run finishes.
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '', Justification = 'Write-Host is the user interface of a console maintenance tool. Its output is progress a person watches, not data a caller consumes, and the summary uses colour to separate failures from noise.')]
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        # The caller's -AllowInstall list, consulted before BurntToast is pulled
        # from the PowerShell Gallery.
        [string[]] $Approved = @(),

        # Notifications are on by default rather than asked for. A missing
        # prerequisite is then a note for the summary, not a warning, and no
        # install is offered unless -AllowInstall names it.
        [switch] $Implied
    )

    # A toast is drawn by the shell in an interactive desktop session. From a
    # service or a SYSTEM-run scheduled task there is no session to draw into,
    # and the call either fails or posts a notification nobody can see.
    if (-not (Test-InteractiveSession)) {
        $reason = 'this is not an interactive session, so a toast has no desktop to appear on. Schedule the task to run as your own user while logged on.'
        if ($Implied) { Write-Verbose "Notifications are on by default, but $reason" }
        else          { Write-Warning "Notifications requested, but $reason" }
        return [pscustomobject]@{ Available = $false; Reason = $reason }
    }

    if (-not (Get-Module BurntToast -ListAvailable)) {
        if ($Implied -and -not ($Approved -contains 'All' -or $Approved -contains 'BurntToast')) {
            $reason = 'the BurntToast module is not installed. "Install-Module BurntToast -Scope CurrentUser" adds a toast when a run finishes.'
            return [pscustomobject]@{ Available = $false; Reason = $reason }
        }
        if (-not (Approve-Install -Component 'BurntToast' -Approved $Approved `
                -Description 'Notifications were requested with -Notify, but the BurntToast module is not installed. This would install it from the PowerShell Gallery for the current user only.')) {
            $reason = 'the BurntToast module is not installed, and installing it was not approved. Install it with "Install-Module BurntToast -Scope CurrentUser", or re-run with -AllowInstall BurntToast.'
            return [pscustomobject]@{ Available = $false; Reason = $reason }
        }
        try {
            Write-Host 'Installing BurntToast (CurrentUser) for notifications...'
            Install-Module -Name BurntToast -Scope CurrentUser -Force -AllowClobber -ErrorAction Stop
        } catch {
            $reason = "BurntToast could not be installed: $($_.Exception.Message)"
            Write-Warning "$reason. Continuing without notifications."
            return [pscustomobject]@{ Available = $false; Reason = $reason }
        }
    }

    try {
        Import-Module BurntToast -ErrorAction Stop
        return [pscustomobject]@{ Available = $true; Reason = 'BurntToast loaded.' }
    } catch {
        $reason = "BurntToast is installed but would not load: $($_.Exception.Message)"
        Write-Warning "$reason. Continuing without notifications."
        return [pscustomobject]@{ Available = $false; Reason = $reason }
    }
}