Scheduled Email Reports of At Risk Users - Snowboundport37/champlain GitHub Wiki

<title>Scheduled Email Reports of At-Risk Users</title> <style> body { font-family: 'Segoe UI', Arial, sans-serif; background: #f5f7fa; color: #1e293b; margin: 0; padding: 40px; line-height: 1.6; } h1, h2, h3 { color: #2563eb; } pre { background: #0f172a; color: #f1f5f9; padding: 15px; border-radius: 10px; overflow-x: auto; font-size: 14px; } code { font-family: Consolas, monospace; } .section { background: white; padding: 25px; border-radius: 10px; margin-bottom: 25px; box-shadow: 0 4px 12px rgba(0,0,0,0.05); } table { width: 100%; border-collapse: collapse; margin: 10px 0; } table th, table td { border: 1px solid #e5e7eb; padding: 8px 12px; text-align: left; } table th { background: #f1f5f9; } .footer { text-align: center; font-size: 13px; color: #64748b; margin-top: 40px; } </style>

📨 Scheduled Email Reports of At-Risk Users

Course: SEC-350
Author: Andrei Gorlitsky
Date: October 14, 2025

📘 Overview

This project automates the process of identifying at-risk users (for example, users with repeated failed logon attempts) and sends a formatted email report daily at a configured time. All automation is handled by PowerShell scripts and Windows Task Scheduler.

Script Description
Configuration.ps1 Manages and saves settings such as number of log days and execution time.
Email.ps1 Builds and sends professional HTML emails using Gmail SMTP and an App Password.
Scheduler.ps1 Creates a Windows Scheduled Task to run main.ps1 daily.
main.ps1 Connects everything together, triggers emails, and manages automation.

⚙️ Configuration.ps1


$ConfigPath = Join-Path $PSScriptRoot 'configuration.txt'

function Get-Configuration { if (-not (Test-Path $ConfigPath)) { Set-Content -Path $ConfigPath -Value @("30","1:12 PM") } $lines = Get-Content -Path $ConfigPath [pscustomobject]@{ Days = [int]$lines[0] ExecutionTime = $lines[1] } }

function Set-Configuration { param( [Parameter(Mandatory)][int]$Days, [Parameter(Mandatory)][string]$ExecutionTime ) Set-Content -Path $ConfigPath -Value @("$Days",$ExecutionTime) }

function Show-Configuration { $cfg = Get-Configuration $cfg | Format-Table -AutoSize }

function Change-Configuration { $cfg = Get-Configuration Write-Host "`nEnter new configuration values:" $newDays = Read-Host "Days to evaluate logs [$($cfg.Days)]" if (-not $newDays) { $newDays = $cfg.Days } $newTime = Read-Host "Daily run time like 1:12 PM [$($cfg.ExecutionTime)]" if (-not $newTime) { $newTime = $cfg.ExecutionTime } Set-Configuration -Days $newDays -ExecutionTime $newTime Write-Host "Configuration updated!" -ForegroundColor Green }

function configurationMenu { while ($true) { Write-Host "`n===== Configuration Menu =====" Write-Host "1. Show configuration" Write-Host "2. Change configuration" Write-Host "3. Exit" $choice = Read-Host "Select option (1–3)" switch ($choice) { '1' { Show-Configuration } '2' { Change-Configuration } '3' { break } default { Write-Host "Please choose 1, 2, or 3." -ForegroundColor Yellow } } } }

if ($MyInvocation.InvocationName -ne '.') { configurationMenu }

📧 Email.ps1


function SendAlertEmail {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string]$BodyText,
        [string]$Title = "At Risk Users Report",
        [string]$Subtitle = "Automated notification",
        [string]$AccentColor = "#2563eb"
    )
$From = "[email protected]"
$To   = "[email protected]"
$Subject = $Title

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$Password   = "nlkaqezoardgbqpr" | ConvertTo-SecureString -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential ($From, $Password)
$now = (Get-Date).ToString("yyyy-MM-dd HH:mm")

$html = @"

<!DOCTYPE html> <html lang='en'> <head><meta charset='UTF-8'><title>$Title</title></head> <body style='margin:0;padding:0;background:#f6f7fb;font-family:Segoe UI,Arial,sans-serif;color:#0f172a;'> <table width='100%' cellspacing='0' cellpadding='0' style='padding:24px 0;background:#f6f7fb;'> <tr><td align='center'> <table width='600' style='background:white;border-radius:12px;box-shadow:0 10px 25px rgba(0,0,0,.1);overflow:hidden;'> <tr><td style='background:$AccentColor;color:white;padding:20px 28px;'> <div style='font-size:20px;font-weight:700;'>$Title</div> <div style='opacity:.9;font-size:13px;'>$Subtitle</div> </td></tr> <tr><td style='padding:28px;font-size:14px;line-height:1.6;'>$BodyText <div style='margin-top:16px;background:#f8fafc;border:1px solid #e5e7eb;border-radius:8px;padding:12px;color:#334155;font-size:13px;'> <strong>Run time:</strong> $now<br/> <strong>Sender:</strong> $From </div> </td></tr> <tr><td style='padding:16px 28px;border-top:1px solid #e5e7eb;background:#f9fafb;font-size:12px;color:#64748b;'> This message was sent automatically by the Scheduled Email Reports script. </td></tr> </table> </td></tr> </table> </body> </html> "@

try {
    Send-MailMessage -From $From -To $To -Subject $Subject -Body $html `
        -BodyAsHtml -SmtpServer "smtp.gmail.com" -Port 587 -UseSsl -Credential $Credential
    Write-Host "✅ Email sent to $To" -ForegroundColor Green
} catch {
    Write-Host "❌ Failed to send email: $($_.Exception.Message)" -ForegroundColor Red
}

}

⏰ Scheduler.ps1


function ChooseTimeToRun([string]$TimeString) {
    $formats = @('h:mm tt','hh:mm tt')
    $culture = [System.Globalization.CultureInfo]::GetCultureInfo('en-US')
try {
    $parsed = [datetime]::ParseExact($TimeString,$formats,$culture,[System.Globalization.DateTimeStyles]::None)
} catch {
    throw "Invalid time format '$TimeString'. Use e.g. 1:12 PM"
}

$taskName = 'AtRiskEmailDaily'
if (Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue) {
    Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
}

$main = Join-Path $PSScriptRoot 'main.ps1'
$action = New-ScheduledTaskAction -Execute 'powershell.exe' `
    -Argument "-NoProfile -ExecutionPolicy Bypass -File `"$main`""
$trigger = New-ScheduledTaskTrigger -Daily -At $parsed.TimeOfDay
$principal = New-ScheduledTaskPrincipal -UserId $env:USERNAME -RunLevel Highest
$settings = New-ScheduledTaskSettingsSet -RunOnlyIfNetworkAvailable -WakeToRun

$task = New-ScheduledTask -Action $action -Trigger $trigger `
                          -Principal $principal -Settings $settings
Register-ScheduledTask $taskName -InputObject $task | Out-Null

Write-Host "✅ Created/Updated schedule '$taskName' for: $TimeString." -ForegroundColor Green

}

🧠 main.ps1


. "$PSScriptRoot\Configuration.ps1"
. "$PSScriptRoot\Scheduler.ps1"
. "$PSScriptRoot\Email.ps1"

$configuration = Get-Configuration

$Failed = @( [pscustomobject]@{ Name = "$env:COMPUTERNAME$env:USERNAME"; Count = (Get-Random -Min 0 -Max 5) }, [pscustomobject]@{ Name = "DESKTOP-TEST01\alex"; Count = (Get-Random -Min 0 -Max 5) } )

$plainTable = ($Failed | Format-Table Name,Count -AutoSize | Out-String).Trim()

SendAlertEmail -BodyText $plainTable -Title "At Risk Users Report" -Subtitle "Automated notification"

ChooseTimeToRun $configuration.ExecutionTime

Write-Host "`nTask Summary:" -ForegroundColor Cyan Get-ScheduledTask -TaskName 'AtRiskEmailDaily' | Select TaskName, State, LastRunTime, NextRunTime | Format-Table -AutoSize

© 2025 Andrei Gorlitsky | Scheduled Email Reports Project — SEC-350
⚠️ **GitHub.com Fallback** ⚠️