Public/Invoke-MorningBrief.ps1

function Invoke-MorningBrief {
    <#
    .SYNOPSIS
        Morning Command Center - one command to see everything that needs attention today.
 
    .DESCRIPTION
        Orchestrates all alert checks (accounts, infrastructure, security, expirations),
        prioritises findings, generates an HTML dashboard, and optionally e-mails it.
 
        Run this when you sit down in the morning and get a single prioritised view of
        locked accounts, failing disks, expiring certificates, and security events.
 
    .PARAMETER OutputPath
        Directory where the HTML report is saved. Defaults to .\Reports.
 
    .PARAMETER ComputerName
        Servers to include in infrastructure and certificate checks. If omitted,
        infrastructure checks are skipped and only the local machine is checked for
        certificates.
 
    .PARAMETER DaysPasswordExpiry
        Warn when passwords expire within this many days. Default 14.
 
    .PARAMETER DaysCertExpiry
        Warn when certificates expire within this many days. Default 30.
 
    .PARAMETER IncludeM365
        Also check Microsoft 365 / Entra ID for risky sign-ins and risky users.
 
    .PARAMETER AutoRefreshSeconds
        If set, the HTML report includes a meta-refresh tag so it reloads automatically.
        Useful when the report is displayed on a wall monitor.
 
    .PARAMETER SendEmail
        Send the HTML report by e-mail after generation.
 
    .PARAMETER SmtpServer
        SMTP relay server (required when -SendEmail is used).
 
    .PARAMETER EmailTo
        Recipient address(es) (required when -SendEmail is used).
 
    .PARAMETER EmailFrom
        Sender address (required when -SendEmail is used).
 
    .EXAMPLE
        Invoke-MorningBrief -ComputerName DC01, SQL02, WEB01
 
    .EXAMPLE
        Invoke-MorningBrief -ComputerName (Get-ADComputer -Filter * -SearchBase "OU=Servers,DC=corp,DC=com").Name -IncludeM365 -SendEmail -SmtpServer mail.corp.com -EmailTo admin@corp.com -EmailFrom brief@corp.com
 
    .OUTPUTS
        PSCustomObject with CriticalCount, HighCount, MediumCount, LowCount, TotalCount,
        ReportPath, and Alerts.
    #>

    [CmdletBinding()]
    param(
        [Parameter()]
        [string]$OutputPath = (Join-Path -Path $PWD -ChildPath 'Reports'),

        [Parameter()]
        [string[]]$ComputerName,

        [Parameter()]
        [ValidateRange(1, 365)]
        [int]$DaysPasswordExpiry = 14,

        [Parameter()]
        [ValidateRange(1, 365)]
        [int]$DaysCertExpiry = 30,

        [Parameter()]
        [switch]$IncludeM365,

        [Parameter()]
        [int]$AutoRefreshSeconds = 0,

        [Parameter()]
        [switch]$SendEmail,

        [Parameter()]
        [string]$SmtpServer,

        [Parameter()]
        [string[]]$EmailTo,

        [Parameter()]
        [string]$EmailFrom
    )

    # ── Validate e-mail parameters ───────────────────────────────────
    if ($SendEmail) {
        if (-not $SmtpServer) { throw 'Invoke-MorningBrief: -SmtpServer is required when -SendEmail is specified.' }
        if (-not $EmailTo)    { throw 'Invoke-MorningBrief: -EmailTo is required when -SendEmail is specified.' }
        if (-not $EmailFrom)  { throw 'Invoke-MorningBrief: -EmailFrom is required when -SendEmail is specified.' }
    }

    $allAlerts = [System.Collections.Generic.List[PSCustomObject]]::new()

    Write-Host ''
    Write-Host ' ========================================' -ForegroundColor Cyan
    Write-Host ' Morning Brief - Gathering alerts ...'   -ForegroundColor Cyan
    Write-Host ' ========================================' -ForegroundColor Cyan
    Write-Host ''

    # ── 1. Account Alerts ────────────────────────────────────────────
    Write-Host ' [1/4] Account alerts ...' -ForegroundColor DarkCyan
    try {
        $accountAlerts = Get-AccountAlerts -DaysPasswordExpiry $DaysPasswordExpiry
        if ($accountAlerts) { $allAlerts.AddRange($accountAlerts) }
        Write-Host " Found $(@($accountAlerts).Count) account alert(s)" -ForegroundColor Gray
    }
    catch {
        Write-Warning "Account alerts failed: $_"
    }

    # ── 2. Infrastructure Alerts ─────────────────────────────────────
    if ($ComputerName) {
        Write-Host ' [2/4] Infrastructure alerts ...' -ForegroundColor DarkCyan
        try {
            $infraAlerts = Get-InfrastructureAlerts -ComputerName $ComputerName
            if ($infraAlerts) { $allAlerts.AddRange($infraAlerts) }
            Write-Host " Found $(@($infraAlerts).Count) infrastructure alert(s)" -ForegroundColor Gray
        }
        catch {
            Write-Warning "Infrastructure alerts failed: $_"
        }
    }
    else {
        Write-Host ' [2/4] Infrastructure alerts ... SKIPPED (no -ComputerName specified)' -ForegroundColor DarkGray
    }

    # ── 3. Security Alerts ───────────────────────────────────────────
    Write-Host ' [3/4] Security alerts ...' -ForegroundColor DarkCyan
    try {
        $securityParams = @{}
        if ($IncludeM365) { $securityParams['IncludeM365'] = $true }
        $secAlerts = Get-SecurityAlerts @securityParams
        if ($secAlerts) { $allAlerts.AddRange($secAlerts) }
        Write-Host " Found $(@($secAlerts).Count) security alert(s)" -ForegroundColor Gray
    }
    catch {
        Write-Warning "Security alerts failed: $_"
    }

    # ── 4. Expiration Alerts ─────────────────────────────────────────
    Write-Host ' [4/4] Expiration alerts ...' -ForegroundColor DarkCyan
    try {
        $expirationParams = @{ DaysCertExpiry = $DaysCertExpiry; DaysPasswordExpiry = $DaysPasswordExpiry }
        if ($ComputerName) { $expirationParams['ComputerName'] = $ComputerName }
        $expAlerts = Get-ExpirationAlerts @expirationParams
        if ($expAlerts) { $allAlerts.AddRange($expAlerts) }
        Write-Host " Found $(@($expAlerts).Count) expiration alert(s)" -ForegroundColor Gray
    }
    catch {
        Write-Warning "Expiration alerts failed: $_"
    }

    # ── Sort by priority ─────────────────────────────────────────────
    $sortedAlerts = $allAlerts | Sort-Object SortOrder, Timestamp

    # ── Counts ───────────────────────────────────────────────────────
    $criticalCount = @($sortedAlerts | Where-Object Priority -eq 'Critical').Count
    $highCount     = @($sortedAlerts | Where-Object Priority -eq 'High').Count
    $mediumCount   = @($sortedAlerts | Where-Object Priority -eq 'Medium').Count
    $lowCount      = @($sortedAlerts | Where-Object Priority -eq 'Low').Count
    $totalCount    = $sortedAlerts.Count

    # ── Generate HTML dashboard ──────────────────────────────────────
    $reportFileName = "MorningBrief_$(Get-Date -Format 'yyyyMMdd_HHmmss').html"
    $reportFullPath = Join-Path -Path $OutputPath -ChildPath $reportFileName

    $dashboardPath = New-HtmlDashboard -Alerts $sortedAlerts `
        -OutputPath $reportFullPath `
        -AutoRefreshSeconds $AutoRefreshSeconds

    # ── Console summary ──────────────────────────────────────────────
    Write-Host ''
    Write-Host ' ── Morning Brief Summary ──' -ForegroundColor Cyan
    $summaryLine = " Morning Brief: $criticalCount Critical, $highCount High, $mediumCount Medium, $lowCount Low"
    if ($criticalCount -gt 0) {
        Write-Host $summaryLine -ForegroundColor Red
    }
    elseif ($highCount -gt 0) {
        Write-Host $summaryLine -ForegroundColor Yellow
    }
    else {
        Write-Host $summaryLine -ForegroundColor Green
    }
    Write-Host " Report saved: $dashboardPath" -ForegroundColor Gray
    Write-Host ''

    # ── Send e-mail ──────────────────────────────────────────────────
    if ($SendEmail) {
        Write-Host ' Sending e-mail ...' -ForegroundColor DarkCyan
        try {
            $htmlBody = [System.IO.File]::ReadAllText($dashboardPath, [System.Text.UTF8Encoding]::new($true))
            $subject  = "Morning Brief $(Get-Date -Format 'yyyy-MM-dd') - $criticalCount Critical, $highCount High"

            $mailParams = @{
                From       = $EmailFrom
                To         = $EmailTo
                Subject    = $subject
                Body       = $htmlBody
                BodyAsHtml = $true
                SmtpServer = $SmtpServer
            }
            Send-MailMessage @mailParams -ErrorAction Stop
            Write-Host ' E-mail sent successfully.' -ForegroundColor Green
        }
        catch {
            Write-Warning "Failed to send e-mail: $_"
        }
    }

    # ── Return summary object ────────────────────────────────────────
    [PSCustomObject]@{
        CriticalCount = $criticalCount
        HighCount     = $highCount
        MediumCount   = $mediumCount
        LowCount      = $lowCount
        TotalCount    = $totalCount
        ReportPath    = $dashboardPath
        Alerts        = $sortedAlerts
    }
}